From af741e150987c1e5ce04a0c6c875b7e629b3b48a Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 2 Mar 2017 00:54:22 +0100 Subject: [PATCH] Added url file handlers. Allow to set protocol handlers via :handler-add that process url protocol like mailto: by using a specific tool. --- README.md | 4 +-- doc/vimb.1 | 22 ++++++++++++ src/ex.c | 25 +++++++++++-- src/handler.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/handler.h | 31 ++++++++++++++++ src/main.c | 32 +++++++++++------ src/main.h | 3 ++ 7 files changed, 200 insertions(+), 16 deletions(-) create mode 100644 src/handler.c create mode 100644 src/handler.h diff --git a/README.md b/README.md index f04c860..e949421 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ project directory. - [x] file paths for :source and :save - [x] search phrases - [x] settings - - [ ] url handler + - [x] url handler - [x] url history - [ ] HSTS - [ ] auto-response-header @@ -131,7 +131,7 @@ project directory. - [x] default zoom - [x] yanking - [x] keymapping - - [ ] URL handler + - [x] URL handler - [x] shortcuts - [ ] autocommands and augroups 3. documentation diff --git a/doc/vimb.1 b/doc/vimb.1 index e3b9dee..92f6479 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -396,6 +396,28 @@ Save the current opened URI with \fItags\fP to the bookmark file. .BI ":bmr [" URI ] Removes all bookmarks for given \fIURI\fP or, if not given, the current opened page. +.SS Handlers +Handlers allow specifying external scripts to handle alternative URI methods. +.TP +.BI ":handler-add " "handler" "=" "cmd" +Adds a handler to direct \fIhandler\fP links to the external \fIcmd\fP. +The \fIcmd\fP can contain one placeholder `%s` that will be filled by the +full URI given when the command is called. +.RS +.P +Examples: +.PD 0 +.IP ":handler-add mailto=urxvt -e mutt %s" +to start email client for mailto links. +.IP ":handler-add magnet=xdg-open %s" +to open magnet links with xdg-open. +.IP ":handler-add ftp=urxvt -e wget %s -P ~/ftp-downloads" +to handle ftp downloads via wget. +.PD +.RE +.TP +.BI ":handler-remove " "handler" +Remove the handler for the given URI \fIhandler\fP. .SS Shortcuts Shortcuts allow the opening of an URI built up from a named template with additional parameters. diff --git a/src/ex.c b/src/ex.c index 42438ce..eaa97cb 100644 --- a/src/ex.c +++ b/src/ex.c @@ -32,6 +32,7 @@ #include "completion.h" #include "config.h" #include "ex.h" +#include "handler.h" #include "history.h" #include "main.h" #include "map.h" @@ -1008,8 +1009,26 @@ static VbCmdResult ex_shellcmd(Client *c, const ExArg *arg) static VbCmdResult ex_handlers(Client *c, const ExArg *arg) { - /* TODO no implemented yet */ - return CMD_SUCCESS; + char *p; + gboolean res = FALSE; + + switch (arg->code) { + case EX_HANDADD: + if (arg->rhs->len && (p = strchr(arg->rhs->str, '='))) { + *p++ = '\0'; + res = handler_add(c, arg->rhs->str, p); + } + break; + + case EX_HANDREM: + res = handler_remove(c, arg->rhs->str); + break; + + default: + break; + } + + return res ? CMD_SUCCESS : CMD_ERROR; } static VbCmdResult ex_shortcut(Client *c, const ExArg *arg) @@ -1165,7 +1184,7 @@ static gboolean complete(Client *c, short direction) break; case EX_HANDREM: - /* TODO fill handler completion */ + found = handler_fill_completion(c, store, token); break; case EX_SAVE: /* Fallthrough */ diff --git a/src/handler.c b/src/handler.c new file mode 100644 index 0000000..6cde888 --- /dev/null +++ b/src/handler.c @@ -0,0 +1,99 @@ +/** + * vimb - a webkit based vim like browser. + * + * Copyright (C) 2012-2017 Daniel Carl + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include + +#include "main.h" +#include "handler.h" +#include "util.h" + +extern struct Vimb vb; + +static char *handler_lookup(Client *c, const char *uri); + +void handler_init(Client *c) +{ + c->handlers.table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); +} + +void handler_cleanup(Client *c) +{ + if (c->handlers.table) { + g_hash_table_destroy(c->handlers.table); + c->handlers.table = NULL; + } +} + +gboolean handler_add(Client *c, const char *key, const char *cmd) +{ + g_hash_table_insert(c->handlers.table, g_strdup(key), g_strdup(cmd)); + + return true; +} + +gboolean handler_remove(Client *c, const char *key) +{ + return g_hash_table_remove(c->handlers.table, key); +} + +gboolean handler_handle_uri(Client *c, const char *uri) +{ + char *handler, *cmd; + GError *error = NULL; + gboolean res; + + if (!(handler = handler_lookup(c, uri))) { + return FALSE; + } + + cmd = g_strdup_printf(handler, uri); + if (!g_spawn_command_line_async(cmd, &error)) { + g_warning("Can't run '%s': %s", cmd, error->message); + g_clear_error(&error); + res = FALSE; + } else { + res = TRUE; + } + + g_free(cmd); + return res; +} + +gboolean handler_fill_completion(Client *c, GtkListStore *store, const char *input) +{ + GList *src = g_hash_table_get_keys(c->handlers.table); + gboolean found = util_fill_completion(store, input, src); + g_list_free(src); + + return found; +} + +static char *handler_lookup(Client *c, const char *uri) +{ + char *p, *schema, *handler = NULL; + + if ((p = strchr(uri, ':'))) { + schema = g_strndup(uri, p - uri); + handler = g_hash_table_lookup(c->handlers.table, schema); + g_free(schema); + } + + return handler; +} + diff --git a/src/handler.h b/src/handler.h new file mode 100644 index 0000000..ce16bdf --- /dev/null +++ b/src/handler.h @@ -0,0 +1,31 @@ +/** + * vimb - a webkit based vim like browser. + * + * Copyright (C) 2012-2017 Daniel Carl + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#ifndef _HANDLERS_H +#define _HANDLERS_H + +void handler_init(Client *c); +void handler_cleanup(Client *c); +gboolean handler_add(Client *c, const char *key, const char *cmd); +gboolean handler_remove(Client *c, const char *key); +gboolean handler_handle_uri(Client *c, const char *uri); +gboolean handler_fill_completion(Client *c, GtkListStore *store, const char *input); + +#endif /* end of include guard: _HANDLERS_H */ + diff --git a/src/main.c b/src/main.c index 904bf19..53fbc15 100644 --- a/src/main.c +++ b/src/main.c @@ -32,6 +32,7 @@ #include "config.h" #include "ex.h" #include "ext-proxy.h" +#include "handler.h" #include "input.h" #include "js.h" #include "main.h" @@ -486,6 +487,7 @@ static void client_destroy(Client *c) map_cleanup(c); register_cleanup(c); setting_cleanup(c); + handler_cleanup(c); g_slice_free(Client, c); @@ -586,6 +588,9 @@ static Client *client_new(WebKitWebView *webview) /* initialize the settings */ setting_init(c); + /* initialize the url handlers */ + handler_init(c); + /* start client in normal mode */ vb_enter(c, 'n'); @@ -956,24 +961,29 @@ static WebKitWebView *on_webview_create(WebKitWebView *webview, static gboolean on_webview_decide_policy(WebKitWebView *webview, WebKitPolicyDecision *dec, WebKitPolicyDecisionType type, Client *c) { - guint status; + guint status, button, mod; WebKitNavigationAction *a; WebKitURIRequest *req; WebKitURIResponse *res; switch (type) { case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION: - a = webkit_navigation_policy_decision_get_navigation_action(WEBKIT_NAVIGATION_POLICY_DECISION(dec)); - req = webkit_navigation_action_get_request(a); + a = webkit_navigation_policy_decision_get_navigation_action(WEBKIT_NAVIGATION_POLICY_DECISION(dec)); + req = webkit_navigation_action_get_request(a); + button = webkit_navigation_action_get_mouse_button(a); + mod = webkit_navigation_action_get_modifiers(a); - if (webkit_navigation_action_get_navigation_type(a) == WEBKIT_NAVIGATION_TYPE_LINK_CLICKED) { - if (webkit_navigation_action_get_mouse_button(a) == 2 - || (webkit_navigation_action_get_mouse_button(a) == 1 - && webkit_navigation_action_get_modifiers(a) & GDK_CONTROL_MASK)) { - webkit_policy_decision_ignore(dec); - spawn_new_instance(webkit_uri_request_get_uri(req), TRUE); - return TRUE; - } + /* Try to handle with specific protocol handler. */ + if (handler_handle_uri(c, webkit_uri_request_get_uri(req))) { + webkit_policy_decision_ignore(dec); + return TRUE; + } + if (webkit_navigation_action_get_navigation_type(a) == WEBKIT_NAVIGATION_TYPE_LINK_CLICKED + && (button == 2 || (button == 1 && mod & GDK_CONTROL_MASK))) { + + webkit_policy_decision_ignore(dec); + spawn_new_instance(webkit_uri_request_get_uri(req), TRUE); + return TRUE; } return FALSE; diff --git a/src/main.h b/src/main.h index 212cd38..59e1fbb 100644 --- a/src/main.h +++ b/src/main.h @@ -244,6 +244,9 @@ struct Client { GHashTable *table; char *fallback; /* default shortcut to use if none given in request */ } shortcut; + struct { + GHashTable *table; /* holds the protocol handlers */ + } handlers; }; struct Vimb { -- 2.20.1