1 /* URI rewriting module */
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
{
26 /* TODO: An event hook for follow-url might also feel at home here. --jonas */
28 enum uri_rewrite_option
{
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
,
43 static struct option_info uri_rewrite_options
[] = {
44 INIT_OPT_TREE("protocol", N_("URI rewriting"),
46 N_("Rules for rewriting URIs entered in the goto dialog.\n"
47 "It makes it possible to define a set of prefixes that will\n"
48 "be expanded if they match a string entered in the goto dialog.\n"
49 "The prefixes can be dumb, meaning that they work only like\n"
50 "URI abbreviations, or smart ones, making it possible to pass\n"
51 "arguments to them like search engine keywords.")),
53 INIT_OPT_BOOL("protocol.rewrite", N_("Enable dumb prefixes"),
55 N_("Enable dumb prefixes - simple URI abbreviations which can\n"
56 "be written to the Goto URL dialog instead of actual URIs - i.e.\n"
57 "if you write 'elinks' there, you are directed to\n"
58 "http://elinks.cz/.")),
60 INIT_OPT_BOOL("protocol.rewrite", N_("Enable smart prefixes"),
62 N_("Enable smart prefixes - URI templates triggered by writing\n"
63 "given abbreviation to the Goto URL dialog followed by a list\n"
64 "of arguments from which the actual URI is composed - i.e.\n"
65 "'gg:search keywords' or 'gn search keywords for news'.")),
67 INIT_OPT_TREE("protocol.rewrite", N_("Dumb Prefixes"),
68 "dumb", OPT_AUTOCREATE
| OPT_SORT
,
69 N_("Dumb prefixes, see enable-dumb description for details.")),
71 INIT_OPT_STRING("protocol.rewrite.dumb", NULL
,
73 /* xgettext:no-c-format */
74 N_("Replacement URI for this dumbprefix:\n"
75 "%c in the string means the current URL\n"
76 "%% in the string means '%'")),
78 INIT_OPT_TREE("protocol.rewrite", N_("Smart Prefixes"),
79 "smart", OPT_AUTOCREATE
| OPT_SORT
,
80 N_("Smart prefixes, see enable-smart description for details.")),
82 /* TODO: In some rare occations current link URI and referrer might
83 * also be useful and dare I mention some kind of proxy argument. --jonas */
84 INIT_OPT_STRING("protocol.rewrite.smart", NULL
,
86 /* xgettext:no-c-format */
87 N_("Replacement URI for this smartprefix:\n"
88 "%c in the string means the current URL\n"
89 "%s in the string means the whole argument to smartprefix\n"
90 "%0,%1,...,%9 means argument 0, 1, ..., 9\n"
91 "%% in the string means '%'")),
93 INIT_OPT_STRING("protocol.rewrite", N_("Default template"),
94 "default_template", 0, "",
95 /* xgettext:no-c-format */
96 N_("Default URI template used when the string entered in\n"
97 "the goto dialog does not appear to be a URI or a filename\n"
98 "(i.e. contains no '.', ':' or '/' characters), and does\n"
99 "not match any defined prefixes. Set the value to \"\" to\n"
100 "disable use of the default template rewrite rule.\n"
101 "%c in the template means the current URL,\n"
102 "%s in the template means the whole string from the goto dialog,\n"
103 "%0,%1,...,%9 mean the 1st,2nd,...,10th space-delimited part of %s,\n"
104 "%% in the template means '%'.")),
106 #define INIT_OPT_DUMB_PREFIX(prefix, uri) \
107 INIT_OPT_STRING("protocol.rewrite.dumb", NULL, prefix, 0, uri, NULL)
109 INIT_OPT_DUMB_PREFIX("elinks", ELINKS_WEBSITE_URL
),
110 INIT_OPT_DUMB_PREFIX("documentation", ELINKS_DOC_URL
),
111 INIT_OPT_DUMB_PREFIX("bz", ELINKS_BUGS_URL
),
112 INIT_OPT_DUMB_PREFIX("bug", ELINKS_BUGS_URL
),
114 INIT_OPT_DUMB_PREFIX("arc", "http://web.archive.org/web/*/%c"),
115 INIT_OPT_DUMB_PREFIX("cia", "http://cia.navi.cx/"),
116 INIT_OPT_DUMB_PREFIX("b", "http://babelfish.altavista.com/babelfish/tr"),
117 INIT_OPT_DUMB_PREFIX("d", "http://www.dict.org"),
118 INIT_OPT_DUMB_PREFIX("g", "http://www.google.com/"),
119 INIT_OPT_DUMB_PREFIX("gg", "http://www.google.com/"),
120 INIT_OPT_DUMB_PREFIX("go", "http://www.google.com/"),
121 INIT_OPT_DUMB_PREFIX("fm", "http://freshmeat.net/"),
122 INIT_OPT_DUMB_PREFIX("sf", "http://www.sourceforge.net/"),
123 INIT_OPT_DUMB_PREFIX("dbug", "http://bugs.debian.org/"),
124 INIT_OPT_DUMB_PREFIX("dpkg", "http://packages.debian.org/"),
125 /* Hm, is this Debian-centric? -- Miciah */
126 /* Well, does anyone but Debian use lua40 naming convention? --pasky */
127 INIT_OPT_DUMB_PREFIX("lua", "file:///usr/share/doc/lua40-doc/manual/idx.html"),
128 INIT_OPT_DUMB_PREFIX("pycur", "http://www.python.org/doc/current/"),
129 INIT_OPT_DUMB_PREFIX("pydev", "http://www.python.org/dev/doc/devel/"),
130 INIT_OPT_DUMB_PREFIX("pyhelp", "http://starship.python.net/crew/theller/pyhelp.cgi"),
131 INIT_OPT_DUMB_PREFIX("pyvault", "http://www.vex.net/parnassus/"),
132 INIT_OPT_DUMB_PREFIX("e2", "http://www.everything2.org/"),
133 INIT_OPT_DUMB_PREFIX("sd", "http://slashdot.org/"),
134 INIT_OPT_DUMB_PREFIX("vhtml", "http://validator.w3.org/check?uri=%c"),
135 INIT_OPT_DUMB_PREFIX("vcss", "http://jigsaw.w3.org/css-validator/validator?uri=%c"),
137 #define INIT_OPT_SMART_PREFIX(prefix, uri) \
138 INIT_OPT_STRING("protocol.rewrite.smart", NULL, prefix, 0, uri, NULL)
139 #define bugzilla_prefix(prefix) (ELINKS_BUGS_URL prefix)
141 INIT_OPT_SMART_PREFIX("bug", bugzilla_prefix("show_bug.cgi?id=%s")),
144 INIT_OPT_SMART_PREFIX("milestone-bugs", bugzilla_prefix("buglist.cgi?target_milestone=%s")),
145 INIT_OPT_SMART_PREFIX("search-bugs", bugzilla_prefix("buglist.cgi?short_desc_type=allwordssubstr&short_desc=%s")),
148 INIT_OPT_SMART_PREFIX("arc", "http://web.archive.org/web/*/%s"),
149 INIT_OPT_SMART_PREFIX("bb", "http://babelfish.altavista.com/babelfish/tr?urltext=%s"),
150 INIT_OPT_SMART_PREFIX("bb_fr_en", "http://babelfish.altavista.com/babelfish/tr?lp=fr_en&submit=1&urltext=%s"),
151 INIT_OPT_SMART_PREFIX("bb_en_fr", "http://babelfish.altavista.com/babelfish/tr?lp=en_fr&submit=1&urltext=%s"),
152 INIT_OPT_SMART_PREFIX("cambridge", "http://dictionary.cambridge.org/results.asp?searchword=%s"),
153 INIT_OPT_SMART_PREFIX("cliki", "http://www.cliki.net/admin/search?words=%s"),
154 INIT_OPT_SMART_PREFIX("d", "http://www.dict.org/bin/Dict?Query=%s&Form=Dict1&Strategy=*&Database=*&submit=Submit+query"),
155 INIT_OPT_SMART_PREFIX("dmoz", "http://search.dmoz.org/cgi-bin/search?search=%s"),
156 INIT_OPT_SMART_PREFIX("foldoc", "http://wombat.doc.ic.ac.uk/foldoc/foldoc.cgi?%s"),
157 INIT_OPT_SMART_PREFIX("g", "http://www.google.com/search?q=%s&btnG=Google+Search"),
158 INIT_OPT_SMART_PREFIX("gd", "http://www.google.com/search?q=%s&cat=gwd/Top"),
159 /* Whose idea was it to use 'gg' for websearches? -- Miciah */
160 /* INIT_OPT_SMART_PREFIX("gg", "http://groups.google.com/groups?q=%s"), */
161 INIT_OPT_SMART_PREFIX("gg", "http://www.google.com/search?q=%s&btnG=Google+Search"),
162 INIT_OPT_SMART_PREFIX("gi", "http://images.google.com/images?q=%s"),
163 INIT_OPT_SMART_PREFIX("gn", "http://news.google.com/news?q=%s"),
164 INIT_OPT_SMART_PREFIX("go", "http://www.google.com/search?q=%s&btnG=Google+Search"),
165 INIT_OPT_SMART_PREFIX("gr", "http://groups.google.com/groups?q=%s"),
166 INIT_OPT_SMART_PREFIX("google", "http://www.google.com/search?q=%s"),
167 INIT_OPT_SMART_PREFIX("gwho", "http://www.googlism.com/?ism=%s&name=1"),
168 INIT_OPT_SMART_PREFIX("gwhat", "http://www.googlism.com/?ism=%s&name=2"),
169 INIT_OPT_SMART_PREFIX("gwhere", "http://www.googlism.com/?ism=%s&name=3"),
170 INIT_OPT_SMART_PREFIX("gwhen", "http://www.googlism.com/?ism=%s&name=4"),
171 INIT_OPT_SMART_PREFIX("fm", "http://freshmeat.net/search/?q=%s"),
172 INIT_OPT_SMART_PREFIX("savannah", "http://savannah.nongnu.org/search/?words=%s&type_of_search=soft&exact=1"),
173 INIT_OPT_SMART_PREFIX("sf", "http://sourceforge.net/search/?q=%s"),
174 INIT_OPT_SMART_PREFIX("sfp", "http://sourceforge.net/projects/%s"),
175 INIT_OPT_SMART_PREFIX("sd", "http://slashdot.org/search.pl?query=%s"),
176 INIT_OPT_SMART_PREFIX("sdc", "http://slashdot.org/search.pl?query=%s&op=comments"),
177 INIT_OPT_SMART_PREFIX("sdu", "http://slashdot.org/search.pl?query=%s&op=users"),
178 INIT_OPT_SMART_PREFIX("sdp", "http://slashdot.org/search.pl?query=%s&op=polls"),
179 INIT_OPT_SMART_PREFIX("sdj", "http://slashdot.org/search.pl?query=%s&op=journals"),
180 INIT_OPT_SMART_PREFIX("dbug", "http://bugs.debian.org/%s"),
181 INIT_OPT_SMART_PREFIX("dpkg", "http://packages.debian.org/%s"),
182 INIT_OPT_SMART_PREFIX("emacs", "http://www.emacswiki.org/cgi-bin/wiki.pl?search=%s"),
183 INIT_OPT_SMART_PREFIX("lyrics", "http://music.lycos.com/lyrics/results.asp?QT=L&QW=%s"),
184 INIT_OPT_SMART_PREFIX("lxr", "http://lxr.linux.no/ident?i=%s"),
185 INIT_OPT_SMART_PREFIX("onelook", "http://onelook.com/?w=%s&ls=a"),
186 INIT_OPT_SMART_PREFIX("py", "http://starship.python.net/crew/theller/pyhelp.cgi?keyword=%s&version=current"),
187 INIT_OPT_SMART_PREFIX("pydev", "http://starship.python.net/crew/theller/pyhelp.cgi?keyword=%s&version=devel"),
188 INIT_OPT_SMART_PREFIX("pyvault", "http://py.vaults.ca/apyllo.py?find=%s"),
189 INIT_OPT_SMART_PREFIX("e2", "http://www.everything2.org/?node=%s"),
190 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"),
191 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"),
192 INIT_OPT_SMART_PREFIX("dict", "http://dictionary.reference.com/search?q=%s"),
193 INIT_OPT_SMART_PREFIX("thes", "http://thesaurus.reference.com/search?q=%s"),
194 INIT_OPT_SMART_PREFIX("a", "http://acronymfinder.com/af-query.asp?String=exact&Acronym=%s"),
195 INIT_OPT_SMART_PREFIX("imdb", "http://imdb.com/Find?%s"),
196 INIT_OPT_SMART_PREFIX("mw", "http://www.m-w.com/cgi-bin/dictionary?book=Dictionary&va=%s"),
197 INIT_OPT_SMART_PREFIX("mwt", "http://www.m-w.com/cgi-bin/thesaurus?book=Thesaurus&va=%s"),
198 INIT_OPT_SMART_PREFIX("whatis", "http://uptime.netcraft.com/up/graph/?host=%s"),
199 INIT_OPT_SMART_PREFIX("wiki", "http://en.wikipedia.org/w/wiki.phtml?search=%s"),
200 INIT_OPT_SMART_PREFIX("wn", "http://www.cogsci.princeton.edu/cgi-bin/webwn1.7.1?stage=1&word=%s"),
201 /* Search the Free Software Directory */
202 INIT_OPT_SMART_PREFIX("fsd", "http://directory.fsf.org/search/fsd-search.py?q=%s"),
204 INIT_OPT_SMART_PREFIX("rfc", "http://www.rfc-editor.org/rfc/rfc%s.txt"),
206 INIT_OPT_SMART_PREFIX("rfcs", "http://www.rfc-editor.org/cgi-bin/rfcsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
207 INIT_OPT_SMART_PREFIX("cr", "http://www.rfc-editor.org/cgi-bin/rfcsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
208 /* Internet Draft search */
209 INIT_OPT_SMART_PREFIX("rfcid", "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
210 INIT_OPT_SMART_PREFIX("id", "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
211 INIT_OPT_SMART_PREFIX("draft", "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
216 #define get_opt_rewrite(which) uri_rewrite_options[(which)].option
217 #define get_dumb_enable() get_opt_rewrite(URI_REWRITE_ENABLE_DUMB).value.number
218 #define get_smart_enable() get_opt_rewrite(URI_REWRITE_ENABLE_SMART).value.number
220 static inline struct option
*
221 get_prefix_tree(enum uri_rewrite_option tree
)
223 assert(tree
== URI_REWRITE_DUMB_TREE
224 || tree
== URI_REWRITE_SMART_TREE
);
225 return &get_opt_rewrite(tree
);
228 #define MAX_URI_ARGS 10
230 static unsigned char *
231 rewrite_uri(unsigned char *url
, struct uri
*current_uri
, unsigned char *arg
)
233 struct string n
= NULL_STRING
;
234 unsigned char *args
[MAX_URI_ARGS
];
235 int argslen
[MAX_URI_ARGS
];
239 if (!init_string(&n
)) return NULL
;
241 /* Extract space separated list of arguments */
244 if (args
[argc
][i
] == ' ') {
247 if (argc
== MAX_URI_ARGS
) break;
248 args
[argc
] = &args
[argc
- 1][i
];
250 for (; *args
[argc
] == ' '; args
[argc
]++);
251 } else if (!args
[argc
][i
]) {
262 for (p
= 0; url
[p
] && url
[p
] != '%'; p
++);
264 add_bytes_to_string(&n
, url
, p
);
267 if (*url
!= '%') continue;
272 if (!current_uri
) break;
273 add_uri_to_string(&n
, current_uri
, URI_ORIGINAL
);
276 if (arg
) encode_uri_string(&n
, arg
, -1, 1);
279 add_char_to_string(&n
, '%');
292 if (value
>= argc
) break;
293 encode_uri_string(&n
, args
[value
],
297 add_bytes_to_string(&n
, url
- 1, 2);
306 static unsigned char *
307 get_uri_rewrite_prefix(enum uri_rewrite_type type
, unsigned char *url
)
309 enum uri_rewrite_option tree
= type
== URI_REWRITE_DUMB
310 ? URI_REWRITE_DUMB_TREE
: URI_REWRITE_SMART_TREE
;
311 struct option
*prefix_tree
= get_prefix_tree(tree
);
312 struct option
*opt
= get_opt_rec_real(prefix_tree
, url
);
313 unsigned char *exp
= opt
? opt
->value
.string
: NULL
;
315 return (exp
&& *exp
) ? exp
: NULL
;
318 static enum evhook_status
319 goto_url_hook(va_list ap
, void *data
)
321 unsigned char **url
= va_arg(ap
, unsigned char **);
322 struct session
*ses
= va_arg(ap
, struct session
*);
323 unsigned char *uu
= NULL
;
324 unsigned char *arg
= "";
325 unsigned char *argstart
= *url
+ strcspn(*url
, " :");
327 if (get_smart_enable() && *argstart
) {
328 unsigned char bucket
= *argstart
;
331 uu
= get_uri_rewrite_prefix(URI_REWRITE_SMART
, *url
);
336 if (get_dumb_enable() && !uu
&& !*argstart
)
337 uu
= get_uri_rewrite_prefix(URI_REWRITE_DUMB
, *url
);
340 && !strchr(*url
, ':')
341 && !strchr(*url
, '.')
342 && !strchr(*url
, '/')) {
343 uu
= get_opt_str("protocol.rewrite.default_template", NULL
);
352 struct uri
*uri
= ses
&& have_location(ses
)
353 ? cur_loc(ses
)->vs
.uri
: NULL
;
355 uu
= rewrite_uri(uu
, uri
, arg
);
362 return EVENT_HOOK_STATUS_NEXT
;
365 struct event_hook_info uri_rewrite_hooks
[] = {
366 { "goto-url", -1, goto_url_hook
},
371 struct module uri_rewrite_module
= struct_module(
372 /* name: */ N_("URI rewrite"),
373 /* options: */ uri_rewrite_options
,
374 /* hooks: */ uri_rewrite_hooks
,
375 /* submodules: */ NULL
,