about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorMichael Vetter <jubalh@iodoru.org>2020-05-20 14:15:53 +0200
committerGitHub <noreply@github.com>2020-05-20 14:15:53 +0200
commit7862542c58cf8319b1609b01c24deedb797854fe (patch)
tree3f87ef6e933d4a6df49c0fd4e8a319618ef46444
parent7d7f0ef5a5a40257996df52aaff58599fddd11d5 (diff)
parent7b541d0a6d196499712f4e7c9854aa294ac16167 (diff)
downloadprofani-tty-7862542c58cf8319b1609b01c24deedb797854fe.tar.gz
Merge pull request #1341 from profanity-im/feature/urlopen
Add urlopen command
-rw-r--r--src/command/cmd_ac.c40
-rw-r--r--src/command/cmd_defs.c45
-rw-r--r--src/command/cmd_funcs.c36
-rw-r--r--src/command/cmd_funcs.h3
-rw-r--r--src/common.c13
-rw-r--r--src/common.h2
-rw-r--r--src/config/preferences.c4
-rw-r--r--src/config/preferences.h1
-rw-r--r--src/tools/autocomplete.c12
-rw-r--r--src/tools/autocomplete.h2
-rw-r--r--src/ui/chatwin.c4
-rw-r--r--src/ui/console.c12
-rw-r--r--src/ui/mucwin.c1
-rw-r--r--src/ui/privwin.c2
-rw-r--r--src/ui/ui.h2
-rw-r--r--src/ui/win_types.h1
-rw-r--r--src/ui/window_list.c43
-rw-r--r--src/ui/window_list.h3
-rw-r--r--src/xmpp/avatar.c10
19 files changed, 212 insertions, 24 deletions
diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c
index 85800f99..e0d5f577 100644
--- a/src/command/cmd_ac.c
+++ b/src/command/cmd_ac.c
@@ -54,6 +54,7 @@
 #include "xmpp/muc.h"
 #include "xmpp/xmpp.h"
 #include "xmpp/roster_list.h"
+#include "ui/buffer.h"
 
 #ifdef HAVE_LIBGPGME
 #include "pgp/gpg.h"
@@ -122,6 +123,8 @@ static char* _avatar_autocomplete(ProfWin *window, const char *const input, gboo
 static char* _correction_autocomplete(ProfWin *window, const char *const input, gboolean previous);
 static char* _correct_autocomplete(ProfWin *window, const char *const input, gboolean previous);
 static char* _software_autocomplete(ProfWin *window, const char *const input, gboolean previous);
+static char* _urlopen_autocomplete(ProfWin *window, const char *const input, gboolean previous);
+static char* _executable_autocomplete(ProfWin *window, const char *const input, gboolean previous);
 
 static char* _script_autocomplete_func(const char *const prefix, gboolean previous, void *context);
 
@@ -254,6 +257,7 @@ static Autocomplete logging_group_ac;
 static Autocomplete color_ac;
 static Autocomplete correction_ac;
 static Autocomplete avatar_ac;
+static Autocomplete executable_ac;
 
 void
 cmd_ac_init(void)
@@ -986,8 +990,11 @@ cmd_ac_init(void)
 
     avatar_ac = autocomplete_new();
     autocomplete_add(avatar_ac, "get");
-    autocomplete_add(avatar_ac, "cmd");
     autocomplete_add(avatar_ac, "open");
+
+    executable_ac = autocomplete_new();
+    autocomplete_add(executable_ac, "avatar");
+    autocomplete_add(executable_ac, "urlopen");
 }
 
 void
@@ -1304,6 +1311,7 @@ cmd_ac_reset(ProfWin *window)
     autocomplete_reset(color_ac);
     autocomplete_reset(correction_ac);
     autocomplete_reset(avatar_ac);
