Added url file handlers.
authorDaniel Carl <danielcarl@gmx.de>
Wed, 1 Mar 2017 23:54:22 +0000 (00:54 +0100)
committerDaniel Carl <danielcarl@gmx.de>
Wed, 1 Mar 2017 23:59:46 +0000 (00:59 +0100)
Allow to set protocol handlers via :handler-add that process url
protocol like mailto: by using a specific tool.

README.md
doc/vimb.1
src/ex.c
src/handler.c [new file with mode: 0644]
src/handler.h [new file with mode: 0644]
src/main.c
src/main.h

index f04c860..e949421 100644 (file)
--- 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
index e3b9dee..92f6479 100644 (file)
@@ -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.
index 42438ce..eaa97cb 100644 (file)
--- 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 (file)
index 0000000..6cde888
--- /dev/null
@@ -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 <string.h>
+
+#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 (file)
index 0000000..ce16bdf
--- /dev/null
@@ -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 */
+
index 904bf19..53fbc15 100644 (file)
@@ -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;
 
index 212cd38..59e1fbb 100644 (file)
@@ -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 {