diff --git a/src/command/command.c b/src/command/command.c index bf323a02..271169cf 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -117,6 +117,7 @@ static char* _subject_autocomplete(ProfWin *window, const char *const input); static char* _console_autocomplete(ProfWin *window, const char *const input); static char* _win_autocomplete(ProfWin *window, const char *const input); static char* _close_autocomplete(ProfWin *window, const char *const input); +static char* _plugins_autocomplete(ProfWin *window, const char *const input); GHashTable *commands = NULL; @@ -1743,14 +1744,17 @@ static struct cmd_t command_defs[] = }, { "/plugins", - cmd_plugins, parse_args, 0, 0, NULL, + cmd_plugins, parse_args, 0, 2, NULL, CMD_NOTAGS CMD_SYN( - "/plugins") + "/plugins", + "/plugins load ") CMD_DESC( - "Show currently installed plugins. ") - CMD_NOARGS - CMD_NOEXAMPLES + "Manage plugins. Passing no arguments lists currently loaded plugins.") + CMD_ARGS( + { "laod ", "Load a plugin." }) + CMD_EXAMPLES( + "/plugin load browser.py") }, { "/prefs", @@ -2022,6 +2026,8 @@ static Autocomplete script_show_ac; static Autocomplete console_ac; static Autocomplete console_msg_ac; static Autocomplete autoping_ac; +static Autocomplete plugins_ac; +static Autocomplete plugins_load_ac; /* * Initialise command autocompleter and history @@ -2348,6 +2354,7 @@ cmd_init(void) autocomplete_add(group_ac, "remove"); theme_load_ac = NULL; + plugins_load_ac = NULL; who_roster_ac = autocomplete_new(); autocomplete_add(who_roster_ac, "chat"); @@ -2572,6 +2579,9 @@ cmd_init(void) autoping_ac = autocomplete_new(); autocomplete_add(autoping_ac, "set"); autocomplete_add(autoping_ac, "timeout"); + + plugins_ac = autocomplete_new(); + autocomplete_add(plugins_ac, "load"); } void @@ -2659,6 +2669,8 @@ cmd_uninit(void) autocomplete_free(console_ac); autocomplete_free(console_msg_ac); autocomplete_free(autoping_ac); + autocomplete_free(plugins_ac); + autocomplete_free(plugins_load_ac); } gboolean @@ -2822,6 +2834,10 @@ cmd_reset_autocomplete(ProfWin *window) autocomplete_free(theme_load_ac); theme_load_ac = NULL; } + if (plugins_load_ac) { + autocomplete_free(plugins_load_ac); + plugins_load_ac = NULL; + } autocomplete_reset(account_ac); autocomplete_reset(account_set_ac); autocomplete_reset(account_clear_ac); @@ -2882,6 +2898,7 @@ cmd_reset_autocomplete(ProfWin *window) autocomplete_reset(console_ac); autocomplete_reset(console_msg_ac); autocomplete_reset(autoping_ac); + autocomplete_reset(plugins_ac); autocomplete_reset(script_ac); if (script_show_ac) { autocomplete_free(script_show_ac); @@ -3148,6 +3165,7 @@ _cmd_complete_parameters(ProfWin *window, const char *const input) g_hash_table_insert(ac_funcs, "/console", _console_autocomplete); g_hash_table_insert(ac_funcs, "/win", _win_autocomplete); g_hash_table_insert(ac_funcs, "/close", _close_autocomplete); + g_hash_table_insert(ac_funcs, "/plugins", _plugins_autocomplete); int len = strlen(input); char parsed[len+1]; @@ -3736,6 +3754,34 @@ _pgp_autocomplete(ProfWin *window, const char *const input) return NULL; } +static char* +_plugins_autocomplete(ProfWin *window, const char *const input) +{ + char *result = NULL; + if ((strncmp(input, "/plugins load ", 14) == 0) && (strlen(input) > 14)) { + if (plugins_load_ac == NULL) { + plugins_load_ac = autocomplete_new(); + GSList *plugins = plugins_file_list(); + GSList *curr = plugins; + while (curr) { + autocomplete_add(plugins_load_ac, curr->data); + curr = g_slist_next(curr); + } + g_slist_free_full(plugins, g_free); + } + result = autocomplete_param_with_ac(input, "/plugins load", plugins_load_ac, TRUE); + if (result) { + return result; + } + } + result = autocomplete_param_with_ac(input, "/plugins", plugins_ac, TRUE); + if (result) { + return result; + } + + return NULL; +} + static char* _theme_autocomplete(ProfWin *window, const char *const input) { diff --git a/src/command/commands.c b/src/command/commands.c index 8ce95c56..8d32237b 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -5657,22 +5657,37 @@ cmd_xa(ProfWin *window, const char *const command, gchar **args) gboolean cmd_plugins(ProfWin *window, const char *const command, gchar **args) { - GSList *plugins = plugins_get_list(); - - GSList *curr = plugins; - if (curr == NULL) { - cons_show("No plugins installed."); - } else { - cons_show("Installed plugins:"); - while (curr) { - ProfPlugin *plugin = curr->data; - char *lang = plugins_get_lang_string(plugin); - cons_show(" %s (%s)", plugin->name, lang); - curr = g_slist_next(curr); + if (g_strcmp0(args[0], "load") == 0) { + if (args[1] == NULL) { + cons_bad_cmd_usage(command); + return TRUE; } + gboolean res = plugins_load(args[1]); + if (res) { + prefs_add_plugin(args[1]); + cons_show("Loaded plugin: %s", args[1]); + } else { + cons_show("Failed to load plugin: %s", args[1]); + } + + return TRUE; + } else { + GSList *plugins = plugins_get_list(); + GSList *curr = plugins; + if (curr == NULL) { + cons_show("No plugins installed."); + } else { + cons_show("Installed plugins:"); + while (curr) { + ProfPlugin *plugin = curr->data; + cons_show(" %s", plugin->name); + curr = g_slist_next(curr); + } + } + g_slist_free(curr); + + return TRUE; } - g_slist_free(curr); - return TRUE; } gboolean diff --git a/src/config/preferences.c b/src/config/preferences.c index 8844b389..0ad95dc4 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -604,6 +604,13 @@ prefs_get_plugins(void) return g_key_file_get_string_list(prefs, "plugins", "load", NULL, NULL); } +void +prefs_add_plugin(const char *const name) +{ + conf_string_list_add(prefs, "plugins", "load", name); + _save_prefs(); +} + void prefs_free_plugins(gchar **plugins) { diff --git a/src/config/preferences.h b/src/config/preferences.h index 8f18149c..287e56d3 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -187,6 +187,7 @@ void prefs_set_autoxa_time(gint value); gchar** prefs_get_plugins(void); void prefs_free_plugins(gchar **plugins); +void prefs_add_plugin(const char *const name); char prefs_get_otr_char(void); void prefs_set_otr_char(char ch); diff --git a/src/plugins/c_plugins.c b/src/plugins/c_plugins.c index 07a618b6..ff05791d 100644 --- a/src/plugins/c_plugins.c +++ b/src/plugins/c_plugins.c @@ -74,10 +74,8 @@ c_plugin_create(const char *const filename) return NULL; } - gchar *module_name = g_strndup(filename, strlen(filename) - 3); - plugin = malloc(sizeof(ProfPlugin)); - plugin->name = strdup(module_name); + plugin->name = strdup(filename); plugin->lang = LANG_C; plugin->module = handle; plugin->init_func = c_init_hook; @@ -110,7 +108,6 @@ c_plugin_create(const char *const filename) plugin->on_room_win_focus = c_on_room_win_focus_hook; g_string_free(path, TRUE); - g_free(module_name); return plugin; } diff --git a/src/plugins/plugins.c b/src/plugins/plugins.c index 73f3f873..e2d0bdf3 100644 --- a/src/plugins/plugins.c +++ b/src/plugins/plugins.c @@ -78,13 +78,13 @@ plugins_init(void) plugin_settings_init(); // load plugins - gchar **plugins_load = prefs_get_plugins(); - if (plugins_load) { + gchar **plugins_pref = prefs_get_plugins(); + if (plugins_pref) { int i; - for (i = 0; i < g_strv_length(plugins_load); i++) + for (i = 0; i < g_strv_length(plugins_pref); i++) { gboolean loaded = FALSE; - gchar *filename = plugins_load[i]; + gchar *filename = plugins_pref[i]; #ifdef HAVE_PYTHON if (g_str_has_suffix(filename, ".py")) { ProfPlugin *plugin = python_plugin_create(filename); @@ -103,8 +103,10 @@ plugins_init(void) } } #endif - if (loaded == TRUE) { + if (loaded) { log_info("Loaded plugin: %s", filename); + } else { + log_info("Failed to load plugin: %s", filename); } } @@ -117,29 +119,94 @@ plugins_init(void) } } - prefs_free_plugins(plugins_load); + prefs_free_plugins(plugins_pref); return; } +gboolean +_find_by_name(gconstpointer pluginp, gconstpointer namep) +{ + char *name = (char*)namep; + ProfPlugin *plugin = (ProfPlugin*)pluginp; + + return g_strcmp0(name, plugin->name); +} + +gboolean +plugins_load(const char *const name) +{ + GSList *found = g_slist_find_custom(plugins, name, (GCompareFunc)_find_by_name); + if (found) { + log_info("Failed to load plugin: %s, plugin already loaded", name); + return FALSE; + } + + ProfPlugin *plugin = NULL; +#ifdef HAVE_PYTHON + if (g_str_has_suffix(name, ".py")) { + plugin = python_plugin_create(name); + } +#endif +#ifdef HAVE_C + if (g_str_has_suffix(name, ".so")) { + plugin = c_plugin_create(name); + } +#endif + if (plugin) { + plugins = g_slist_append(plugins, plugin); + plugin->init_func(plugin, PACKAGE_VERSION, PACKAGE_STATUS); + log_info("Loaded plugin: %s", name); + return TRUE; + } else { + log_info("Failed to load plugin: %s", name); + return FALSE; + } +} + GSList * plugins_get_list(void) { return plugins; } -char * -plugins_get_lang_string(ProfPlugin *plugin) +static gchar* +_get_plugins_dir(void) { - switch (plugin->lang) - { - case LANG_PYTHON: - return "Python"; - case LANG_C: - return "C"; - default: - return "Unknown"; + gchar *xdg_data = xdg_get_data_home(); + GString *plugins_dir = g_string_new(xdg_data); + g_free(xdg_data); + g_string_append(plugins_dir, "/profanity/plugins"); + return g_string_free(plugins_dir, FALSE); +} + +void +_plugins_list_dir(const gchar *const dir, GSList **result) +{ + GDir *plugins = g_dir_open(dir, 0, NULL); + if (plugins == NULL) { + return; } + + const gchar *plugin = g_dir_read_name(plugins); + while (plugin) { + if (g_str_has_suffix(plugin, ".so") || g_str_has_suffix(plugin, ".py")) { + *result = g_slist_append(*result, strdup(plugin)); + } + plugin = g_dir_read_name(plugins); + } + g_dir_close(plugins); +} + +GSList* +plugins_file_list(void) +{ + GSList *result = NULL; + char *plugins_dir = _get_plugins_dir(); + _plugins_list_dir(plugins_dir, &result); + free(plugins_dir); + + return result; } char * diff --git a/src/plugins/plugins.h b/src/plugins/plugins.h index 335bf8f5..85792674 100644 --- a/src/plugins/plugins.h +++ b/src/plugins/plugins.h @@ -99,11 +99,13 @@ typedef struct prof_plugin_t { void plugins_init(void); GSList* plugins_get_list(void); -char* plugins_get_lang_string(ProfPlugin *plugin); +GSList *plugins_file_list(void); char* plugins_autocomplete(const char *const input); void plugins_reset_autocomplete(void); void plugins_shutdown(void); +gboolean plugins_load(const char *const name); + void plugins_on_start(void); void plugins_on_shutdown(void); diff --git a/src/plugins/python_plugins.c b/src/plugins/python_plugins.c index 90ff36ed..a73cc15b 100644 --- a/src/plugins/python_plugins.c +++ b/src/plugins/python_plugins.c @@ -95,7 +95,7 @@ python_plugin_create(const char *const filename) python_check_error(); if (p_module) { ProfPlugin *plugin = malloc(sizeof(ProfPlugin)); - plugin->name = strdup(module_name); + plugin->name = strdup(filename); plugin->lang = LANG_PYTHON; plugin->module = p_module; plugin->init_func = python_init_hook;