Revert "Show the URI in the title bar even if document title is set."
[elinks.git] / src / protocol / rewrite / rewrite.c
blobdd5c7abb0b495ab6f42d034cea89964580a3190c
1 /* URI rewriting module */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include "elinks.h"
9 #include "config/options.h"
10 #include "intl/gettext/libintl.h"
11 #include "main/event.h"
12 #include "main/module.h"
13 #include "protocol/rewrite/rewrite.h"
14 #include "protocol/uri.h"
15 #include "session/location.h"
16 #include "session/session.h"
17 #include "util/string.h"
20 enum uri_rewrite_type {
21         URI_REWRITE_DUMB,
22         URI_REWRITE_SMART,
26 /* TODO: An event hook for follow-url might also feel at home here. --jonas */
28 enum uri_rewrite_option {
29         URI_REWRITE_TREE,
31         URI_REWRITE_ENABLE_DUMB,
32         URI_REWRITE_ENABLE_SMART,
34         URI_REWRITE_DUMB_TREE,
35         URI_REWRITE_DUMB_TEMPLATE,
37         URI_REWRITE_SMART_TREE,
38         URI_REWRITE_SMART_TEMPLATE,
40         URI_REWRITE_OPTIONS,
43 static struct option_info uri_rewrite_options[] = {
44         INIT_OPT_TREE("protocol", N_("URI rewriting"),
45                 "rewrite", OPT_SORT,
46                 N_("Rules for rewriting URIs entered in the goto dialog. "
47                 "It makes it possible to define a set of prefixes that will "
48                 "be expanded if they match a string entered in the goto "
49                 "dialog. The prefixes can be dumb, meaning that they work "
50                 "only like URI abbreviations, or smart ones, making it "
51                 "possible to pass arguments to them like search engine "
52                 "keywords.")),
54         INIT_OPT_BOOL("protocol.rewrite", N_("Enable dumb prefixes"),
55                 "enable-dumb", 0, 1,
56                 N_("Enable dumb prefixes - simple URI abbreviations which "
57                 "can be written to the Goto URL dialog instead of actual URIs "
58                 "- i.e. if you write 'elinks' there, you are directed to "
59                 "http://elinks.cz/.")),
61         INIT_OPT_BOOL("protocol.rewrite", N_("Enable smart prefixes"),
62                 "enable-smart", 0, 1,
63                 N_("Enable smart prefixes - URI templates triggered by "
64                 "writing given abbreviation to the Goto URL dialog followed "
65                 "by a list of arguments from which the actual URI is composed "
66                 "- i.e. 'gg:search keywords' or 'gn search keywords for "
67                 "news'.")),
69         INIT_OPT_TREE("protocol.rewrite", N_("Dumb Prefixes"),
70                 "dumb", OPT_AUTOCREATE | OPT_SORT,
71                 N_("Dumb prefixes, see enable-dumb description for details.")),
73         INIT_OPT_STRING("protocol.rewrite.dumb", NULL,
74                 "_template_", 0, "",
75                 /* xgettext:no-c-format */
76                 N_("Replacement URI for this dumbprefix:\n"
77                 "%c in the string means the current URL\n"
78                 "%% in the string means '%'")),
80         INIT_OPT_TREE("protocol.rewrite", N_("Smart Prefixes"),
81                 "smart", OPT_AUTOCREATE | OPT_SORT,
82                 N_("Smart prefixes, see enable-smart description for "
83                 "details.")),
85         /* TODO: In some rare occations current link URI and referrer might
86          * also be useful and dare I mention some kind of proxy argument. --jonas */
87         INIT_OPT_STRING("protocol.rewrite.smart", NULL,
88                 "_template_", 0, "",
89                 /* xgettext:no-c-format */
90                 N_("Replacement URI for this smartprefix:\n"
91                 "%c in the string means the current URL\n"
92                 "%s in the string means the whole argument to smartprefix\n"
93                 "%0,%1,...,%9 means argument 0, 1, ..., 9\n"
94                 "%% in the string means '%'")),
96         INIT_OPT_STRING("protocol.rewrite", N_("Default template"),
97                 "default_template", 0, "",
98                 /* xgettext:no-c-format */
99                 N_("Default URI template used when the string entered in "
100                 "the goto dialog does not appear to be a URI or a filename "
101                 "(i.e. contains no '.', ':' or '/' characters), and does "
102                 "not match any defined prefixes. Set the value to \"\" to "
103                 "disable use of the default template rewrite rule.\n"
104                 "\n"
105                 "%c in the template means the current URL,\n"
106                 "%s in the template means the whole string from the goto\n"
107                 "   dialog,\n"
108                 "%0,%1,...,%9 mean the 1st,2nd,...,10th space-delimited part\n"
109                 "   of %s,\n"
110                 "%% in the template means '%'.")),
112 #define INIT_OPT_DUMB_PREFIX(prefix, uri) \
113         INIT_OPT_STRING("protocol.rewrite.dumb", NULL, prefix, 0, uri, NULL)
115         INIT_OPT_DUMB_PREFIX("elinks", ELINKS_WEBSITE_URL),
116         INIT_OPT_DUMB_PREFIX("documentation", ELINKS_DOC_URL),
117         INIT_OPT_DUMB_PREFIX("bz", ELINKS_BUGS_URL),
118         INIT_OPT_DUMB_PREFIX("bug", ELINKS_BUGS_URL),
120         INIT_OPT_DUMB_PREFIX("arc", "http://web.archive.org/web/*/%c"),
121         INIT_OPT_DUMB_PREFIX("cia", "http://cia.navi.cx/"),
122         INIT_OPT_DUMB_PREFIX("b", "http://babelfish.altavista.com/babelfish/tr"),
123         INIT_OPT_DUMB_PREFIX("d", "http://www.dict.org"),
124         INIT_OPT_DUMB_PREFIX("g", "http://www.google.com/"),
125         INIT_OPT_DUMB_PREFIX("gg", "http://www.google.com/"),
126         INIT_OPT_DUMB_PREFIX("go", "http://www.google.com/"),
127         INIT_OPT_DUMB_PREFIX("fm", "http://freshmeat.net/"),
128         INIT_OPT_DUMB_PREFIX("sf", "http://www.sourceforge.net/"),
129         INIT_OPT_DUMB_PREFIX("dbug", "http://bugs.debian.org/"),
130         INIT_OPT_DUMB_PREFIX("dpkg", "http://packages.debian.org/"),
131         /* Hm, is this Debian-centric? -- Miciah */
132         /* Well, does anyone but Debian use lua40 naming convention? --pasky */
133         INIT_OPT_DUMB_PREFIX("lua", "file:///usr/share/doc/lua40-doc/manual/idx.html"),
134         INIT_OPT_DUMB_PREFIX("pycur", "http://www.python.org/doc/current/"),
135         INIT_OPT_DUMB_PREFIX("pydev", "http://www.python.org/dev/doc/devel/"),
136         INIT_OPT_DUMB_PREFIX("pyhelp", "http://starship.python.net/crew/theller/pyhelp.cgi"),
137         INIT_OPT_DUMB_PREFIX("pyvault", "http://www.vex.net/parnassus/"),
138         INIT_OPT_DUMB_PREFIX("e2", "http://www.everything2.org/"),
139         INIT_OPT_DUMB_PREFIX("sd", "http://slashdot.org/"),
140         INIT_OPT_DUMB_PREFIX("vhtml", "http://validator.w3.org/check?uri=%c"),
141         INIT_OPT_DUMB_PREFIX("vcss", "http://jigsaw.w3.org/css-validator/validator?uri=%c"),
143 #define INIT_OPT_SMART_PREFIX(prefix, uri) \
144         INIT_OPT_STRING("protocol.rewrite.smart", NULL, prefix, 0, uri, NULL)
145 #define bugzilla_prefix(prefix) (ELINKS_BUGS_URL prefix)
147         INIT_OPT_SMART_PREFIX("bug", bugzilla_prefix("show_bug.cgi?id=%s")),
149 #ifdef CONFIG_DEBUG
150         INIT_OPT_SMART_PREFIX("milestone-bugs", bugzilla_prefix("buglist.cgi?target_milestone=%s")),
151         INIT_OPT_SMART_PREFIX("search-bugs", bugzilla_prefix("buglist.cgi?short_desc_type=allwordssubstr&short_desc=%s")),
152 #endif
154         INIT_OPT_SMART_PREFIX("arc", "http://web.archive.org/web/*/%s"),
155         INIT_OPT_SMART_PREFIX("bb", "http://babelfish.altavista.com/babelfish/tr?urltext=%s"),
156         INIT_OPT_SMART_PREFIX("bb_fr_en", "http://babelfish.altavista.com/babelfish/tr?lp=fr_en&submit=1&urltext=%s"),
157         INIT_OPT_SMART_PREFIX("bb_en_fr", "http://babelfish.altavista.com/babelfish/tr?lp=en_fr&submit=1&urltext=%s"),
158         INIT_OPT_SMART_PREFIX("cambridge", "http://dictionary.cambridge.org/results.asp?searchword=%s"),
159         INIT_OPT_SMART_PREFIX("cliki", "http://www.cliki.net/admin/search?words=%s"),
160         INIT_OPT_SMART_PREFIX("d", "http://www.dict.org/bin/Dict?Query=%s&Form=Dict1&Strategy=*&Database=*&submit=Submit+query"),
161         INIT_OPT_SMART_PREFIX("dmoz", "http://search.dmoz.org/cgi-bin/search?search=%s"),
162         INIT_OPT_SMART_PREFIX("foldoc", "http://wombat.doc.ic.ac.uk/foldoc/foldoc.cgi?%s"),
163         INIT_OPT_SMART_PREFIX("g", "http://www.google.com/search?q=%s&btnG=Google+Search"),
164         INIT_OPT_SMART_PREFIX("gd", "http://www.google.com/search?q=%s&cat=gwd/Top"),
165         /* Whose idea was it to use 'gg' for websearches? -- Miciah */
166         /* INIT_OPT_SMART_PREFIX("gg", "http://groups.google.com/groups?q=%s"), */
167         INIT_OPT_SMART_PREFIX("gg", "http://www.google.com/search?q=%s&btnG=Google+Search"),
168         INIT_OPT_SMART_PREFIX("gi", "http://images.google.com/images?q=%s"),
169         INIT_OPT_SMART_PREFIX("gn", "http://news.google.com/news?q=%s"),
170         INIT_OPT_SMART_PREFIX("go", "http://www.google.com/search?q=%s&btnG=Google+Search"),
171         INIT_OPT_SMART_PREFIX("gr", "http://groups.google.com/groups?q=%s"),
172         INIT_OPT_SMART_PREFIX("google", "http://www.google.com/search?q=%s"),
173         INIT_OPT_SMART_PREFIX("gwho", "http://www.googlism.com/?ism=%s&name=1"),
174         INIT_OPT_SMART_PREFIX("gwhat", "http://www.googlism.com/?ism=%s&name=2"),
175         INIT_OPT_SMART_PREFIX("gwhere", "http://www.googlism.com/?ism=%s&name=3"),
176         INIT_OPT_SMART_PREFIX("gwhen", "http://www.googlism.com/?ism=%s&name=4"),
177         INIT_OPT_SMART_PREFIX("fm", "http://freshmeat.net/search/?q=%s"),
178         INIT_OPT_SMART_PREFIX("savannah", "http://savannah.nongnu.org/search/?words=%s&type_of_search=soft&exact=1"),
179         INIT_OPT_SMART_PREFIX("sf", "http://sourceforge.net/search/?q=%s"),
180         INIT_OPT_SMART_PREFIX("sfp", "http://sourceforge.net/projects/%s"),
181         INIT_OPT_SMART_PREFIX("sd", "http://slashdot.org/search.pl?query=%s"),
182         INIT_OPT_SMART_PREFIX("sdc", "http://slashdot.org/search.pl?query=%s&op=comments"),
183         INIT_OPT_SMART_PREFIX("sdu", "http://slashdot.org/search.pl?query=%s&op=users"),
184         INIT_OPT_SMART_PREFIX("sdp", "http://slashdot.org/search.pl?query=%s&op=polls"),
185         INIT_OPT_SMART_PREFIX("sdj", "http://slashdot.org/search.pl?query=%s&op=journals"),
186         INIT_OPT_SMART_PREFIX("dbug", "http://bugs.debian.org/%s"),
187         INIT_OPT_SMART_PREFIX("dpkg", "http://packages.debian.org/%s"),
188         INIT_OPT_SMART_PREFIX("emacs", "http://www.emacswiki.org/cgi-bin/wiki.pl?search=%s"),
189         INIT_OPT_SMART_PREFIX("lyrics", "http://music.lycos.com/lyrics/results.asp?QT=L&QW=%s"),
190         INIT_OPT_SMART_PREFIX("lxr", "http://lxr.linux.no/ident?i=%s"),
191         INIT_OPT_SMART_PREFIX("onelook", "http://onelook.com/?w=%s&ls=a"),
192         INIT_OPT_SMART_PREFIX("py", "http://starship.python.net/crew/theller/pyhelp.cgi?keyword=%s&version=current"),
193         INIT_OPT_SMART_PREFIX("pydev", "http://starship.python.net/crew/theller/pyhelp.cgi?keyword=%s&version=devel"),
194         INIT_OPT_SMART_PREFIX("pyvault", "http://py.vaults.ca/apyllo.py?find=%s"),
195         INIT_OPT_SMART_PREFIX("e2", "http://www.everything2.org/?node=%s"),
196         INIT_OPT_SMART_PREFIX("encz", "http://www.slovnik.cz/bin/ecd?ecd_il=1&ecd_vcb=%s&ecd_trn=translate&ecd_trn_dir=0&ecd_lines=15&ecd_hptxt=0"),
197         INIT_OPT_SMART_PREFIX("czen", "http://www.slovnik.cz/bin/ecd?ecd_il=1&ecd_vcb=%s&ecd_trn=translate&ecd_trn_dir=1&ecd_lines=15&ecd_hptxt=0"),
198         INIT_OPT_SMART_PREFIX("dict", "http://dictionary.reference.com/search?q=%s"),
199         INIT_OPT_SMART_PREFIX("thes", "http://thesaurus.reference.com/search?q=%s"),
200         INIT_OPT_SMART_PREFIX("a", "http://acronymfinder.com/af-query.asp?String=exact&Acronym=%s"),
201         INIT_OPT_SMART_PREFIX("imdb", "http://imdb.com/Find?%s"),
202         INIT_OPT_SMART_PREFIX("mw", "http://www.m-w.com/cgi-bin/dictionary?book=Dictionary&va=%s"),
203         INIT_OPT_SMART_PREFIX("mwt", "http://www.m-w.com/cgi-bin/thesaurus?book=Thesaurus&va=%s"),
204         INIT_OPT_SMART_PREFIX("whatis", "http://uptime.netcraft.com/up/graph/?host=%s"),
205         INIT_OPT_SMART_PREFIX("wiki", "http://en.wikipedia.org/w/wiki.phtml?search=%s"),
206         INIT_OPT_SMART_PREFIX("wn", "http://www.cogsci.princeton.edu/cgi-bin/webwn1.7.1?stage=1&word=%s"),
207         /* Search the Free Software Directory */
208         INIT_OPT_SMART_PREFIX("fsd", "http://directory.fsf.org/search/fsd-search.py?q=%s"),
209         /* rfc by number */
210         INIT_OPT_SMART_PREFIX("rfc", "http://www.rfc-editor.org/rfc/rfc%s.txt"),
211         /* rfc search */
212         INIT_OPT_SMART_PREFIX("rfcs", "http://www.rfc-editor.org/cgi-bin/rfcsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
213         INIT_OPT_SMART_PREFIX("cr", "http://www.rfc-editor.org/cgi-bin/rfcsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
214         /* Internet Draft search */
215         INIT_OPT_SMART_PREFIX("rfcid", "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
216         INIT_OPT_SMART_PREFIX("id", "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
217         INIT_OPT_SMART_PREFIX("draft", "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
219         NULL_OPTION_INFO,
222 #define get_opt_rewrite(which)  uri_rewrite_options[(which)].option
223 #define get_dumb_enable()       get_opt_rewrite(URI_REWRITE_ENABLE_DUMB).value.number
224 #define get_smart_enable()      get_opt_rewrite(URI_REWRITE_ENABLE_SMART).value.number
226 static inline struct option *
227 get_prefix_tree(enum uri_rewrite_option tree)
229         assert(tree == URI_REWRITE_DUMB_TREE
230                || tree == URI_REWRITE_SMART_TREE);
231         return &get_opt_rewrite(tree);
234 #define MAX_URI_ARGS 10
236 static unsigned char *
237 rewrite_uri(unsigned char *url, struct uri *current_uri, unsigned char *arg)
239         struct string n = NULL_STRING;
240         unsigned char *args[MAX_URI_ARGS];
241         int argslen[MAX_URI_ARGS];
242         int argc = 0;
243         int i;
245         if (!init_string(&n)) return NULL;
247         /* Extract space separated list of arguments */
248         args[argc] = arg;
249         for (i = 0; ; i++) {
250                 if (args[argc][i] == ' ') {
251                         argslen[argc] = i;
252                         argc++;
253                         if (argc == MAX_URI_ARGS) break;
254                         args[argc] = &args[argc - 1][i];
255                         i = 0;
256                         for (; *args[argc] == ' '; args[argc]++);
257                 } else if (!args[argc][i]) {
258                         argslen[argc] = i;
259                         argc++;
260                         break;
261                 }
262         }
264         while (*url) {
265                 int p;
266                 int value;
268                 for (p = 0; url[p] && url[p] != '%'; p++);
270                 add_bytes_to_string(&n, url, p);
271                 url += p;
273                 if (*url != '%') continue;
275                 url++;
276                 switch (*url) {
277                         case 'c':
278                                 if (!current_uri) break;
279                                 add_uri_to_string(&n, current_uri, URI_ORIGINAL);
280                                 break;
281                         case 's':
282                                 if (arg) encode_uri_string(&n, arg, -1, 1);
283                                 break;
284                         case '%':
285                                 add_char_to_string(&n, '%');
286                                 break;
287                         case '0':
288                         case '1':
289                         case '2':
290                         case '3':
291                         case '4':
292                         case '5':
293                         case '6':
294                         case '7':
295                         case '8':
296                         case '9':
297                                 value = *url - '0';
298                                 if (value >= argc) break;
299                                 encode_uri_string(&n, args[value],
300                                                   argslen[value], 1);
301                                 break;
302                         default:
303                                 add_bytes_to_string(&n, url - 1, 2);
304                                 break;
305                 }
306                 if (*url) url++;
307         }
309         return n.source;
312 static unsigned char *
313 get_uri_rewrite_prefix(enum uri_rewrite_type type, unsigned char *url)
315         enum uri_rewrite_option tree = type == URI_REWRITE_DUMB
316                         ? URI_REWRITE_DUMB_TREE : URI_REWRITE_SMART_TREE;
317         struct option *prefix_tree = get_prefix_tree(tree);
318         struct option *opt = get_opt_rec_real(prefix_tree, url);
319         unsigned char *exp = opt ? opt->value.string : NULL;
321         return (exp && *exp) ? exp : NULL;
324 static enum evhook_status
325 goto_url_hook(va_list ap, void *data)
327         unsigned char **url = va_arg(ap, unsigned char **);
328         struct session *ses = va_arg(ap, struct session *);
329         unsigned char *uu = NULL;
330         unsigned char *arg = "";
331         unsigned char *argstart = *url + strcspn(*url, " :");
333         if (get_smart_enable() && *argstart) {
334                 unsigned char bucket = *argstart;
336                 *argstart = '\0';
337                 uu = get_uri_rewrite_prefix(URI_REWRITE_SMART, *url);
338                 *argstart = bucket;
339                 arg = argstart + 1;
340         }
342         if (get_dumb_enable() && !uu && !*argstart)
343                 uu = get_uri_rewrite_prefix(URI_REWRITE_DUMB, *url);
345         if (!uu
346             && !strchr(*url, ':')
347             && !strchr(*url, '.')
348             && !strchr(*url, '/')) {
349                 uu = get_opt_str("protocol.rewrite.default_template", NULL);
350                 if (uu && *uu) {
351                         arg = *url;
352                 } else {
353                         uu = NULL;
354                 }
355         }
357         if (uu) {
358                 struct uri *uri = ses && have_location(ses)
359                                 ? cur_loc(ses)->vs.uri : NULL;
361                 uu = rewrite_uri(uu, uri, arg);
362                 if (uu) {
363                         mem_free(*url);
364                         *url = uu;
365                 }
366         }
368         return EVENT_HOOK_STATUS_NEXT;
371 struct event_hook_info uri_rewrite_hooks[] = {
372         { "goto-url", -1, goto_url_hook },
374         NULL_EVENT_HOOK_INFO
377 struct module uri_rewrite_module = struct_module(
378         /* name: */             N_("URI rewrite"),
379         /* options: */          uri_rewrite_options,
380         /* hooks: */            uri_rewrite_hooks,
381         /* submodules: */       NULL,
382         /* data: */             NULL,
383         /* init: */             NULL,
384         /* done: */             NULL