1
0
mirror of https://github.com/profanity-im/profanity.git synced 2024-09-22 19:45:54 -04:00

Merge pull request #1341 from profanity-im/feature/urlopen

Add urlopen command
This commit is contained in:
Michael Vetter 2020-05-20 14:15:53 +02:00 committed by GitHub
commit 7862542c58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 212 additions and 24 deletions

View File

@ -54,6 +54,7 @@
#include "xmpp/muc.h" #include "xmpp/muc.h"
#include "xmpp/xmpp.h" #include "xmpp/xmpp.h"
#include "xmpp/roster_list.h" #include "xmpp/roster_list.h"
#include "ui/buffer.h"
#ifdef HAVE_LIBGPGME #ifdef HAVE_LIBGPGME
#include "pgp/gpg.h" #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* _correction_autocomplete(ProfWin *window, const char *const input, gboolean previous);
static char* _correct_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* _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); 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 color_ac;
static Autocomplete correction_ac; static Autocomplete correction_ac;
static Autocomplete avatar_ac; static Autocomplete avatar_ac;
static Autocomplete executable_ac;
void void
cmd_ac_init(void) cmd_ac_init(void)
@ -986,8 +990,11 @@ cmd_ac_init(void)
avatar_ac = autocomplete_new(); avatar_ac = autocomplete_new();
autocomplete_add(avatar_ac, "get"); autocomplete_add(avatar_ac, "get");
autocomplete_add(avatar_ac, "cmd");
autocomplete_add(avatar_ac, "open"); autocomplete_add(avatar_ac, "open");
executable_ac = autocomplete_new();
autocomplete_add(executable_ac, "avatar");
autocomplete_add(executable_ac, "urlopen");
} }
void void
@ -1304,6 +1311,7 @@ cmd_ac_reset(ProfWin *window)
autocomplete_reset(color_ac); autocomplete_reset(color_ac);
autocomplete_reset(correction_ac); autocomplete_reset(correction_ac);
autocomplete_reset(avatar_ac); autocomplete_reset(avatar_ac);
autocomplete_reset(executable_ac);
autocomplete_reset(script_ac); autocomplete_reset(script_ac);
if (script_show_ac) { if (script_show_ac) {
@ -1462,6 +1470,7 @@ cmd_ac_uninit(void)
autocomplete_free(color_ac); autocomplete_free(color_ac);
autocomplete_free(correction_ac); autocomplete_free(correction_ac);
autocomplete_free(avatar_ac); autocomplete_free(avatar_ac);
autocomplete_free(executable_ac);
} }
static void 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, "/correction", _correction_autocomplete);
g_hash_table_insert(ac_funcs, "/correct", _correct_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, "/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); int len = strlen(input);
char parsed[len+1]; char parsed[len+1];
@ -3912,3 +3923,30 @@ _software_autocomplete(ProfWin *window, const char *const input, gboolean previo
return result; 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;
}

View File

@ -2340,26 +2340,23 @@ static struct cmd_t command_defs[] =
}, },
{ "/avatar", { "/avatar",
parse_args, 2, 2, &cons_avatar_setting, parse_args, 2, 2, NULL,
CMD_NOSUBFUNCS CMD_NOSUBFUNCS
CMD_MAINFUNC(cmd_avatar) CMD_MAINFUNC(cmd_avatar)
CMD_TAGS( CMD_TAGS(
CMD_TAG_CHAT) CMD_TAG_CHAT)
CMD_SYN( CMD_SYN(
"/avatar get <barejid>", "/avatar get <barejid>",
"/avatar open <barejid>", "/avatar open <barejid>")
"/avatar cmd <command>")
CMD_DESC( CMD_DESC(
"Download avatar (XEP-0084) for a certain contact. " "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 " "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.") "or doesn't use XEP-0084 to publish it.")
CMD_ARGS( CMD_ARGS(
{ "get <barejid>", "Download the avatar. barejid is the JID to download avatar from."}, { "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."}) { "open <barejid>", "Download avatar and open it with command."})
CMD_EXAMPLES( CMD_EXAMPLES(
"/avatar get thor@valhalla.edda", "/avatar get thor@valhalla.edda",
"/avatar cmd xdg-open",
"/avatar open freyja@vanaheimr.edda") "/avatar open freyja@vanaheimr.edda")
}, },
@ -2449,7 +2446,43 @@ static struct cmd_t command_defs[] =
CMD_EXAMPLES( CMD_EXAMPLES(
"/software valhalla.edda", "/software valhalla.edda",
"/software xmpp.vanaheimr.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; static GHashTable *search_index;

View File

@ -8858,3 +8858,39 @@ cmd_slashguard(ProfWin *window, const char *const command, gchar **args)
return TRUE; 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;
}

View File

@ -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_correct(ProfWin *window, const char *const command, gchar **args);
gboolean cmd_slashguard(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_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 #endif

View File

@ -483,3 +483,16 @@ get_mentions(gboolean whole_word, gboolean case_sensitive, const char *const mes
return mentions; 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);
}

View File

@ -106,4 +106,6 @@ void get_file_paths_recursive(const char *directory, GSList **contents);
char* get_random_string(int length); char* get_random_string(int length);
void call_external(const char *const exe, const char *const param);
#endif #endif

View File

@ -1783,6 +1783,7 @@ _get_group(preference_t pref)
case PREF_LOG_ROTATE: case PREF_LOG_ROTATE:
case PREF_LOG_SHARED: case PREF_LOG_SHARED:
case PREF_AVATAR_CMD: case PREF_AVATAR_CMD:
case PREF_URL_OPEN_CMD:
return PREF_GROUP_LOGGING; return PREF_GROUP_LOGGING;
case PREF_AUTOAWAY_CHECK: case PREF_AUTOAWAY_CHECK:
case PREF_AUTOAWAY_MODE: case PREF_AUTOAWAY_MODE:
@ -2070,6 +2071,8 @@ _get_key(preference_t pref)
return "slashguard"; return "slashguard";
case PREF_MAM: case PREF_MAM:
return "mam"; return "mam";
case PREF_URL_OPEN_CMD:
return "urlopen.cmd";
default: default:
return NULL; return NULL;
} }
@ -2205,6 +2208,7 @@ _get_default_string(preference_t pref)
case PREF_COLOR_NICK: case PREF_COLOR_NICK:
return "false"; return "false";
case PREF_AVATAR_CMD: case PREF_AVATAR_CMD:
case PREF_URL_OPEN_CMD:
return "xdg-open"; return "xdg-open";
default: default:
return NULL; return NULL;

