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 ", - "/avatar open ", - "/avatar cmd ") + "/avatar open ") 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 ", "Download the avatar. barejid is the JID to download avatar from."}, - { "cmd ", "Set a command to execute with 'avatar open'. Use your favourite image viewer here."}, { "open ", "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 ") + CMD_DESC( + "Open the URL") + CMD_ARGS( + { "", "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 ", + "/executable urlopen ") + 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); }