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 union option_info uri_rewrite_options
[] = {
44 INIT_OPT_TREE("protocol", N_("URI rewriting"),
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 "
54 INIT_OPT_BOOL("protocol.rewrite", N_("Enable dumb prefixes"),
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"),
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 "
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
,
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 "
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
,
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"
105 "%c in the template means the current URL,\n"
106 "%s in the template means the whole string from the goto\n"
108 "%0,%1,...,%9 mean the 1st,2nd,...,10th space-delimited part\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")),
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")),
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"),
210 INIT_OPT_SMART_PREFIX("rfc", "http://www.rfc-editor.org/rfc/rfc%s.txt"),
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"),
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
];
245 if (!init_string(&n
)) return NULL
;
247 /* Extract space separated list of arguments */
250 if (args
[argc
][i
] == ' ') {
253 if (argc
== MAX_URI_ARGS
) break;
254 args
[argc
] = &args
[argc
- 1][i
];
256 for (; *args
[argc
] == ' '; args
[argc
]++);
257 } else if (!args
[argc
][i
]) {
268 for (p
= 0; url
[p
] && url
[p
] != '%'; p
++);
270 add_bytes_to_string(&n
, url
, p
);
273 if (*url
!= '%') continue;
278 if (!current_uri
) break;
279 add_uri_to_string(&n
, current_uri
, URI_ORIGINAL
);
282 if (arg
) encode_uri_string(&n
, arg
, -1, 1);
285 add_char_to_string(&n
, '%');
298 if (value
>= argc
) break;
299 encode_uri_string(&n
, args
[value
],
303 add_bytes_to_string(&n
, url
- 1, 2);
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
;
337 uu
= get_uri_rewrite_prefix(URI_REWRITE_SMART
, *url
);
342 if (get_dumb_enable() && !uu
&& !*argstart
)
343 uu
= get_uri_rewrite_prefix(URI_REWRITE_DUMB
, *url
);
346 && !strchr(*url
, ':')
347 && !strchr(*url
, '.')
348 && !strchr(*url
, '/')) {
349 uu
= get_opt_str("protocol.rewrite.default_template");
358 struct uri
*uri
= ses
&& have_location(ses
)
359 ? cur_loc(ses
)->vs
.uri
: NULL
;
361 uu
= rewrite_uri(uu
, uri
, arg
);
368 return EVENT_HOOK_STATUS_NEXT
;
371 struct event_hook_info uri_rewrite_hooks
[] = {
372 { "goto-url", -1, goto_url_hook
},
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
,