View File

@ -170,6 +170,7 @@ typedef enum {
PREF_AVATAR_CMD, PREF_AVATAR_CMD,
PREF_SLASH_GUARD, PREF_SLASH_GUARD,
PREF_MAM, PREF_MAM,
PREF_URL_OPEN_CMD,
} preference_t; } preference_t;
typedef struct prof_alias_t { typedef struct prof_alias_t {

View File

@ -385,6 +385,18 @@ autocomplete_param_no_with_func(const char *const input, char *command, int arg_
return NULL; 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* static gchar*
_search_next(Autocomplete ac, GList *curr, gboolean quote) _search_next(Autocomplete ac, GList *curr, gboolean quote)
{ {

View File

@ -74,4 +74,6 @@ char* autocomplete_param_no_with_func(const char *const input, char *command,
void autocomplete_reset(Autocomplete ac); void autocomplete_reset(Autocomplete ac);
gboolean autocomplete_contains(Autocomplete ac, const char *value); gboolean autocomplete_contains(Autocomplete ac, const char *value);
void autocomplete_remove_older_than_max(Autocomplete ac, int maxsize);
#endif #endif

View File

@ -292,7 +292,7 @@ chatwin_incoming_msg(ProfChatWin *chatwin, ProfMessage *message, gboolean win_cr
//1) only send IQ once //1) only send IQ once
//2) sort incoming messages on timestamp //2) sort incoming messages on timestamp
//for now if experimental MAM is enabled we dont show no history from sql either //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 // 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) // (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) { 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); win_print_incoming(window, display_name, message);
} }
wins_add_urls_ac(window, message);
if (prefs_get_boolean(PREF_BEEP)) { if (prefs_get_boolean(PREF_BEEP)) {
beep(); beep();
} }

View File

@ -2051,13 +2051,15 @@ cons_correction_setting(void)
} }
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); char *exec = prefs_get_string(PREF_URL_OPEN_CMD);
cons_show("urlopen command (/executable urlopen) : %s", exec);
prefs_free_string(pref); prefs_free_string(exec);
} }
void void

View File

@ -557,6 +557,7 @@ mucwin_incoming_msg(ProfMucWin *mucwin, const ProfMessage *const message, GSList
} }
win_insert_last_read_position_marker((ProfWin*)mucwin, mucwin->roomjid); win_insert_last_read_position_marker((ProfWin*)mucwin, mucwin->roomjid);
wins_add_urls_ac(window, message);
if (g_slist_length(mentions) > 0) { if (g_slist_length(mentions) > 0) {
_mucwin_print_mention(window, message->plain, message->from_jid->resourcepart, mynick, mentions, ch, flags); _mucwin_print_mention(window, message->plain, message->from_jid->resourcepart, mynick, mentions, ch, flags);

View File

@ -79,6 +79,8 @@ privwin_incoming_msg(ProfPrivateWin *privatewin, ProfMessage *message)
} }
} }
wins_add_urls_ac(window, message);
if (prefs_get_boolean(PREF_BEEP)) { if (prefs_get_boolean(PREF_BEEP)) {
beep(); beep();
} }

View File

