/* URI rewriting module */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "elinks.h" #include "config/options.h" #include "intl/gettext/libintl.h" #include "main/event.h" #include "main/module.h" #include "protocol/rewrite/rewrite.h" #include "protocol/uri.h" #include "session/location.h" #include "session/session.h" #include "util/string.h" enum uri_rewrite_type { URI_REWRITE_DUMB, URI_REWRITE_SMART, }; /* TODO: An event hook for follow-url might also feel at home here. --jonas */ enum uri_rewrite_option { URI_REWRITE_TREE, URI_REWRITE_ENABLE_DUMB, URI_REWRITE_ENABLE_SMART, URI_REWRITE_DUMB_TREE, URI_REWRITE_DUMB_TEMPLATE, URI_REWRITE_SMART_TREE, URI_REWRITE_SMART_TEMPLATE, URI_REWRITE_OPTIONS, }; static union option_info uri_rewrite_options[] = { INIT_OPT_TREE("protocol", N_("URI rewriting"), "rewrite", OPT_SORT, N_("Rules for rewriting URIs entered in the goto dialog. " "It makes it possible to define a set of prefixes that will " "be expanded if they match a string entered in the goto " "dialog. The prefixes can be dumb, meaning that they work " "only like URI abbreviations, or smart ones, making it " "possible to pass arguments to them like search engine " "keywords.")), INIT_OPT_BOOL("protocol.rewrite", N_("Enable dumb prefixes"), "enable-dumb", 0, 1, N_("Enable dumb prefixes - simple URI abbreviations which " "can be written to the Goto URL dialog instead of actual URIs " "- i.e. if you write 'elinks' there, you are directed to " "http://elinks.cz/.")), INIT_OPT_BOOL("protocol.rewrite", N_("Enable smart prefixes"), "enable-smart", 0, 1, N_("Enable smart prefixes - URI templates triggered by " "writing given abbreviation to the Goto URL dialog followed " "by a list of arguments from which the actual URI is composed " "- i.e. 'gg:search keywords' or 'gn search keywords for " "news'.")), INIT_OPT_TREE("protocol.rewrite", N_("Dumb Prefixes"), "dumb", OPT_AUTOCREATE | OPT_SORT, N_("Dumb prefixes, see enable-dumb description for details.")), INIT_OPT_STRING("protocol.rewrite.dumb", NULL, "_template_", 0, "", /* xgettext:no-c-format */ N_("Replacement URI for this dumbprefix:\n" "%c in the string means the current URL\n" "%% in the string means '%'")), INIT_OPT_TREE("protocol.rewrite", N_("Smart Prefixes"), "smart", OPT_AUTOCREATE | OPT_SORT, N_("Smart prefixes, see enable-smart description for " "details.")), /* TODO: In some rare occations current link URI and referrer might * also be useful and dare I mention some kind of proxy argument. --jonas */ INIT_OPT_STRING("protocol.rewrite.smart", NULL, "_template_", 0, "", /* xgettext:no-c-format */ N_("Replacement URI for this smartprefix:\n" "%c in the string means the current URL\n" "%s in the string means the whole argument to smartprefix\n" "%0,%1,...,%9 means argument 0, 1, ..., 9\n" "%% in the string means '%'")), INIT_OPT_STRING("protocol.rewrite", N_("Default template"), "default_template", 0, "", /* xgettext:no-c-format */ N_("Default URI template used when the string entered in " "the goto dialog does not appear to be a URI or a filename " "(i.e. contains no '.', ':' or '/' characters), and does " "not match any defined prefixes. Set the value to \"\" to " "disable use of the default template rewrite rule.\n" "\n" "%c in the template means the current URL,\n" "%s in the template means the whole string from the goto\n" " dialog,\n" "%0,%1,...,%9 mean the 1st,2nd,...,10th space-delimited part\n" " of %s,\n" "%% in the template means '%'.")), #define INIT_OPT_DUMB_PREFIX(prefix, uri) \ INIT_OPT_STRING("protocol.rewrite.dumb", NULL, prefix, 0, uri, NULL) INIT_OPT_DUMB_PREFIX("elinks", ELINKS_WEBSITE_URL), INIT_OPT_DUMB_PREFIX("documentation", ELINKS_DOC_URL), INIT_OPT_DUMB_PREFIX("bz", ELINKS_BUGS_URL), INIT_OPT_DUMB_PREFIX("bug", ELINKS_BUGS_URL), INIT_OPT_DUMB_PREFIX("arc", "http://web.archive.org/web/*/%c"), INIT_OPT_DUMB_PREFIX("cia", "http://cia.vc/"), INIT_OPT_DUMB_PREFIX("d", "http://www.dict.org"), INIT_OPT_DUMB_PREFIX("ddg", "http://duckduckgo.com/?t=elinks"), INIT_OPT_DUMB_PREFIX("g", "http://www.google.com/"), INIT_OPT_DUMB_PREFIX("gg", "http://www.google.com/"), INIT_OPT_DUMB_PREFIX("go", "http://www.google.com/"), INIT_OPT_DUMB_PREFIX("fm", "http://freshmeat.net/"), INIT_OPT_DUMB_PREFIX("sf", "http://www.sourceforge.net/"), INIT_OPT_DUMB_PREFIX("dbug", "http://bugs.debian.org/"), INIT_OPT_DUMB_PREFIX("dpkg", "http://packages.debian.org/"), /* Hm, is this Debian-centric? -- Miciah */ /* Well, does anyone but Debian use lua40 naming convention? --pasky */ /* lua5.1 may be a more standard format. --KON */ INIT_OPT_DUMB_PREFIX("lua", "file:///usr/share/doc/lua5.1-doc/doc/contents.html#index"), INIT_OPT_DUMB_PREFIX("pycur", "http://www.python.org/doc/current/"), INIT_OPT_DUMB_PREFIX("pydev", "http://www.python.org/dev/doc/devel/"), INIT_OPT_DUMB_PREFIX("e2", "http://www.everything2.org/"), INIT_OPT_DUMB_PREFIX("sd", "http://slashdot.org/"), INIT_OPT_DUMB_PREFIX("vhtml", "http://validator.w3.org/check?uri=%c"), INIT_OPT_DUMB_PREFIX("vcss", "http://jigsaw.w3.org/css-validator/validator?uri=%c"), #define INIT_OPT_SMART_PREFIX(prefix, uri) \ INIT_OPT_STRING("protocol.rewrite.smart", NULL, prefix, 0, uri, NULL) #define bugzilla_prefix(prefix) (ELINKS_BUGS_URL prefix) INIT_OPT_SMART_PREFIX("bug", bugzilla_prefix("show_bug.cgi?id=%s")), #ifdef CONFIG_DEBUG INIT_OPT_SMART_PREFIX("milestone-bugs", bugzilla_prefix("buglist.cgi?target_milestone=%s")), INIT_OPT_SMART_PREFIX("search-bugs", bugzilla_prefix("buglist.cgi?short_desc_type=allwordssubstr&short_desc=%s")), #endif INIT_OPT_SMART_PREFIX("arc", "http://web.archive.org/web/*/%s"), INIT_OPT_SMART_PREFIX("cambridge", "http://dictionary.cambridge.org/results.asp?searchword=%s"), INIT_OPT_SMART_PREFIX("cliki", "http://www.cliki.net/site/search?words=%s"), INIT_OPT_SMART_PREFIX("d", "http://www.dict.org/bin/Dict?Query=%s&Form=Dict1&Strategy=*&Database=*&submit=Submit+query"), INIT_OPT_SMART_PREFIX("ddg", "http://duckduckgo.com/?q=%s&t=elinks"), INIT_OPT_SMART_PREFIX("dmoz", "http://search.dmoz.org/cgi-bin/search?search=%s"), INIT_OPT_SMART_PREFIX("foldoc", "http://foldoc.org/?%s"), INIT_OPT_SMART_PREFIX("g", "http://www.google.com/search?q=%s&btnG=Google+Search"), /* Whose idea was it to use 'gg' for websearches? -- Miciah */ /* INIT_OPT_SMART_PREFIX("gg", "http://groups.google.com/groups?q=%s"), */ INIT_OPT_SMART_PREFIX("gg", "http://www.google.com/search?q=%s&btnG=Google+Search"), INIT_OPT_SMART_PREFIX("gi", "http://images.google.com/images?q=%s"), INIT_OPT_SMART_PREFIX("gn", "http://news.google.com/news?q=%s"), INIT_OPT_SMART_PREFIX("go", "http://www.google.com/search?q=%s&btnG=Google+Search"), INIT_OPT_SMART_PREFIX("gr", "http://groups.google.com/groups?q=%s"), INIT_OPT_SMART_PREFIX("google", "http://www.google.com/search?q=%s"), INIT_OPT_SMART_PREFIX("gwho", "http://www.googlism.com/search/?ism=%s&type=1"), INIT_OPT_SMART_PREFIX("gwhat", "http://www.googlism.com/search/?ism=%s&type=2"), INIT_OPT_SMART_PREFIX("gwhere", "http://www.googlism.com/search/?ism=%s&type=3"), INIT_OPT_SMART_PREFIX("gwhen", "http://www.googlism.com/search/?ism=%s&type=4"), INIT_OPT_SMART_PREFIX("fm", "http://freshmeat.net/search/?q=%s"), INIT_OPT_SMART_PREFIX("savannah", "http://savannah.nongnu.org/search/?words=%s&type_of_search=soft&exact=1"), INIT_OPT_SMART_PREFIX("sf", "http://sourceforge.net/search/?q=%s"), INIT_OPT_SMART_PREFIX("sfp", "http://sourceforge.net/projects/%s"), INIT_OPT_SMART_PREFIX("dbug", "http://bugs.debian.org/%s"), INIT_OPT_SMART_PREFIX("dpkg", "http://packages.debian.org/%s"), INIT_OPT_SMART_PREFIX("emacs", "http://www.emacswiki.org/cgi-bin/wiki.pl?search=%s"), INIT_OPT_SMART_PREFIX("lxr", "http://lxr.linux.no/ident?i=%s"), INIT_OPT_SMART_PREFIX("onelook", "http://onelook.com/?w=%s&ls=a"), INIT_OPT_SMART_PREFIX("e2", "http://www.everything2.org/?node=%s"), 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"), 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"), INIT_OPT_SMART_PREFIX("dict", "http://dictionary.reference.com/search?q=%s"), INIT_OPT_SMART_PREFIX("thes", "http://thesaurus.reference.com/search?q=%s"), INIT_OPT_SMART_PREFIX("a", "http://www.acronymfinder.com/af-query.asp?String=exact&Acronym=%s"), INIT_OPT_SMART_PREFIX("imdb", "http://www.imdb.com/find?q=%s"), INIT_OPT_SMART_PREFIX("mw", "http://www.m-w.com/cgi-bin/dictionary?book=Dictionary&va=%s"), INIT_OPT_SMART_PREFIX("mwt", "http://www.m-w.com/cgi-bin/thesaurus?book=Thesaurus&va=%s"), INIT_OPT_SMART_PREFIX("wiki", "http://en.wikipedia.org/w/wiki.phtml?search=%s"), INIT_OPT_SMART_PREFIX("wn", "http://wordnetweb.princeton.edu/perl/webwn?s=%s"), /* Search the Free Software Directory */ INIT_OPT_SMART_PREFIX("fsd", "http://directory.fsf.org/wiki?title=Special%3ASearch&search=%s"), /* rfc by number */ INIT_OPT_SMART_PREFIX("rfc", "http://www.rfc-editor.org/rfc/rfc%s.txt"), /* rfc search */ INIT_OPT_SMART_PREFIX("rfcs", "http://www.rfc-editor.org/cgi-bin/rfcsearch.pl?searchwords=%s&filefmt=txt&format=http&abstract=abson&keywords=keyon&num=25"), INIT_OPT_SMART_PREFIX("cr", "http://www.rfc-editor.org/cgi-bin/rfcsearch.pl?searchwords=%s&filefmt=txt&format=http&abstract=abson&keywords=keyon&num=25"), NULL_OPTION_INFO, }; #define get_opt_rewrite(which) uri_rewrite_options[(which)].option #define get_dumb_enable() get_opt_rewrite(URI_REWRITE_ENABLE_DUMB).value.number #define get_smart_enable() get_opt_rewrite(URI_REWRITE_ENABLE_SMART).value.number static inline struct option * get_prefix_tree(enum uri_rewrite_option tree) { assert(tree == URI_REWRITE_DUMB_TREE || tree == URI_REWRITE_SMART_TREE); return &get_opt_rewrite(tree); } #define MAX_URI_ARGS 10 static unsigned char * rewrite_uri(unsigned char *url, struct uri *current_uri, unsigned char *arg) { struct string n = NULL_STRING; unsigned char *args[MAX_URI_ARGS]; int argslen[MAX_URI_ARGS]; int argc = 0; int i; if (!init_string(&n)) return NULL; /* Extract space separated list of arguments */ args[argc] = arg; for (i = 0; ; i++) { if (args[argc][i] == ' ') { argslen[argc] = i; argc++; if (argc == MAX_URI_ARGS) break; args[argc] = &args[argc - 1][i]; i = 0; for (; *args[argc] == ' '; args[argc]++); } else if (!args[argc][i]) { argslen[argc] = i; argc++; break; } } while (*url) { int p; int value; for (p = 0; url[p] && url[p] != '%'; p++); add_bytes_to_string(&n, url, p); url += p; if (*url != '%') continue; url++; switch (*url) { case 'c': if (!current_uri) break; add_uri_to_string(&n, current_uri, URI_ORIGINAL); break; case 's': if (arg) encode_uri_string(&n, arg, -1, 1); break; case '%': add_char_to_string(&n, '%'); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': value = *url - '0'; if (value >= argc) break; encode_uri_string(&n, args[value], argslen[value], 1); break; default: add_bytes_to_string(&n, url - 1, 2); break; } if (*url) url++; } return n.source; } static unsigned char * get_uri_rewrite_prefix(enum uri_rewrite_type type, unsigned char *url) { enum uri_rewrite_option tree = type == URI_REWRITE_DUMB ? URI_REWRITE_DUMB_TREE : URI_REWRITE_SMART_TREE; struct option *prefix_tree = get_prefix_tree(tree); struct option *opt = get_opt_rec_real(prefix_tree, url); unsigned char *exp = opt ? opt->value.string : NULL; return (exp && *exp) ? exp : NULL; } static enum evhook_status goto_url_hook(va_list ap, void *data) { unsigned char **url = va_arg(ap, unsigned char **); struct session *ses = va_arg(ap, struct session *); unsigned char *uu = NULL; unsigned char *arg = ""; unsigned char *argstart = *url + strcspn(*url, " :"); if (get_smart_enable() && *argstart) { unsigned char bucket = *argstart; *argstart = '\0'; uu = get_uri_rewrite_prefix(URI_REWRITE_SMART, *url); *argstart = bucket; arg = argstart + 1; } if (get_dumb_enable() && !uu && !*argstart) uu = get_uri_rewrite_prefix(URI_REWRITE_DUMB, *url); if (!uu && !strchr(*url, ':') && !strchr(*url, '.') && !strchr(*url, '/')) { uu = get_opt_str("protocol.rewrite.default_template", NULL); if (uu && *uu) { arg = *url; } else { uu = NULL; } } if (uu) { struct uri *uri = ses && have_location(ses) ? cur_loc(ses)->vs.uri : NULL; uu = rewrite_uri(uu, uri, arg); if (uu) { mem_free(*url); *url = uu; } } return EVENT_HOOK_STATUS_NEXT; } struct event_hook_info uri_rewrite_hooks[] = { { "goto-url", -1, goto_url_hook }, NULL_EVENT_HOOK_INFO }; struct module uri_rewrite_module = struct_module( /* name: */ N_("URI rewrite"), /* options: */ uri_rewrite_options, /* hooks: */ uri_rewrite_hooks, /* submodules: */ NULL, /* data: */ NULL, /* init: */ NULL, /* done: */ NULL );