+    autocomplete_reset(executable_ac);
 
     autocomplete_reset(script_ac);
     if (script_show_ac) {
@@ -1462,6 +1470,7 @@ cmd_ac_uninit(void)
     autocomplete_free(color_ac);
     autocomplete_free(correction_ac);
     autocomplete_free(avatar_ac);
+    autocomplete_free(executable_ac);
 }
 
 static void
@@ -1721,6 +1730,8 @@ _cmd_ac_complete_params(ProfWin *window, const char *const input, gboolean previ
     g_hash_table_insert(ac_funcs, "/correction",    _correction_autocomplete);
     g_hash_table_insert(ac_funcs, "/correct",       _correct_autocomplete);
     g_hash_table_insert(ac_funcs, "/software",      _software_autocomplete);
+    g_hash_table_insert(ac_funcs, "/urlopen",       _urlopen_autocomplete);
+    g_hash_table_insert(ac_funcs, "/executable",    _executable_autocomplete);
 
     int len = strlen(input);
     char parsed[len+1];
@@ -3912,3 +3923,30 @@ _software_autocomplete(ProfWin *window, const char *const input, gboolean previo
 
 	return result;
 }
+
+static char*
+_urlopen_autocomplete(ProfWin *window, const char *const input, gboolean previous)
+{
+    char *result = NULL;
+
+	if (window->type == WIN_CHAT ||
+        window->type == WIN_MUC ||
+        window->type == WIN_PRIVATE) {
+        result = autocomplete_param_with_func(input, "/urlopen", wins_get_url, previous, window);
+    }
+
+    return result;
+}
+
+static char*
+_executable_autocomplete(ProfWin *window, const char *const input, gboolean previous)
+{
+    char *result = NULL;
+
+    result = autocomplete_param_with_ac(input, "/executable", executable_ac, TRUE, previous);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c
index db55eaf4..3cf019b5 100644
--- a/src/command/cmd_defs.c
+++ b/src/command/cmd_defs.c
@@ -2340,26 +2340,23 @@ static struct cmd_t command_defs[] =
     },
 
     { "/avatar",
-        parse_args, 2, 2, &cons_avatar_setting,
+        parse_args, 2, 2, NULL,
         CMD_NOSUBFUNCS
         CMD_MAINFUNC(cmd_avatar)
         CMD_TAGS(
             CMD_TAG_CHAT)
         CMD_SYN(
             "/avatar get <barejid>",
-            "/avatar open <barejid>",
-            "/avatar cmd <command>")
+            "/avatar open <barejid>")
         CMD_DESC(
             "Download avatar (XEP-0084) for a certain contact. "
             "If nothing happens after using this command the user either doesn't have an avatar set at all "
             "or doesn't use XEP-0084 to publish it.")
         CMD_ARGS(
             { "get <barejid>", "Download the avatar. barejid is the JID to download avatar from."},
-            { "cmd <command>", "Set a command to execute with 'avatar open'. Use your favourite image viewer here."},
             { "open <barejid>", "Download avatar and open it with command."})
         CMD_EXAMPLES(
             "/avatar get thor@valhalla.edda",
-            "/avatar cmd xdg-open",
             "/avatar open freyja@vanaheimr.edda")
     },
 
@@ -2449,7 +2446,43 @@ static struct cmd_t command_defs[] =
         CMD_EXAMPLES(
             "/software valhalla.edda",
             "/software xmpp.vanaheimr.edda")
-    }
+    },
+
+    { "/urlopen",
+        parse_args, 1, -1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_urlopen)
+        CMD_TAGS(
+            CMD_TAG_CHAT,
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/urlopen <url>")
+        CMD_DESC(
+            "Open the URL")
+        CMD_ARGS(
+            { "<url>",    "URL to open."})
+        CMD_NOEXAMPLES
+    },
+
+    { "/executable",
+        parse_args, 2, 2, &cons_executable_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_executable)
+        CMD_TAGS(
+            CMD_TAG_DISCOVERY)
+        CMD_SYN(
+            "/executable avatar <cmd>",
+            "/executable urlopen <cmd>")
+        CMD_DESC(
+            "Configure executable that should be called upon a certain command."
+            "Default is xdg-open.")
+        CMD_ARGS(
+            { "avatar", "Set executable that is run in /avatar open. Use your favourite image viewer." },
+            { "urlopen", "Set executable that is run in /urlopen. Use your favourite browser." })
+        CMD_EXAMPLES(
+            "/executable avatar xdg-open",
+            "/executable urlopen firefox")
+    },
 };
 
 static GHashTable *search_index;
diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c
index 96f6c372..580494e2 100644
--- a/src/command/cmd_funcs.c
+++ b/src/command/cmd_funcs.c
@@ -8858,3 +8858,39 @@ cmd_slashguard(ProfWin *window, const char *const command, gchar **args)
 
     return TRUE;
 }
+
+gboolean
+cmd_urlopen(ProfWin *window, const char *const command, gchar **args)
+{
+	if (window->type == WIN_CHAT ||
+        window->type == WIN_MUC ||
+        window->type == WIN_PRIVATE) {
+
+        if (args[0] == NULL) {
+            cons_bad_cmd_usage(command);
+            return TRUE;
+        }
+
+        call_external(prefs_get_string(PREF_URL_OPEN_CMD), args[0]);
+    } else {
+        cons_show("urlopen not supported in this window");
+    }
+
+    return TRUE;
+}
+
+gboolean
+cmd_executable(ProfWin *window, const char *const command, gchar **args)
+{
+    if (g_strcmp0(args[0], "avatar") == 0) {
+        prefs_set_string(PREF_AVATAR_CMD, args[1]);
+        cons_show("Avatar command set to: %s", args[1]);
+    } else if (g_strcmp0(args[0], "urlopen") == 0) {
+        prefs_set_string(PREF_URL_OPEN_CMD, args[1]);
+        cons_show("urlopen command set to: %s", args[1]);
+    } else {
+        cons_bad_cmd_usage(command);
+    }
+
+    return TRUE;
+}
diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h
index 6f82a88a..b87cc22f 100644
--- a/src/command/cmd_funcs.h
+++ b/src/command/cmd_funcs.h
@@ -232,4 +232,7 @@ gboolean cmd_correction(ProfWin *window, const char *const command, gchar **args
 gboolean cmd_correct(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_slashguard(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_serversoftware(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_urlopen(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_executable(ProfWin *window, const char *const command, gchar **args);
+
 #endif
diff --git a/src/common.c b/src/common.c
index d06307cb..821acd3e 100644
--- a/src/common.c
+++ b/src/common.c
@@ -483,3 +483,16 @@ get_mentions(gboolean whole_word, gboolean case_sensitive, const char *const mes
 
     return mentions;
 }
+
+void
+call_external(const char *const exe, const char *const param)
+{
+    GString *cmd = g_string_new("");
+
+    g_string_append_printf(cmd, "%s %s > /dev/null 2>&1", exe, param);
+    log_debug("Calling external: %s", cmd->str);
+    FILE *stream = popen(cmd->str, "r");
+
+    pclose(stream);
+    g_string_free(cmd, TRUE);
+}
diff --git a/src/common.h b/src/common.h
index 4676a9ac..108536ed 100644
--- a/src/common.h
+++ b/src/common.h
@@ -106,4 +106,6 @@ void get_file_paths_recursive(const char *directory, GSList **contents);
 
 char* get_random_string(int length);
 
+void call_external(const char *const exe, const char *const param);
+
 #endif
diff --git a/src/config/preferences.c b/src/config/preferences.c
index 9a2105c0..3821f024 100644
--- a/src/config/preferences.c
+++ b/src/config/preferences.c
@@ -1783,6 +1783,7 @@ _get_group(preference_t pref)
         case PREF_LOG_ROTATE:
         case PREF_LOG_SHARED:
         case PREF_AVATAR_CMD:
+        case PREF_URL_OPEN_CMD:
             return PREF_GROUP_LOGGING;
         case PREF_AUTOAWAY_CHECK:
         case PREF_AUTOAWAY_MODE:
@@ -2070,6 +2071,8 @@ _get_key(preference_t pref)
             return "slashguard";
         case PREF_MAM:
             return "mam";
+        case PREF_URL_OPEN_CMD:
+            return "urlopen.cmd";
         default:
             return NULL;
     }
@@ -2205,6 +2208,7 @@ _get_default_string(preference_t pref)
         case PREF_COLOR_NICK:
             return "false";
         case PREF_AVATAR_CMD:
+        case PREF_URL_OPEN_CMD:
             return "xdg-open";
         default:
             return NULL;
diff --git a/src/config/preferences.h b/src/config/preferences.h
index 7d651ac3..fc6eeafc 100644
--- a/src/config/preferences.h
+++ b/src/config/preferences.h
@@ -170,6 +170,7 @@ typedef enum {
     PREF_AVATAR_CMD,
     PREF_SLASH_GUARD,
     PREF_MAM,
+    PREF_URL_OPEN_CMD,
 } preference_t;
 
 typedef struct prof_alias_t {
diff --git a/src/tools/autocomplete.c b/src/tools/autocomplete.c
index 5e9f14f1..6c960ba2 100644
--- a/src/tools/autocomplete.c
+++ b/src/tools/autocomplete.c
@@ -385,6 +385,18 @@ autocomplete_param_no_with_func(const char *const input, char *command, int arg_
     return NULL;
 }
 
+/* remove the first message if we have more than max */
+void
+autocomplete_remove_older_than_max(Autocomplete ac, int maxsize)
+{
+    if (autocomplete_length(ac) > maxsize) {
+        GList *first = g_list_nth(ac->items, 0);
+        if (first) {
+            ac->items = g_list_delete_link(ac->items, first);
+        }
+    }
+}
+
 static gchar*
 _search_next(Autocomplete ac, GList *curr, gboolean quote)
 {
diff --git a/src/tools/autocomplete.h b/src/tools/autocomplete.h
index 90b17bb3..10bbbf61 100644
--- a/src/tools/autocomplete.h
+++ b/src/tools/autocomplete.h
@@ -74,4 +74,6 @@ char* autocomplete_param_no_with_func(const char *const input, char *command,
 void autocomplete_reset(Autocomplete ac);
 
 gboolean autocomplete_contains(Autocomplete ac, const char *value);
+
+void autocomplete_remove_older_than_max(Autocomplete ac, int maxsize);
 #endif
diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c
index ffffb2e1..260b9f06 100644
--- a/src/ui/chatwin.c
+++ b/src/ui/chatwin.c
@@ -292,7 +292,7 @@ chatwin_incoming_msg(ProfChatWin *chatwin, ProfMessage *message, gboolean win_cr
         //1) only send IQ once
         //2) sort incoming messages on timestamp
         //for now if experimental MAM is enabled we dont show no history from sql either
-        
+
         // MUCPMs also get printed here. In their case we don't save any logs (because nick owners can change) and thus we shouldn't read logs
         // (and if we do we need to check the resourcepart)
         if (!prefs_get_boolean(PREF_MAM) && prefs_get_boolean(PREF_CHLOG) && prefs_get_boolean(PREF_HISTORY) && message->type == PROF_MSG_TYPE_CHAT) {
@@ -311,6 +311,8 @@ chatwin_incoming_msg(ProfChatWin *chatwin, ProfMessage *message, gboolean win_cr
         win_print_incoming(window, display_name, message);
     }
 
+    wins_add_urls_ac(window, message);
+
     if (prefs_get_boolean(PREF_BEEP)) {
         beep();
     }
diff --git a/src/ui/console.c b/src/ui/console.c
index 58b33204..d2158862 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -2051,13 +2051,15 @@ cons_correction_setting(void)
 }
 
 void
-cons_avatar_setting(void)
+cons_executable_setting(void)
 {
-    char *pref = prefs_get_string(PREF_AVATAR_CMD);
+    char *avatar = prefs_get_string(PREF_AVATAR_CMD);
+    cons_show("Avatar command (/executable avatar)                                   : %s", avatar);
+    prefs_free_string(avatar);
 
-    cons_show("Avatar command (/avatar cmd)                                       : %s", pref);
-
-    prefs_free_string(pref);
+    char *exec = prefs_get_string(PREF_URL_OPEN_CMD);
+    cons_show("urlopen command (/executable urlopen)                                 : %s", exec);
+    prefs_free_string(exec);
 }
 
 void
diff --git a/src/ui/mucwin.c b/src/ui/mucwin.c
index ae98eb43..cb0167d0 100644
--- a/src/ui/mucwin.c
+++ b/src/ui/mucwin.c
@@ -557,6 +557,7 @@ mucwin_incoming_msg(ProfMucWin *mucwin, const ProfMessage *const message, GSList
     }
 
     win_insert_last_read_position_marker((ProfWin*)mucwin, mucwin->roomjid);
+    wins_add_urls_ac(window, message);
 
     if (g_slist_length(mentions) > 0) {
         _mucwin_print_mention(window, message->plain, message->from_jid->resourcepart, mynick, mentions, ch, flags);
diff --git a/src/ui/privwin.c b/src/ui/privwin.c
index cbe32500..c18588fb 100644
--- a/src/ui/privwin.c
+++ b/src/ui/privwin.c
@@ -79,6 +79,8 @@ privwin_incoming_msg(ProfPrivateWin *privatewin, ProfMessage *message)
         }
     }
 
+    wins_add_urls_ac(window, message);
+
     if (prefs_get_boolean(PREF_BEEP)) {
         beep();
     }
diff --git a/src/ui/ui.h b/src/ui/ui.h
index a4878106..6e8083da 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -319,7 +319,7 @@ void cons_winpos_setting(void);
 void cons_color_setting(void);
 void cons_os_setting(void);
 void cons_correction_setting(void);
-void cons_avatar_setting(void);
+void cons_executable_setting(void);
 void cons_slashguard_setting(void);
 void cons_show_contact_online(PContact contact, Resource *resource, GDateTime *last_activity);
 void cons_show_contact_offline(PContact contact, char *resource, char *status);
diff --git a/src/ui/win_types.h b/src/ui/win_types.h
index 81944bc0..5da1765a 100644
--- a/src/ui/win_types.h
+++ b/src/ui/win_types.h
@@ -138,6 +138,7 @@ typedef enum {
 typedef struct prof_win_t {
     win_type_t type;
     ProfLayout *layout;
+    Autocomplete urls_ac;
 } ProfWin;
 
 typedef struct prof_console_win_t {
diff --git a/src/ui/window_list.c b/src/ui/window_list.c
index b1c01c41..e506a957 100644
--- a/src/ui/window_list.c
+++ b/src/ui/window_list.c
@@ -534,7 +534,7 @@ wins_close_by_num(int i)
                         }
                     }
                 }
-
+                autocomplete_free(window->urls_ac);
                 break;
             }
             case WIN_MUC:
@@ -546,6 +546,7 @@ wins_close_by_num(int i)
                 if (mucwin->last_msg_timestamp) {
                     g_date_time_unref(mucwin->last_msg_timestamp);
                 }
+                autocomplete_free(window->urls_ac);
                 break;
             }
             case WIN_PRIVATE:
@@ -553,6 +554,7 @@ wins_close_by_num(int i)
                 ProfPrivateWin *privwin = (ProfPrivateWin*)window;
                 autocomplete_remove(wins_ac, privwin->fulljid);
                 autocomplete_remove(wins_close_ac, privwin->fulljid);
+                autocomplete_free(window->urls_ac);
                 break;
             }
             case WIN_XML:
@@ -624,6 +626,7 @@ wins_new_chat(const char *const barejid)
             autocomplete_add(wins_close_ac, nick);
         }
     }
+    newwin->urls_ac = autocomplete_new();
 
     return newwin;
 }
