1
0
mirror of https://github.com/profanity-im/profanity.git synced 2024-11-03 19:37:16 -05:00

Merge pull request #1374 from profanity-im/revampUrlopen

Rework /url and /executable for filetypes
This commit is contained in:
Michael Vetter 2020-07-02 11:26:18 +02:00 committed by GitHub
commit 9774b0c550
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 433 additions and 59 deletions

View File

@ -124,7 +124,7 @@ 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* _url_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);
@ -263,6 +263,7 @@ static Autocomplete logging_group_ac;
static Autocomplete color_ac;
static Autocomplete correction_ac;
static Autocomplete avatar_ac;
static Autocomplete url_ac;
static Autocomplete executable_ac;
/*!
@ -1041,9 +1042,14 @@ cmd_ac_init(void)
autocomplete_add(avatar_ac, "get");
autocomplete_add(avatar_ac, "open");
url_ac = autocomplete_new();
autocomplete_add(url_ac, "open");
autocomplete_add(url_ac, "save");
executable_ac = autocomplete_new();
autocomplete_add(executable_ac, "avatar");
autocomplete_add(executable_ac, "urlopen");
autocomplete_add(executable_ac, "urlsave");
}
void
@ -1362,6 +1368,7 @@ cmd_ac_reset(ProfWin *window)
autocomplete_reset(color_ac);
autocomplete_reset(correction_ac);
autocomplete_reset(avatar_ac);
autocomplete_reset(url_ac);
autocomplete_reset(executable_ac);
autocomplete_reset(script_ac);
@ -1523,6 +1530,7 @@ cmd_ac_uninit(void)
autocomplete_free(color_ac);
autocomplete_free(correction_ac);
autocomplete_free(avatar_ac);
autocomplete_free(url_ac);
autocomplete_free(executable_ac);
}
@ -1784,7 +1792,7 @@ _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, "/url", _url_autocomplete);
g_hash_table_insert(ac_funcs, "/executable", _executable_autocomplete);
int len = strlen(input);
@ -4133,14 +4141,24 @@ _software_autocomplete(ProfWin *window, const char *const input, gboolean previo
}
static char*
_urlopen_autocomplete(ProfWin *window, const char *const input, gboolean previous)
_url_autocomplete(ProfWin *window, const char *const input, gboolean previous)
{
char *result = NULL;
result = autocomplete_param_with_ac(input, "/url", url_ac, TRUE, previous);
if (result) {
return result;
}
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);
result = autocomplete_param_with_func(input, "/url open", wins_get_url, previous, window);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/url save", wins_get_url, previous, window);
}
return result;

View File

@ -2506,40 +2506,50 @@ static struct cmd_t command_defs[] =
"/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,
parse_args, 2, 4, &cons_executable_setting,
CMD_NOSUBFUNCS
CMD_MAINFUNC(cmd_executable)
CMD_TAGS(
CMD_TAG_DISCOVERY)
CMD_SYN(
"/executable avatar <cmd>",
"/executable urlopen <cmd>")
"/executable urlopen (<fileType>|DEF <require_save> <cmd>",
"/executable urlsave (<protocol>|DEF) <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." })
{ "urlopen", "Set executable that is run in /url open for a given file type. It may be your favorite browser or a specific viewer. Use DEF to set default command for undefined file type." },
{ "urlsave", "Set executable that is run in /url save for a given protocol. Use your favourite downloader. Use DEF to set default command for undefined protocol."})
CMD_EXAMPLES(
"/executable avatar xdg-open",
"/executable urlopen firefox")
"/executable urlopen DEF false \"xdg-open %u\"",
"/executable urlopen html false \"firefox %u\"",
"/executable urlsave aesgcm \"omut -d %u %p\"")
},
{ "/url",
parse_args, 2, 3, NULL,
CMD_SUBFUNCS(
{ "open", cmd_url_open},
{ "save", cmd_url_save })
CMD_NOMAINFUNC
CMD_TAGS(
CMD_TAG_CHAT,
CMD_TAG_GROUPCHAT)
CMD_SYN(
"/url open <url>",
"/url save <url> [<path>]")
CMD_DESC(
"Deal with URLs")
CMD_ARGS(
{ "open", "Open URL with predefined executable." },
{ "save", "Save URL to optional path, default path is current directory"})
CMD_EXAMPLES(
"/url open https://profanity-im.github.io",
"/url save https://profanity-im.github.io/guide/latest/userguide.html /home/user/Download/")
},
};

View File

@ -44,6 +44,8 @@
#include <errno.h>
#include <assert.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
@ -9071,29 +9073,210 @@ cmd_slashguard(ProfWin *window, const char *const command, gchar **args)
}
gboolean
cmd_urlopen(ProfWin *window, const char *const command, gchar **args)
cmd_url_open(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;
}
gchar* cmd = prefs_get_string(PREF_URL_OPEN_CMD);
gchar *argv[] = {cmd, args[0], NULL};
if (!call_external(argv, NULL, NULL)) {
cons_show_error("Unable to open url: check the logs for more information.");
}
g_free(cmd);
} else {
cons_show("urlopen not supported in this window");
if (window->type != WIN_CHAT &&
window->type != WIN_MUC &&
window->type != WIN_PRIVATE) {
cons_show("url open not supported in this window");
return TRUE;
}
if (args[1] == NULL) {
cons_bad_cmd_usage(command);
return TRUE;
}
gboolean require_save = false;
gchar *fileStart = g_strrstr(args[1], "/");
if (fileStart == NULL) {
cons_show("URL '%s' is not valid.", args[1]);
return TRUE;
}
fileStart++;
if (((char*)(fileStart - 2))[0] == '/' &&
((char*)(fileStart - 3))[0] == ':'
){
// If the '/' is last character of the '://' string, there will be no suffix
// Therefore, it is considered that there is no file name in the URL and
// fileStart is set to the end of the URL.
fileStart = args[1] + strlen(args[1]);
}
gchar *suffix = NULL;
gchar *suffixStart = g_strrstr(fileStart, ".");
if (suffixStart != NULL) {
suffixStart++;
gchar *suffixEnd = g_strrstr(suffixStart, "#");
if(suffixEnd == NULL) {
suffix = g_strdup(suffixStart);
} else {
suffix = g_strndup(suffixStart, suffixEnd - suffixStart);
}
}
gchar **suffix_cmd_pref = prefs_get_string_list_with_option(PREF_URL_OPEN_CMD, NULL);
if (suffix != NULL) {
gchar *lowercase_suffix = g_ascii_strdown(suffix, -1);
g_strfreev(suffix_cmd_pref);
suffix_cmd_pref = prefs_get_string_list_with_option(PREF_URL_OPEN_CMD, lowercase_suffix);
g_free(lowercase_suffix);
g_free(suffix);
}
if (0 == g_strcmp0(suffix_cmd_pref[0], "true")) {
require_save = true;
}
gchar *suffix_cmd = g_strdup(suffix_cmd_pref[1]);
g_strfreev(suffix_cmd_pref);
gchar *scheme = g_uri_parse_scheme(args[1]);
if( 0 == g_strcmp0(scheme, "aesgcm")) {
require_save = true;
}
g_free(scheme);
if (require_save) {
gchar *save_args[] = { "open", args[1], "/tmp/profanity.tmp", NULL};
cmd_url_save(window, command, save_args);
}
gchar **argv = g_strsplit(suffix_cmd, " ", 0);
guint num_args = 0;
while (argv[num_args]) {
if (0 == g_strcmp0(argv[num_args], "%u")) {
g_free(argv[num_args]);
if (require_save) {
argv[num_args] = g_strdup("/tmp/profanity.tmp");
} else {
argv[num_args] = g_strdup(args[1]);
}
break;
}
num_args++;
}
if (!call_external(argv, NULL, NULL)) {
cons_show_error("Unable to open url: check the logs for more information.");
}
if (require_save) {
g_unlink("/tmp/profanity.tmp");
}
g_strfreev(argv);
g_free(suffix_cmd);
return TRUE;
}
gboolean
cmd_url_save(ProfWin *window, const char *const command, gchar **args)
{
if (window->type != WIN_CHAT &&
window->type != WIN_MUC &&
window->type != WIN_PRIVATE) {
cons_show("url save not supported in this window");
return TRUE;
}
if (args[1] == NULL) {
cons_bad_cmd_usage(command);
return TRUE;
}
gchar *uri = args[1];
gchar *target_path = g_strdup(args[2]);
GFile *file = g_file_new_for_uri(uri);
gchar *target_dir = NULL;
gchar *base_name = NULL;
if (target_path == NULL) {
target_dir = g_strdup("./");
base_name = g_file_get_basename(file);
if (0 == g_strcmp0(base_name, ".")) {
g_free(base_name);
base_name = g_strdup("saved_url_content.html");
}
target_path = g_strconcat(target_dir, base_name, NULL);
}
if (g_file_test(target_path, G_FILE_TEST_EXISTS) &&
g_file_test(target_path, G_FILE_TEST_IS_DIR)
) {
target_dir = g_strdup(target_path);
base_name = g_file_get_basename(file);
g_free(target_path);
target_path = g_strconcat(target_dir, "/", base_name, NULL);
}
g_object_unref(file);
file = NULL;
if (base_name == NULL) {
base_name = g_path_get_basename(target_path);
target_dir = g_path_get_dirname(target_path);
}
if (!g_file_test(target_dir, G_FILE_TEST_EXISTS) ||
!g_file_test(target_dir, G_FILE_TEST_IS_DIR)) {
cons_show("%s does not exist or is not a directory.", target_dir);
g_free(target_path);
g_free(target_dir);
g_free(base_name);
return TRUE;
}
gchar *scheme = g_uri_parse_scheme(uri);
if (scheme == NULL) {
cons_show("URL '%s' is not valid.", uri);
g_free(target_path);
g_free(target_dir);
g_free(base_name);
return TRUE;
}
gchar *scheme_cmd = NULL;
if (0 == g_strcmp0(scheme, "http")
|| 0 == g_strcmp0(scheme, "https")
|| 0 == g_strcmp0(scheme, "aesgcm")
) {
scheme_cmd = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme);
}
g_free(scheme);
gchar **argv = g_strsplit(scheme_cmd, " ", 0);
g_free(scheme_cmd);
guint num_args = 0;
while (argv[num_args]) {
if (0 == g_strcmp0(argv[num_args], "%u")) {
g_free(argv[num_args]);
argv[num_args] = g_strdup(uri);
} else if (0 == g_strcmp0(argv[num_args], "%p")) {
g_free(argv[num_args]);
argv[num_args] = target_path;
}
num_args++;
}
if (!call_external(argv, NULL, NULL)) {
cons_show_error("Unable to save url: check the logs for more information.");
} else {
cons_show("URL '%s' has been saved into '%s'.", uri, target_path);
}
g_free(target_dir);
g_free(base_name);
g_strfreev(argv);
return TRUE;
}
@ -9104,8 +9287,26 @@ cmd_executable(ProfWin *window, const char *const command, gchar **args)
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]);
if (g_strv_length(args) < 4) {
cons_bad_cmd_usage(command);
return TRUE;
}
gchar *str = g_strjoinv(" ", &args[3]);
const gchar* const list[] = {args[2], str, NULL};
prefs_set_string_list_with_option(PREF_URL_OPEN_CMD, args[1], list);
cons_show("`url open` command set to: %s for %s files", str, args[1]);
g_free(str);
} else if (g_strcmp0(args[0], "urlsave") == 0) {
if (g_strv_length(args) < 3) {
cons_bad_cmd_usage(command);
return TRUE;
}
gchar *str = g_strjoinv(" ", &args[2]);
prefs_set_string_with_option(PREF_URL_SAVE_CMD, args[1], str);
cons_show("`url save` command set to: %s for scheme %s", str, args[1]);
g_free(str);
} else {
cons_bad_cmd_usage(command);
}

View File

@ -236,7 +236,8 @@ 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_url_open(ProfWin *window, const char *const command, gchar **args);
gboolean cmd_url_save(ProfWin *window, const char *const command, gchar **args);
gboolean cmd_executable(ProfWin *window, const char *const command, gchar **args);
#endif

View File

@ -65,6 +65,7 @@
#define PREF_GROUP_OX "ox"
#define PREF_GROUP_MUC "muc"
#define PREF_GROUP_PLUGINS "plugins"
#define PREF_GROUP_EXECUTABLES "executables"
#define INPBLOCK_DEFAULT 1000
@ -80,6 +81,7 @@ static const char* _get_group(preference_t pref);
static const char* _get_key(preference_t pref);
static gboolean _get_default_boolean(preference_t pref);
static char* _get_default_string(preference_t pref);
static char** _get_default_string_list(preference_t pref);
static void _prefs_load(void)
{
@ -160,6 +162,27 @@ static void _prefs_load(void)
}
}
// 0.9.0 introduced /urlopen. It was saved under "logging" section. Now we have a new "executables" section.
if (g_key_file_has_key(prefs, PREF_GROUP_LOGGING, "urlopen.cmd", NULL)) {
char *val = g_key_file_get_string(prefs, PREF_GROUP_LOGGING, "urlopen.cmd", NULL);
GString *value = g_string_new("false;");
value = g_string_append(value, val);
value = g_string_append(value, " %u;");
g_key_file_set_locale_string(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", "DEF", value->str);
g_key_file_remove_key(prefs, PREF_GROUP_LOGGING, "urlopen.cmd", NULL);
g_string_free(value, TRUE);
}
// 0.9.0 introduced configurable /avatar. It was saved under "logging" section. Now we have a new "executables" section.
if (g_key_file_has_key(prefs, PREF_GROUP_LOGGING, "avatar.cmd", NULL)) {
char *value = g_key_file_get_string(prefs, PREF_GROUP_LOGGING, "avatar.cmd", NULL);
g_key_file_set_string(prefs, PREF_GROUP_EXECUTABLES, "avatar.cmd", value);
g_key_file_remove_key(prefs, PREF_GROUP_LOGGING, "avatar.cmd", NULL);
}
_save_prefs();
boolean_choice_ac = autocomplete_new();
@ -218,7 +241,7 @@ prefs_load(char *config_file)
}
prefs = g_key_file_new();
g_key_file_load_from_file(prefs, prefs_loc, G_KEY_FILE_KEEP_COMMENTS, NULL);
g_key_file_load_from_file(prefs, prefs_loc, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, NULL);
_prefs_load();
}
@ -494,12 +517,62 @@ prefs_get_string(preference_t pref)
}
}
char*
prefs_get_string_with_option(preference_t pref, gchar *option)
{
const char *group = _get_group(pref);
const char *key = _get_key(pref);
char *def = _get_default_string(pref);
char *result = g_key_file_get_locale_string(prefs, group, key, option, NULL);
if (result == NULL) {
// check for user set default
result = g_key_file_get_locale_string(prefs, group, key, "DEF", NULL);
if (result == NULL) {
if (def) {
// use hardcoded profanity default
return g_strdup(def);
} else {
return NULL;
}
}
}
return result;
}
gchar**
prefs_get_string_list_with_option(preference_t pref, gchar *option)
{
const char *group = _get_group(pref);
const char *key = _get_key(pref);
char **def = _get_default_string_list(pref);
gchar **result = g_key_file_get_locale_string_list(prefs, group, key, option, NULL, NULL);
if (result) {
g_strfreev(def);
return result;
}
result = g_key_file_get_string_list(prefs, group, key, NULL, NULL);
if (result) {
g_strfreev(def);
return result;
}
if (def) {
return def;
} else {
g_strfreev(def);
return NULL;
}
}
void
prefs_free_string(char *pref)
{
if (pref) {
g_free(pref);
}
g_free(pref);
}
void
@ -514,6 +587,42 @@ prefs_set_string(preference_t pref, char *value)
}
}
void
prefs_set_string_with_option(preference_t pref, char *option, char *value)
{
const char *group = _get_group(pref);
const char *key = _get_key(pref);
if (value == NULL) {
g_key_file_remove_key(prefs, group, key, NULL);
} else {
g_key_file_set_locale_string(prefs, group, key, option, value);
}
}
void
prefs_set_string_list_with_option(preference_t pref, char *option, const gchar* const *values)
{
const char *group = _get_group(pref);
const char *key = _get_key(pref);
if (values == NULL || *values == NULL){
if (g_strcmp0(option, "*") == 0) {
g_key_file_set_string_list(prefs, group, key, NULL, 0);
} else {
g_key_file_set_locale_string_list(prefs, group, key, option, NULL, 0);
}
} else {
guint num_values = 0;
while(values[num_values]) {
num_values++;
}
if (g_strcmp0(option, "*") == 0) {
g_key_file_set_string_list(prefs, group, key, values, num_values);
} else {
g_key_file_set_locale_string_list(prefs, group, key, option, values, num_values);
}
}
}
char*
prefs_get_tls_certpath(void)
{
@ -1796,9 +1905,11 @@ _get_group(preference_t pref)
case PREF_GRLOG:
case PREF_LOG_ROTATE:
case PREF_LOG_SHARED:
return PREF_GROUP_LOGGING;
case PREF_AVATAR_CMD:
case PREF_URL_OPEN_CMD:
return PREF_GROUP_LOGGING;
case PREF_URL_SAVE_CMD:
return PREF_GROUP_EXECUTABLES;
case PREF_AUTOAWAY_CHECK:
case PREF_AUTOAWAY_MODE:
case PREF_AUTOAWAY_MESSAGE:
@ -2088,7 +2199,9 @@ _get_key(preference_t pref)
case PREF_MAM:
return "mam";
case PREF_URL_OPEN_CMD:
return "urlopen.cmd";
return "url.open.cmd";
case PREF_URL_SAVE_CMD:
return "url.save.cmd";
default:
return NULL;
}
@ -2225,8 +2338,28 @@ _get_default_string(preference_t pref)
case PREF_COLOR_NICK:
return "false";
case PREF_AVATAR_CMD:
case PREF_URL_OPEN_CMD:
return "xdg-open";
case PREF_URL_SAVE_CMD:
return "curl -o %p %u";
default:
return NULL;
}
}
// the default setting for a string list type preference
// if it is not specified in .profrc
static char**
_get_default_string_list(preference_t pref)
{
char **str_array = NULL;
switch (pref)
{
case PREF_URL_OPEN_CMD:
str_array = g_malloc0(3);
str_array[0] = g_strdup("false");
str_array[1] = g_strdup("xdg-open %u");
return str_array;
default:
return NULL;
}

View File

@ -172,6 +172,7 @@ typedef enum {
PREF_SLASH_GUARD,
PREF_MAM,
PREF_URL_OPEN_CMD,
PREF_URL_SAVE_CMD,
} preference_t;
typedef struct prof_alias_t {
@ -317,8 +318,12 @@ void prefs_save_win_placement(ProfWinPlacement *placement);
gboolean prefs_get_boolean(preference_t pref);
void prefs_set_boolean(preference_t pref, gboolean value);
char* prefs_get_string(preference_t pref);
char* prefs_get_string_with_option(preference_t pref, gchar *option);
gchar **prefs_get_string_list_with_option(preference_t pref, gchar *option);
void prefs_free_string(char *pref);
void prefs_set_string(preference_t pref, char *value);
void prefs_set_string_with_option(preference_t pref, char *option, char *value);
void prefs_set_string_list_with_option(preference_t pref, char *option, const gchar* const *values);
char* prefs_get_tls_certpath(void);

View File

@ -2067,12 +2067,18 @@ void
cons_executable_setting(void)
{
char *avatar = prefs_get_string(PREF_AVATAR_CMD);
cons_show("Avatar command (/executable avatar) : %s", avatar);
cons_show("Default '/avatar open' command (/executable avatar) : %s", avatar);
prefs_free_string(avatar);
char *exec = prefs_get_string(PREF_URL_OPEN_CMD);
cons_show("urlopen command (/executable urlopen) : %s", exec);
prefs_free_string(exec);
//TODO: there needs to be a way to get all the "locales"/schemes so we can
//display the defualt openers for all filetypes
gchar **urlopen = prefs_get_string_list_with_option(PREF_URL_OPEN_CMD, "");
cons_show("Default '/url open' command (/executable urlopen) : %s", urlopen[1]);
g_strfreev(urlopen);
char *urlsave = prefs_get_string(PREF_URL_SAVE_CMD);
cons_show("Default '/url save' command (/executable urlsave) : %s", urlsave);
g_free(urlsave);
}
void

View File

@ -1156,7 +1156,7 @@ 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);
regex = g_regex_new("(https?|aesgcm)://\\S+", 0, 0, NULL);
g_regex_match (regex, message->plain, 0, &match_info);
while (g_match_info_matches (match_info))