@ -319,7 +319,7 @@ void cons_winpos_setting(void);
void cons_color_setting(void); void cons_color_setting(void);
void cons_os_setting(void); void cons_os_setting(void);
void cons_correction_setting(void); void cons_correction_setting(void);
void cons_avatar_setting(void); void cons_executable_setting(void);
void cons_slashguard_setting(void); void cons_slashguard_setting(void);
void cons_show_contact_online(PContact contact, Resource *resource, GDateTime *last_activity); void cons_show_contact_online(PContact contact, Resource *resource, GDateTime *last_activity);
void cons_show_contact_offline(PContact contact, char *resource, char *status); void cons_show_contact_offline(PContact contact, char *resource, char *status);

View File

@ -138,6 +138,7 @@ typedef enum {
typedef struct prof_win_t { typedef struct prof_win_t {
win_type_t type; win_type_t type;
ProfLayout *layout; ProfLayout *layout;
Autocomplete urls_ac;
} ProfWin; } ProfWin;
typedef struct prof_console_win_t { typedef struct prof_console_win_t {

View File

@ -534,7 +534,7 @@ wins_close_by_num(int i)
} }
} }
} }
autocomplete_free(window->urls_ac);
break; break;
} }
case WIN_MUC: case WIN_MUC:
@ -546,6 +546,7 @@ wins_close_by_num(int i)
if (mucwin->last_msg_timestamp) { if (mucwin->last_msg_timestamp) {
g_date_time_unref(mucwin->last_msg_timestamp); g_date_time_unref(mucwin->last_msg_timestamp);
} }
autocomplete_free(window->urls_ac);
break; break;
} }
case WIN_PRIVATE: case WIN_PRIVATE:
@ -553,6 +554,7 @@ wins_close_by_num(int i)
ProfPrivateWin *privwin = (ProfPrivateWin*)window; ProfPrivateWin *privwin = (ProfPrivateWin*)window;
autocomplete_remove(wins_ac, privwin->fulljid); autocomplete_remove(wins_ac, privwin->fulljid);
autocomplete_remove(wins_close_ac, privwin->fulljid); autocomplete_remove(wins_close_ac, privwin->fulljid);
autocomplete_free(window->urls_ac);
break; break;
} }
case WIN_XML: case WIN_XML:
@ -624,6 +626,7 @@ wins_new_chat(const char *const barejid)
autocomplete_add(wins_close_ac, nick); autocomplete_add(wins_close_ac, nick);
} }
} }
newwin->urls_ac = autocomplete_new();
return newwin; return newwin;
} }
@ -638,6 +641,8 @@ wins_new_muc(const char *const roomjid)
g_hash_table_insert(windows, GINT_TO_POINTER(result), newwin); g_hash_table_insert(windows, GINT_TO_POINTER(result), newwin);
autocomplete_add(wins_ac, roomjid); autocomplete_add(wins_ac, roomjid);
autocomplete_add(wins_close_ac, roomjid); autocomplete_add(wins_close_ac, roomjid);
newwin->urls_ac = autocomplete_new();
return newwin; return newwin;
} }
@ -649,6 +654,7 @@ wins_new_config(const char *const roomjid, DataForm *form, ProfConfWinCallback s
g_list_free(keys); g_list_free(keys);
ProfWin *newwin = win_create_config(roomjid, form, submit, cancel, userdata); ProfWin *newwin = win_create_config(roomjid, form, submit, cancel, userdata);
g_hash_table_insert(windows, GINT_TO_POINTER(result), newwin); g_hash_table_insert(windows, GINT_TO_POINTER(result), newwin);
return newwin; return newwin;
} }
@ -662,6 +668,8 @@ wins_new_private(const char *const fulljid)
g_hash_table_insert(windows, GINT_TO_POINTER(result), newwin); g_hash_table_insert(windows, GINT_TO_POINTER(result), newwin);
autocomplete_add(wins_ac, fulljid); autocomplete_add(wins_ac, fulljid);
autocomplete_add(wins_close_ac, fulljid); autocomplete_add(wins_close_ac, fulljid);
newwin->urls_ac = autocomplete_new();
return newwin; return newwin;
} }
@ -1141,3 +1149,36 @@ wins_get_next_unread(void)
g_list_free(values); g_list_free(values);
return NULL; 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);
}

View File

@ -98,4 +98,7 @@ void win_reset_search_attempts(void);
char* win_close_autocomplete(const char *const search_str, gboolean previous, void *context); char* win_close_autocomplete(const char *const search_str, gboolean previous, void *context);
void win_close_reset_search_attempts(void); 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 #endif

View File

@ -266,15 +266,7 @@ _avatar_request_item_result_handler(xmpp_stanza_t *const stanza, void *const use
// if we shall open it // if we shall open it
if (g_hash_table_contains(shall_open, from_attr)) { if (g_hash_table_contains(shall_open, from_attr)) {
GString *cmd = g_string_new(""); call_external(prefs_get_string(PREF_AVATAR_CMD), filename->str);
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);
g_hash_table_remove(shall_open, from_attr); g_hash_table_remove(shall_open, from_attr);
} }