@@ -638,6 +641,8 @@ wins_new_muc(const char *const roomjid)
     g_hash_table_insert(windows, GINT_TO_POINTER(result), newwin);
     autocomplete_add(wins_ac, roomjid);
     autocomplete_add(wins_close_ac, roomjid);
+    newwin->urls_ac = autocomplete_new();
+
     return newwin;
 }
 
@@ -649,6 +654,7 @@ wins_new_config(const char *const roomjid, DataForm *form, ProfConfWinCallback s
     g_list_free(keys);
     ProfWin *newwin = win_create_config(roomjid, form, submit, cancel, userdata);
     g_hash_table_insert(windows, GINT_TO_POINTER(result), newwin);
+
     return newwin;
 }
 
@@ -662,6 +668,8 @@ wins_new_private(const char *const fulljid)
     g_hash_table_insert(windows, GINT_TO_POINTER(result), newwin);
     autocomplete_add(wins_ac, fulljid);
     autocomplete_add(wins_close_ac, fulljid);
+    newwin->urls_ac = autocomplete_new();
+
     return newwin;
 }
 
@@ -1141,3 +1149,36 @@ wins_get_next_unread(void)
     g_list_free(values);
     return NULL;
 }
+
+void
+wins_add_urls_ac(const ProfWin *const win, const ProfMessage *const message)
+{
+    GRegex *regex;
+    GMatchInfo *match_info;
+
+    regex = g_regex_new("https?://\\S+", 0, 0, NULL);
+    g_regex_match (regex, message->plain, 0, &match_info);
+
+    while (g_match_info_matches (match_info))
+    {
+        gchar *word = g_match_info_fetch (match_info, 0);
+
+        autocomplete_add(win->urls_ac, word);
+        // for people who run profanity a long time, we don't want to waste a lot of memory
+        autocomplete_remove_older_than_max(win->urls_ac, 20);
+
+        g_free (word);
+        g_match_info_next (match_info, NULL);
+    }
+
+    g_match_info_free (match_info);
+    g_regex_unref (regex);
+}
+
+char*
+wins_get_url(const char *const search_str, gboolean previous, void *context)
+{
+    ProfWin *win = (ProfWin*)context;
+
+    return autocomplete_complete(win->urls_ac, search_str, FALSE, previous);
+}
diff --git a/src/ui/window_list.h b/src/ui/window_list.h
index bec59721..6547354d 100644
--- a/src/ui/window_list.h
+++ b/src/ui/window_list.h
@@ -98,4 +98,7 @@ void win_reset_search_attempts(void);
 char* win_close_autocomplete(const char *const search_str, gboolean previous, void *context);
 void win_close_reset_search_attempts(void);
 
+void wins_add_urls_ac(const ProfWin *const win, const ProfMessage *const message);
+char* wins_get_url(const char *const search_str, gboolean previous, void *context);
+
 #endif
diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c
index 9d043cb3..701d6cb7 100644
--- a/src/xmpp/avatar.c
+++ b/src/xmpp/avatar.c
@@ -266,15 +266,7 @@ _avatar_request_item_result_handler(xmpp_stanza_t *const stanza, void *const use
 
     // if we shall open it
     if (g_hash_table_contains(shall_open, from_attr)) {
-        GString *cmd = g_string_new("");
-
-        g_string_append_printf(cmd, "%s %s > /dev/null 2>&1", prefs_get_string(PREF_AVATAR_CMD), filename->str);
-        cons_show("Calling: %s", cmd->str);
-        FILE *stream = popen(cmd->str, "r");
-
-        pclose(stream);
-        g_string_free(cmd, TRUE);
-
+        call_external(prefs_get_string(PREF_AVATAR_CMD), filename->str);
         g_hash_table_remove(shall_open, from_attr);
     }