diff options
Diffstat (limited to 'src/remmina_plugin_manager.c')
-rw-r--r-- | src/remmina_plugin_manager.c | 1555 |
1 files changed, 1555 insertions, 0 deletions
diff --git a/src/remmina_plugin_manager.c b/src/remmina_plugin_manager.c new file mode 100644 index 0000000..60c0bee --- /dev/null +++ b/src/remmina_plugin_manager.c @@ -0,0 +1,1555 @@ +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2010-2011 Vic Lee + * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo + * Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo + * Copyright (C) 2023-2024 Hiroyuki Tanaka, Sunil Bhat + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. * If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. * If you + * do not wish to do so, delete this exception statement from your + * version. * If you delete this exception statement from all source + * files in the program, then also delete it here. + * + */ + +#include "config.h" + + +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include <gmodule.h> +#include <json-glib/json-glib.h> +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#elif defined(GDK_WINDOWING_WAYLAND) +#include <gdk/gdkwayland.h> +#endif +#include "remmina_public.h" +#include "remmina_main.h" +#include "remmina_file_manager.h" +#include "remmina_pref.h" +#include "remmina_info.h" +#include "remmina_protocol_widget.h" +#include "remmina_log.h" +#include "remmina_widget_pool.h" +#include "rcw.h" +#include "remmina_plugin_manager.h" +#include "remmina_plugin_native.h" +#include "remmina_public.h" +#include "remmina_masterthread_exec.h" +#include "remmina/remmina_trace_calls.h" +#include "remmina_curl_connector.h" +#include "remmina_utils.h" +#include "remmina_unlock.h" + +static GPtrArray* remmina_plugin_table = NULL; + +static GPtrArray* remmina_available_plugin_table = NULL; + +static GtkDialog* remmina_plugin_window = NULL; + +static SignalData* remmina_plugin_signal_data = NULL; + +/* A GHashTable of GHashTables where to store the names of the encrypted settings */ +static GHashTable *encrypted_settings_cache = NULL; + +/* There can be only one secret plugin loaded */ +static RemminaSecretPlugin *remmina_secret_plugin = NULL; + + + +// TRANSLATORS: "Language Wrapper" is a wrapper for plugins written in other programmin languages (Python in this context) +static const gchar *remmina_plugin_type_name[] = +{ N_("Protocol"), N_("Entry"), N_("File"), N_("Tool"), N_("Preference"), N_("Secret"), N_("Language Wrapper"), NULL }; + +static gint remmina_plugin_manager_compare_func(RemminaPlugin **a, RemminaPlugin **b) +{ + TRACE_CALL(__func__); + return g_strcmp0((*a)->name, (*b)->name); +} + +static void htdestroy(gpointer ht) +{ + g_hash_table_unref(ht); +} + +static void init_settings_cache(RemminaPlugin *plugin) +{ + TRACE_CALL(__func__); + RemminaProtocolPlugin *protocol_plugin; + const RemminaProtocolSetting* setting_iter; + GHashTable *pht; + + /* This code make a encrypted setting cache only for protocol plugins */ + + if (plugin->type != REMMINA_PLUGIN_TYPE_PROTOCOL) { + return; + } + + if (encrypted_settings_cache == NULL) + encrypted_settings_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, htdestroy); + + if (!(pht = g_hash_table_lookup(encrypted_settings_cache, plugin->name))) { + pht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + g_hash_table_insert(encrypted_settings_cache, g_strdup(plugin->name), pht); + } + + /* Some settings are encrypted "by name" */ + /* g_hash_table_insert(pht, g_strdup("proxy_password"), (gpointer)TRUE); */ + + g_hash_table_insert(pht, g_strdup("ssh_tunnel_password"), (gpointer)TRUE); + g_hash_table_insert(pht, g_strdup("ssh_tunnel_passphrase"), (gpointer)TRUE); + + /* ssh_password is no longer used starting from remmina 1.4. + * But we MUST mark it as encrypted setting, or the migration procedure will fail */ + g_hash_table_insert(pht, g_strdup("ssh_password"), (gpointer)TRUE); + + /* Other settings are encrypted because of their type */ + protocol_plugin = (RemminaProtocolPlugin *)plugin; + setting_iter = protocol_plugin->basic_settings; + if (setting_iter) { + while (setting_iter->type != REMMINA_PROTOCOL_SETTING_TYPE_END) { + if (setting_iter->type == REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD) + g_hash_table_insert(pht, g_strdup(remmina_plugin_manager_get_canonical_setting_name(setting_iter)), (gpointer)TRUE); + setting_iter++; + } + } + setting_iter = protocol_plugin->advanced_settings; + if (setting_iter) { + while (setting_iter->type != REMMINA_PROTOCOL_SETTING_TYPE_END) { + if (setting_iter->type == REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD) + g_hash_table_insert(pht, g_strdup(remmina_plugin_manager_get_canonical_setting_name(setting_iter)), (gpointer)TRUE); + setting_iter++; + } + } + +} + +static gboolean remmina_plugin_manager_register_plugin(RemminaPlugin *plugin) +{ + TRACE_CALL(__func__); + if (plugin->type == REMMINA_PLUGIN_TYPE_SECRET) { + REMMINA_DEBUG("Remmina plugin %s (type=%s) has been registered, but is not yet initialized/activated. " + "The initialization order is %d.", plugin->name, + _(remmina_plugin_type_name[plugin->type]), + ((RemminaSecretPlugin*)plugin)->init_order); + } + init_settings_cache(plugin); + + g_ptr_array_add(remmina_plugin_table, plugin); + g_ptr_array_sort(remmina_plugin_table, (GCompareFunc)remmina_plugin_manager_compare_func); + return TRUE; +} + +gboolean remmina_gtksocket_available() +{ + GdkDisplayManager* dm; + GdkDisplay* d; + gboolean available; + + dm = gdk_display_manager_get(); + d = gdk_display_manager_get_default_display(dm); + available = FALSE; + +#ifdef GDK_WINDOWING_X11 + if (GDK_IS_X11_DISPLAY(d)) { + /* GtkSocket support is available only under X.Org */ + available = TRUE; + } +#endif + return available; +} + +RemminaPluginService remmina_plugin_manager_service = +{ + remmina_plugin_manager_register_plugin, + remmina_protocol_widget_get_width, + remmina_protocol_widget_set_width, + remmina_protocol_widget_get_height, + remmina_protocol_widget_set_height, + remmina_protocol_widget_get_current_scale_mode, + remmina_protocol_widget_get_expand, + remmina_protocol_widget_set_expand, + remmina_protocol_widget_has_error, + remmina_protocol_widget_set_error, + remmina_protocol_widget_is_closed, + remmina_protocol_widget_get_file, + remmina_protocol_widget_emit_signal, + remmina_protocol_widget_register_hostkey, + remmina_protocol_widget_start_direct_tunnel, + remmina_protocol_widget_start_reverse_tunnel, + remmina_protocol_widget_start_xport_tunnel, + remmina_protocol_widget_set_display, + remmina_protocol_widget_signal_connection_closed, + remmina_protocol_widget_signal_connection_opened, + remmina_protocol_widget_update_align, + remmina_protocol_widget_lock_dynres, + remmina_protocol_widget_unlock_dynres, + remmina_protocol_widget_desktop_resize, + remmina_protocol_widget_panel_auth, + remmina_protocol_widget_panel_new_certificate, + remmina_protocol_widget_panel_changed_certificate, + remmina_protocol_widget_get_username, + remmina_protocol_widget_get_password, + remmina_protocol_widget_get_domain, + remmina_protocol_widget_get_savepassword, + remmina_protocol_widget_panel_authx509, + remmina_protocol_widget_get_cacert, + remmina_protocol_widget_get_cacrl, + remmina_protocol_widget_get_clientcert, + remmina_protocol_widget_get_clientkey, + remmina_protocol_widget_save_cred, + remmina_protocol_widget_panel_show_listen, + remmina_protocol_widget_panel_show_retry, + remmina_protocol_widget_panel_show, + remmina_protocol_widget_panel_hide, + remmina_protocol_widget_ssh_exec, + remmina_protocol_widget_chat_open, + remmina_protocol_widget_chat_close, + remmina_protocol_widget_chat_receive, + remmina_protocol_widget_send_keys_signals, + + remmina_file_get_datadir, + + remmina_file_new, + remmina_file_get_filename, + remmina_file_set_string, + remmina_file_get_string, + remmina_file_get_secret, + remmina_file_set_int, + remmina_file_get_int, + remmina_file_get_double, + remmina_file_unsave_passwords, + + remmina_pref_set_value, + remmina_pref_get_value, + remmina_pref_get_scale_quality, + remmina_pref_get_sshtunnel_port, + remmina_pref_get_ssh_loglevel, + remmina_pref_get_ssh_parseconfig, + remmina_pref_keymap_get_table, + remmina_pref_keymap_get_keyval, + + _remmina_info, + _remmina_message, + _remmina_debug, + _remmina_warning, + _remmina_audit, + _remmina_error, + _remmina_critical, + remmina_log_print, + remmina_log_printf, + + remmina_widget_pool_register, + + rcw_open_from_file_full, + remmina_public_open_unix_sock, + remmina_public_get_server_port, + remmina_masterthread_exec_is_main_thread, + remmina_gtksocket_available, + remmina_protocol_widget_get_profile_remote_width, + remmina_protocol_widget_get_profile_remote_height, + remmina_protocol_widget_get_name, + remmina_protocol_widget_get_width, + remmina_protocol_widget_get_height, + remmina_protocol_widget_set_width, + remmina_protocol_widget_set_height, + remmina_protocol_widget_get_current_scale_mode, + remmina_protocol_widget_get_expand, + remmina_protocol_widget_set_expand, + remmina_protocol_widget_set_error, + remmina_protocol_widget_has_error, + remmina_protocol_widget_gtkviewport, + remmina_protocol_widget_is_closed, + remmina_protocol_widget_get_file, + remmina_protocol_widget_panel_auth, + remmina_protocol_widget_register_hostkey, + remmina_protocol_widget_start_direct_tunnel, + remmina_protocol_widget_start_reverse_tunnel, + remmina_protocol_widget_send_keys_signals, + remmina_protocol_widget_chat_receive, + remmina_protocol_widget_panel_hide, + remmina_protocol_widget_chat_open, + remmina_protocol_widget_ssh_exec, + remmina_protocol_widget_panel_show, + remmina_protocol_widget_panel_show_retry, + remmina_protocol_widget_start_xport_tunnel, + remmina_protocol_widget_set_display, + remmina_protocol_widget_signal_connection_closed, + remmina_protocol_widget_signal_connection_opened, + remmina_protocol_widget_update_align, + remmina_protocol_widget_unlock_dynres, + remmina_protocol_widget_desktop_resize, + remmina_protocol_widget_panel_new_certificate, + remmina_protocol_widget_panel_changed_certificate, + remmina_protocol_widget_get_username, + remmina_protocol_widget_get_password, + remmina_protocol_widget_get_domain, + remmina_protocol_widget_get_savepassword, + remmina_protocol_widget_panel_authx509, + remmina_protocol_widget_get_cacert, + remmina_protocol_widget_get_cacrl, + remmina_protocol_widget_get_clientcert, + remmina_protocol_widget_get_clientkey, + remmina_protocol_widget_save_cred, + remmina_protocol_widget_panel_show_listen, + remmina_widget_pool_register, + rcw_open_from_file_full, + remmina_main_show_dialog, + remmina_main_get_window, + remmina_unlock_new, +}; + +const char *get_filename_ext(const char *filename) { + const char* last = strrchr(filename, '/'); + const char *dot = strrchr(last, '.'); + if(!dot || dot == filename) return ""; + return dot + 1; +} + +static gint compare_secret_plugin_init_order(gconstpointer a, gconstpointer b) +{ + RemminaSecretPlugin *sa, *sb; + + sa = (RemminaSecretPlugin*)a; + sb = (RemminaSecretPlugin*)b; + + if (sa->init_order > sb->init_order) + return 1; + else if (sa->init_order < sb->init_order) + return -1; + return 0; +} + +gchar* remmina_plugin_manager_create_alt_plugin_dir() +{ + gchar *plugin_dir; + plugin_dir = g_build_path("/", g_get_user_config_dir(), "remmina", "plugins", NULL); + + if (g_file_test(plugin_dir, G_FILE_TEST_IS_DIR)) { + // Do nothing, directory already exists + } + else + { + gint result = g_mkdir_with_parents(plugin_dir, 0755); + if (result != 0) + { + plugin_dir = NULL; + } + } + return plugin_dir; +} + + +void remmina_plugin_manager_load_plugins(GPtrArray *plugin_dirs, int array_size) +{ + TRACE_CALL(__func__); + GDir *dir; + const gchar *name, *ptr; + gchar *fullpath; + RemminaPlugin *plugin; + RemminaSecretPlugin *sp; + guint i, j; + GSList *secret_plugins; + GSList *sple; + GPtrArray *alternative_language_plugins; + GPtrArray *loaded_plugins = g_ptr_array_new(); + alternative_language_plugins = g_ptr_array_new(); + char* plugin_dir = NULL; + + for (i = 0; i < array_size; i++){ + plugin_dir = g_ptr_array_remove_index(plugin_dirs, 0); + if (plugin_dir){ + dir = g_dir_open(plugin_dir, 0, NULL); + + if (dir == NULL){ + continue; + } + while ((name = g_dir_read_name(dir)) != NULL) { + if ((ptr = strrchr(name, '.')) == NULL) + continue; + ptr++; + fullpath = g_strconcat(plugin_dir, "/", name, NULL); + if (!remmina_plugin_manager_loader_supported(ptr)) { + g_ptr_array_add(alternative_language_plugins, g_strconcat(plugin_dir, "/", name, NULL)); + g_free(fullpath); + continue; + } + if (!g_ptr_array_find_with_equal_func(loaded_plugins, name, g_str_equal, NULL)){ + if (remmina_plugin_native_load(&remmina_plugin_manager_service, fullpath)){ + g_ptr_array_add(loaded_plugins, g_strdup(name)); + } + } + + g_free(fullpath); + } + g_dir_close(dir); + } + } + + + while (alternative_language_plugins->len > 0) { + gboolean has_loaded = FALSE; + gchar* name = (gchar*)g_ptr_array_remove_index(alternative_language_plugins, 0); + const gchar* ext = get_filename_ext(name); + char* filename = strrchr(name, '/'); + + for (j = 0; j < remmina_plugin_table->len && !has_loaded; j++) { + plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, j); + if (plugin->type == REMMINA_PLUGIN_TYPE_LANGUAGE_WRAPPER) { + RemminaLanguageWrapperPlugin* wrapper_plugin = (RemminaLanguageWrapperPlugin*)plugin; + const gchar** supported_extention = wrapper_plugin->supported_extentions; + while (*supported_extention) { + if (g_str_equal(*supported_extention, ext)) { + if (!g_ptr_array_find_with_equal_func(loaded_plugins, name, g_str_equal, NULL)){ + has_loaded = wrapper_plugin->load(wrapper_plugin, name); + if (has_loaded) { + if (filename){ + g_ptr_array_add(loaded_plugins, filename); + } + break; + } + } + } + supported_extention++; + } + if (has_loaded) break; + } + } + + if (!has_loaded) { + g_print("%s: Skip unsupported file type '%s'\n", name, ext); + } + + g_free(name); + } + + /* Now all secret plugins needs to initialize, following their init_order. + * The 1st plugin which will initialize correctly will be + * the default remmina_secret_plugin */ + + if (remmina_secret_plugin) + g_print("Internal ERROR: remmina_secret_plugin must be null here\n"); + + secret_plugins = NULL; + for (i = 0; i < remmina_plugin_table->len; i++) { + plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, i); + if (plugin->type == REMMINA_PLUGIN_TYPE_SECRET) { + secret_plugins = g_slist_insert_sorted(secret_plugins, (gpointer)plugin, compare_secret_plugin_init_order); + } + } + + sple = secret_plugins; + while(sple != NULL) { + sp = (RemminaSecretPlugin*)sple->data; + if (sp->init(sp)) { + REMMINA_DEBUG("The %s secret plugin has been initialized and it will be your default secret plugin", + sp->name); + remmina_secret_plugin = sp; + break; + } + sple = sple->next; + } + + g_slist_free(secret_plugins); + g_ptr_array_free(alternative_language_plugins, TRUE); + g_ptr_array_free(loaded_plugins, TRUE); +} + +void remmina_plugin_manager_init() +{ + TRACE_CALL(__func__); + + gchar* alternative_dir; + + remmina_plugin_table = g_ptr_array_new(); + remmina_available_plugin_table = g_ptr_array_new(); + GPtrArray *plugin_dirs = g_ptr_array_new(); + int array_size = 1; + + if (!g_module_supported()) { + g_print("Dynamic loading of plugins is not supported on this platform!\n"); + return; + } + alternative_dir = remmina_plugin_manager_create_alt_plugin_dir(); + + if(alternative_dir != NULL){ + g_ptr_array_add(plugin_dirs, alternative_dir); + array_size += 1; + + } + g_ptr_array_add(plugin_dirs, REMMINA_RUNTIME_PLUGINDIR); + remmina_plugin_manager_load_plugins(plugin_dirs, array_size); + + + if (alternative_dir){ + g_free(alternative_dir); + } + + if (plugin_dirs != NULL) { + g_ptr_array_free(plugin_dirs, TRUE); + } +} + +/* + * Creates a JsonNode with all the installed plugin names and version + * + * @returns NULL if there is an error creating the json builder object. Otherwise, return the JsonNode object with the plugin data. + * + */ +JsonNode *remmina_plugin_manager_get_installed_plugins() +{ + TRACE_CALL(__func__); + JsonBuilder *b; + JsonNode *r; + RemminaPlugin *plugin; + guint i; + + b = json_builder_new(); + if(b != NULL) + { + json_builder_begin_object(b); + + for (i = 0; i < remmina_plugin_table->len; i++) { + plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, i); + json_builder_set_member_name(b, plugin->name); + json_builder_add_string_value(b, plugin->version); + } + json_builder_end_object(b); + r = json_builder_get_root(b); + g_object_unref(b); + + return r; + } + else + { + return NULL; + } +} + +gboolean remmina_plugin_manager_is_python_wrapper_installed() +{ + gchar* name = "Python Wrapper"; + RemminaPlugin *plugin; + + for(int i = 0; i < remmina_plugin_table->len; i++) { + plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, i); + if(g_strcmp0(plugin->name, name) == 0) + { + return TRUE; + } + } + return FALSE; +} + +/* + * Creates a JsonNode with some environment data as well as the installed plugins + * + * @returns NULL if there is an error creating the json builder object. Otherwise, return the JsonNode object with the plugin data. + * + */ +JsonNode *remmina_plugin_manager_plugin_stats_get_all() +{ + JsonNode *n; + gchar *unenc_p, *enc_p, *uid; + JsonGenerator *g; + JsonBuilder *b_inner, *b_outer; + + b_outer = json_builder_new(); + if (b_outer == NULL) { + g_object_unref(b_outer); + return NULL; + } + json_builder_begin_object(b_outer); + + b_inner = json_builder_new(); + if(b_inner != NULL) + { + json_builder_begin_object(b_inner); + + //get architecture and python version to determine what plugins are compatible + n = remmina_info_stats_get_os_info(); + json_builder_set_member_name(b_inner, "OS_INFO"); + json_builder_add_value(b_inner, n); + + n = remmina_info_stats_get_python(); + json_builder_set_member_name(b_inner, "PYTHON"); + json_builder_add_value(b_inner, n); + + n = remmina_info_stats_get_uid(); + uid = g_strdup(json_node_get_string(n)); + json_builder_set_member_name(b_outer, "UID"); + json_builder_add_string_value(b_outer, uid); + + n = remmina_plugin_manager_get_installed_plugins(); + json_builder_set_member_name(b_inner, "PLUGINS"); + json_builder_add_value(b_inner, n); + + json_builder_end_object(b_inner); + + n = json_builder_get_root(b_inner); + g_object_unref(b_inner); + + g = json_generator_new(); + json_generator_set_root(g, n); + unenc_p = json_generator_to_data(g, NULL); + + + enc_p = g_base64_encode((guchar *)unenc_p, strlen(unenc_p)); + if (enc_p == NULL) { + g_object_unref(b_outer); + g_object_unref(g); + g_free(uid); + g_free(unenc_p); + return NULL; + } + + + json_builder_set_member_name(b_outer, "encdata"); + json_builder_add_string_value(b_outer, enc_p); + + if(remmina_plugin_manager_is_python_wrapper_installed() == TRUE) { + json_builder_set_member_name(b_outer, "pywrapper_installed"); + json_builder_add_boolean_value(b_outer, TRUE); + } else { + json_builder_set_member_name(b_outer, "pywrapper_installed"); + json_builder_add_boolean_value(b_outer, FALSE); + } + + json_builder_end_object(b_outer); + n = json_builder_get_root(b_outer); + g_object_unref(b_outer); + + g_free(enc_p); + g_object_unref(g); + g_free(unenc_p); + g_free(uid); + + return n; + } + else + { + return NULL; + } +} + +gboolean remmina_plugin_manager_loader_supported(const char *filetype) { + TRACE_CALL(__func__); + return g_str_equal(G_MODULE_SUFFIX, filetype); +} + +RemminaPlugin* remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name) +{ + TRACE_CALL(__func__); + RemminaPlugin *plugin; + guint i; + + for (i = 0; i < remmina_plugin_table->len; i++) { + plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, i); + if (plugin->type == type && g_strcmp0(plugin->name, name) == 0) { + return plugin; + } + } + return NULL; +} + +const gchar *remmina_plugin_manager_get_canonical_setting_name(const RemminaProtocolSetting* setting) +{ + if (setting->name == NULL) { + if (setting->type == REMMINA_PROTOCOL_SETTING_TYPE_SERVER) + return "server"; + if (setting->type == REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD) + return "password"; + if (setting->type == REMMINA_PROTOCOL_SETTING_TYPE_RESOLUTION) + return "resolution"; + if (setting->type == REMMINA_PROTOCOL_SETTING_TYPE_ASSISTANCE) + return "assistance_mode"; + return "missing_setting_name_into_plugin_RemminaProtocolSetting"; + } + return setting->name; +} + +void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data) +{ + TRACE_CALL(__func__); + RemminaPlugin *plugin; + guint i; + + for (i = 0; i < remmina_plugin_table->len; i++) { + plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, i); + if (plugin->type == type) { + func((gchar*)plugin->name, plugin, data); + } + } +} + +/* A copy of remmina_plugin_manager_show and remmina_plugin_manager_show_for_each + * This is because we want to print the list of plugins, and their versions, to the standard output + * with the remmina command line option --full-version instead of using the plugins widget + ** @todo Investigate to use only GListStore and than pass the elements to be shown to 2 separate + * functions + * WARNING: GListStore is supported only from GLib 2.44 */ +static gboolean remmina_plugin_manager_show_for_each_stdout(RemminaPlugin *plugin) +{ + TRACE_CALL(__func__); + + g_print("%-20s%-16s%-64s%-10s\n", plugin->name, + _(remmina_plugin_type_name[plugin->type]), + g_dgettext(plugin->domain, plugin->description), + plugin->version); + return FALSE; +} + +void remmina_plugin_manager_show_stdout() +{ + TRACE_CALL(__func__); + g_print("%-20s%-16s%-64s%-10s\n", "NAME", "TYPE", "DESCRIPTION", "PLUGIN AND LIBRARY VERSION"); + g_ptr_array_foreach(remmina_plugin_table, (GFunc)remmina_plugin_manager_show_for_each_stdout, NULL); +} + +static gboolean remmina_plugin_manager_show_for_each(RemminaPlugin *plugin, GtkListStore *store) +{ + TRACE_CALL(__func__); + GtkTreeIter iter; + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, FALSE, 1, plugin->name, 2, _(remmina_plugin_type_name[plugin->type]), 3, + g_dgettext(plugin->domain, plugin->description), 4, "Installed", 5, plugin->version, -1); + + return FALSE; +} + +static gboolean remmina_plugin_manager_show_for_each_available(RemminaPlugin *plugin, GtkListStore *store) +{ + TRACE_CALL(__func__); + GtkTreeIter iter; + + gtk_list_store_append(store, &iter); + + gtk_list_store_set(store, &iter, 0, FALSE, 1, plugin->name, 2, _(remmina_plugin_type_name[plugin->type]), 3, + g_dgettext(plugin->domain, plugin->description), 4, "Available", 5, plugin->version, -1); + + return FALSE; +} + +void result_dialog_cleanup(GtkDialog * dialog, gint response_id, gpointer user_data) +{ + TRACE_CALL(__func__); + gtk_widget_destroy(GTK_WIDGET(dialog)); + + gtk_widget_hide(remmina_plugin_signal_data->label); + gtk_spinner_stop(GTK_SPINNER(remmina_plugin_signal_data->spinner)); + gtk_widget_set_visible(remmina_plugin_signal_data->spinner, FALSE); +} + +void remmina_plugin_manager_download_result_dialog(GtkDialog * dialog, gchar * message) +{ + TRACE_CALL(__func__); + GtkWidget *child_dialog, *content_area, *label; + + child_dialog = gtk_dialog_new_with_buttons(_("Plugin Download"), GTK_WINDOW(dialog), GTK_DIALOG_MODAL, _("_OK"), 1, NULL); + content_area = gtk_dialog_get_content_area (GTK_DIALOG (child_dialog)); + label = gtk_label_new(message); + remmina_plugin_signal_data->downloading = FALSE; + + gtk_window_set_default_size(GTK_WINDOW(child_dialog), 500, 50); + gtk_container_add(GTK_CONTAINER (content_area), label); + g_signal_connect(G_OBJECT(child_dialog), "response", G_CALLBACK(result_dialog_cleanup), NULL); + gtk_widget_show_all(child_dialog); +} + +void remmina_plugin_manager_toggle_checkbox(GtkCellRendererToggle * cell, gchar * path, GtkListStore * model) +{ + TRACE_CALL(__func__); + GtkTreeIter iter; + gboolean active; + + active = gtk_cell_renderer_toggle_get_active (cell); + + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (model), &iter, path); + if (active) { + gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(cell), 0.5, 0.5); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, FALSE, -1); + } + else { + gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(cell), 0.5, 0.5); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, TRUE, -1); + } + +} + +/* + * Appends json objects that have been deserialized from the response string. + * + * @returns void but modifies @data_array by appending plugins to it. It is the caller's responsibility to free @data_array + * and its underlying elements. + */ +void remmina_append_json_objects_from_response_str(JsonReader *reader, GArray* data_array) +{ + TRACE_CALL(__func__); + + int plugin_array_index = 0; + + while (json_reader_read_element(reader, plugin_array_index)) { + if (plugin_array_index >= MAX_PLUGINS_ALLOWED_TO_DOWNLOAD) { + break; + } + /* This is the current plugin we are building from the deserialized response string. */ + RemminaServerPluginResponse *current_plugin = g_malloc(sizeof(RemminaServerPluginResponse)); + if (current_plugin == NULL) { + plugin_array_index += 1; + continue; + } + + json_reader_read_member(reader, "name"); + + current_plugin->name = g_strdup(json_reader_get_string_value(reader)); + json_reader_end_member(reader); + if (g_utf8_strlen(current_plugin->name, -1) > MAX_PLUGIN_NAME_SIZE) { // -1 indicates null terminated str. + g_free(current_plugin); + plugin_array_index += 1; + continue; // continue attempting to deserialize the rest of the server response. + } + + json_reader_read_member(reader, "version"); + current_plugin->version = g_strdup(json_reader_get_string_value(reader)); + json_reader_end_member(reader); + + json_reader_read_member(reader, "filename"); + current_plugin->file_name = g_strdup(json_reader_get_string_value(reader)); + json_reader_end_member(reader); + + json_reader_read_member(reader, "data"); + const gchar* data = json_reader_get_string_value(reader); + + + /* Because the data is in the form \'\bdata\' and we only want data, we must remove three characters + and then add one more character at the end for NULL, which is (1 + total_length - 3). */ + current_plugin->data = g_malloc(1 + g_utf8_strlen(data, -1) - 3); + if (current_plugin->data == NULL) { + g_free(current_plugin); + plugin_array_index += 1; + continue; + } + + /* remove \'\b and \' */ + g_strlcpy((char*)current_plugin->data, data + 2, g_utf8_strlen(data, -1) - 2); + json_reader_end_member(reader); + + json_reader_read_member(reader, "signature"); + if (json_reader_get_string_value(reader) != NULL) + { + current_plugin->signature = (guchar*) g_strdup(json_reader_get_string_value(reader)); + } + else + { + g_free(current_plugin); + plugin_array_index += 1; + continue; // continue attempting to deserialize the rest of the server response. + } + json_reader_end_member(reader); + + g_array_append_val(data_array, current_plugin); + plugin_array_index += 1; + + json_reader_end_element(reader); // end inspection of this plugin element + } + +} + +GFile* remmina_create_plugin_file(const gchar* plugin_name, const gchar* plugin_version) +{ + gchar file_name[MAX_PLUGIN_NAME_SIZE]; + gchar *plugin_dir; + + if (g_utf8_strlen(plugin_name, -1) > MAX_PLUGIN_NAME_SIZE) { + return NULL; + } + + + if (plugin_version != NULL){ + plugin_dir = g_build_path("/", "/tmp", NULL); + snprintf(file_name, MAX_PLUGIN_NAME_SIZE, "%s/%s_%s", plugin_dir, plugin_name, plugin_version); + } + else{ + plugin_dir = g_build_path("/", g_get_user_config_dir(), "remmina", "plugins", NULL); + snprintf(file_name, MAX_PLUGIN_NAME_SIZE, "%s/%s", plugin_dir, plugin_name); + } + GFile* plugin_file = g_file_new_for_path(file_name); + + if (plugin_dir != NULL) { + g_free(plugin_dir); + } + + return plugin_file; +} + +gchar* remmina_plugin_manager_get_selected_plugin_list_str(GArray *selected_plugins) +{ + gchar *plugin_list = NULL; + + for(guint i = 0; i < selected_plugins->len; i++) + { + gchar *plugin = g_array_index(selected_plugins, gchar*, i); + + if (i == 0) + { + plugin_list = g_strdup(plugin); + } + else + { + plugin_list = g_strconcat(plugin_list, ", ", plugin, NULL); + } + + } + return plugin_list; +} + +gboolean remmina_attempt_to_write_plugin_data_to_disk(RemminaServerPluginResponse *plugin) +{ + gsize decoded_len = 0; + guchar *decoded = g_base64_decode((gchar*) plugin->data, &decoded_len); + + if (decoded == NULL) { + return FALSE; + } + + gsize signature_len = 0; + guchar * decoded_signature = g_base64_decode((gchar*) plugin->signature, &signature_len); + if (decoded_signature == NULL) { + g_free(decoded); + return FALSE; + } + + /* Create path write temporary plugin */ + GFile* plugin_file = remmina_create_plugin_file(plugin->file_name, plugin->version); + if (plugin_file == NULL) { + g_free(decoded); + g_free(decoded_signature); + return FALSE; + } + + /* Convert the base64 into inflated memory */ + gsize bytes_converted = remmina_decompress_from_memory_to_file(decoded, (int) decoded_len, plugin_file); + + /* Don't attempt to write file if we can't inflate anything. */ + if (bytes_converted <= 0) { + g_free(decoded); + g_free(decoded_signature); + g_file_delete(plugin_file, NULL, NULL); + g_object_unref(plugin_file); + return FALSE; + } + /* Verify the signature given to us by the server*/ + gboolean passed = remmina_verify_plugin_signature(decoded_signature, plugin_file, bytes_converted, signature_len); + + if (!passed) { + g_free(decoded); + g_free(decoded_signature); + g_file_delete(plugin_file, NULL, NULL); + g_object_unref(plugin_file); + return FALSE; + } + + /* Create path to store plugin */ + GFile* plugin_file_final = remmina_create_plugin_file(plugin->file_name, NULL); + if (plugin_file == NULL) { + g_free(decoded); + g_free(decoded_signature); + g_file_delete(plugin_file, NULL, NULL); + g_object_unref(plugin_file); + return FALSE; + } + + GError *error = NULL; // Error for testing + GFileOutputStream *g_output_stream = g_file_replace(plugin_file_final, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error); + if (error !=NULL) { + g_free(decoded); + g_free(decoded_signature); + g_file_delete(plugin_file, NULL, NULL); + g_object_unref(plugin_file); + g_file_delete(plugin_file_final, NULL, NULL); + g_object_unref(plugin_file_final); + return FALSE; + } + + GFileIOStream *tmp_file_stream = (GFileIOStream*)g_file_read(plugin_file, NULL, NULL); + g_output_stream_splice((GOutputStream*)g_output_stream, (GInputStream*)tmp_file_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, NULL, NULL); + GFileInfo* info = g_file_query_info(plugin_file_final, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL); + gsize bytes_written = g_file_info_get_size(info); + + /* Delete temporary file */ + g_file_delete(plugin_file, NULL, NULL); + + if (bytes_written != bytes_converted) { + g_object_unref(info); + g_free(decoded); + g_free(decoded_signature); + g_file_delete(plugin_file_final, NULL, NULL); + g_object_unref(plugin_file_final); + return FALSE; + } + + if (info != NULL) { + g_object_unref(info); + } + g_free(decoded); + g_free(decoded_signature); + g_object_unref(g_output_stream); + g_object_unref(plugin_file); + return TRUE; +} + + +gboolean remmina_plugin_manager_parse_plugin_list(gpointer user_data) +{ + int index = 0; + JsonReader* reader = (JsonReader*)user_data; + if (!json_reader_read_element(reader, 0)) { + return FALSE; + } + if (json_reader_read_member(reader, "LIST")){ + while (json_reader_read_element(reader, index)) { + + RemminaPlugin *available_plugin = g_malloc (sizeof(RemminaPlugin)); + + json_reader_read_member(reader, "name"); + + available_plugin->name = g_strdup(json_reader_get_string_value(reader)); + + json_reader_end_member(reader); + json_reader_read_member(reader, "description"); + + available_plugin->description = g_strdup(json_reader_get_string_value(reader)); + + json_reader_end_member(reader); + json_reader_read_member(reader, "version"); + + available_plugin->version = g_strdup(json_reader_get_string_value(reader)); + + json_reader_end_member(reader); + json_reader_read_member(reader, "type"); + + const char *type = json_reader_get_string_value(reader); + if (g_strcmp0(type, "protocol") == 0) + { + available_plugin->type=REMMINA_PLUGIN_TYPE_PROTOCOL; + } + else if(g_strcmp0(type, "entry") == 0) + { + available_plugin->type=REMMINA_PLUGIN_TYPE_ENTRY; + } + else if(g_strcmp0(type, "tool") == 0) + { + available_plugin->type=REMMINA_PLUGIN_TYPE_TOOL; + } + else if(g_strcmp0(type,"pref") == 0) + { + available_plugin->type=REMMINA_PLUGIN_TYPE_PREF; + } + else if(g_strcmp0(type, "secret") == 0) + { + available_plugin->type=REMMINA_PLUGIN_TYPE_SECRET; + } + else if(g_strcmp0(type, "language_wrapper") == 0) + { + available_plugin->type=REMMINA_PLUGIN_TYPE_LANGUAGE_WRAPPER; + } + else if(g_strcmp0(type, "file") == 0) + { + available_plugin->type=REMMINA_PLUGIN_TYPE_FILE; + } + else + { + available_plugin->type=REMMINA_PLUGIN_TYPE_PREF; + } + + json_reader_end_member(reader); + json_reader_read_member(reader, "domain"); + + available_plugin->domain = g_strdup(json_reader_get_string_value(reader)); + + json_reader_end_member(reader); + + if(remmina_plugin_manager_verify_duplicate_plugins(available_plugin) == FALSE) + { + g_ptr_array_add(remmina_available_plugin_table, available_plugin); + } + + json_reader_end_element(reader); + + index = index + 1; + + } + } + json_reader_end_member(reader); + json_reader_end_element(reader); + g_object_unref(reader); + return FALSE; +} + + + +gboolean remmina_plugin_manager_download_plugins(gpointer user_data) +{ + int success_count = 0; + guint i; + JsonReader* reader = (JsonReader*)user_data; + if (!json_reader_read_element(reader, 0)) { + return FALSE; + } + + GArray* data_array = g_array_new(FALSE, FALSE, sizeof(RemminaServerPluginResponse *)); + if (json_reader_read_member(reader, "PLUGINS")){ + remmina_append_json_objects_from_response_str(reader, data_array); + + for (i = 0; i < data_array->len; i+=1) { + RemminaServerPluginResponse *plugin = g_array_index(data_array, RemminaServerPluginResponse *, i); + if (remmina_attempt_to_write_plugin_data_to_disk(plugin)) { + success_count += 1; + } + } + + if (remmina_plugin_window != NULL && remmina_plugin_signal_data != NULL && remmina_plugin_signal_data->downloading == TRUE){ + if(success_count == data_array->len) + { + remmina_plugin_manager_download_result_dialog(remmina_plugin_window, "Plugin download successful! Please reboot Remmina in order to apply changes.\n"); + } + else if((success_count < data_array->len) && (success_count != 0)) + { + remmina_plugin_manager_download_result_dialog(remmina_plugin_window, "Plugin download partially successful! Please reboot Remmina in order to apply changes.\n"); + } + else + { + remmina_plugin_manager_download_result_dialog(remmina_plugin_window, "Plugin download failed.\n"); + } + + } + + } + + g_array_free(data_array, TRUE); //Passing in free_segment TRUE frees the underlying elements as well as the array. + json_reader_end_member(reader); + json_reader_end_element(reader); + g_object_unref(reader); + + return FALSE; +} + + +/* + * Creates request to download selected plugins from the server + * + */ +void remmina_plugin_manager_plugin_request(GArray *selected_plugins) +{ + TRACE_CALL(__func__); + gchar *formdata, *encdata, *plugin_list, *uid; + JsonGenerator *g; + JsonObject *o; + JsonBuilder *b; + + JsonNode *n = remmina_plugin_manager_plugin_stats_get_all(); + + if (n == NULL || (o = json_node_get_object(n)) == NULL) + { + formdata = "{\"UID\":\"\"}"; + } + else + { + b = json_builder_new(); + + if(b != NULL) + { + json_builder_begin_object(b); + + uid = g_strdup(json_object_get_string_member(o, "UID")); + + json_builder_set_member_name(b, "UID"); + json_builder_add_string_value(b, uid); + + encdata = g_strdup(json_object_get_string_member(o, "encdata")); + + json_builder_set_member_name(b, "encdata"); + json_builder_add_string_value(b, encdata); + + plugin_list = remmina_plugin_manager_get_selected_plugin_list_str(selected_plugins); + + json_builder_set_member_name(b, "selected_plugins"); + json_builder_add_string_value(b, plugin_list); + + json_builder_set_member_name(b, "keyversion"); + json_builder_add_int_value(b, 1); + + json_builder_end_object(b); + n = json_builder_get_root(b); + g_object_unref(b); + + g = json_generator_new(); + json_generator_set_root(g, n); + formdata = json_generator_to_data(g, NULL); + + g_object_unref(g); + + if(plugin_list != NULL) + { + g_free(plugin_list); + } + + json_node_free(n); + g_free(encdata); + g_free(uid); + } + else + { + json_node_free(n); + formdata = "{\"UID\":\"\"}"; + } + } + remmina_curl_compose_message(formdata, "POST", DOWNLOAD_URL, NULL); + + +} + + +gboolean remmina_plugin_manager_verify_duplicate_plugins(RemminaPlugin *available_plugin) +{ + RemminaPlugin *plugin; + gsize i; + for (i = 0; i < remmina_plugin_table->len; i++) { + plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, i); + if ((g_strcmp0(plugin->name, available_plugin->name) == 0) && (g_strcmp0(plugin->version, available_plugin->version) == 0)) + { + return TRUE; + } + } + for (i = 0; i < remmina_available_plugin_table->len; i++) { + plugin = (RemminaPlugin*)g_ptr_array_index(remmina_available_plugin_table, i); + if ((g_strcmp0(plugin->name, available_plugin->name) == 0) && (g_strcmp0(plugin->version, available_plugin->version) == 0)) + { + return TRUE; + } + } + return FALSE; +} + + +void* remmina_plugin_manager_get_available_plugins() +{ + TRACE_CALL(__func__); + + gchar *formdata; + JsonGenerator *g; + + JsonNode *n = remmina_plugin_manager_plugin_stats_get_all(); + + if (n == NULL) + { + formdata = "{}"; + } + else + { + g = json_generator_new(); + json_generator_set_root(g, n); + formdata = json_generator_to_data(g, NULL); + g_object_unref(g); + json_node_free(n); + } + remmina_curl_compose_message(formdata, "POST", LIST_URL, NULL); + return NULL; +} + +void remmina_plugin_manager_on_response(GtkDialog * dialog, gint response_id, gpointer user_data) +{ + TRACE_CALL(__func__); + SignalData *data = remmina_plugin_signal_data; + + GtkListStore *store = data->store; + GtkWidget *label = data->label; + GtkWidget *spinner = data->spinner; + + GtkTreeIter iter; + gboolean found = TRUE; + + if (response_id == ON_DOWNLOAD){ + found = FALSE; + data->downloading = TRUE; + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) { + GArray* name_array = g_array_new(FALSE, FALSE, sizeof(gchar*)); + do { + gboolean value; + gchar *available, *name; + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &value, -1); + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 1, &name, -1); + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 4, &available, -1); + + if(value){ + if(g_strcmp0(available, "Installed") == 0){ + REMMINA_DEBUG("%s is already installed", name); + } + else{ + REMMINA_DEBUG("%s can be installed!", name); + found = TRUE; + gchar *c_name = g_strdup(name); + + g_array_append_val(name_array, c_name); + + gtk_widget_set_visible(spinner, TRUE); + if (gtk_widget_get_visible(label)) { + continue; + } else { + gtk_widget_show(label); + gtk_spinner_start(GTK_SPINNER(spinner)); + } + } + } + g_free(available); + g_free(name); + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)); + if (name_array->len > 0) { + remmina_plugin_manager_plugin_request(name_array); + g_array_free(name_array, TRUE); // free the array and element data + + } + } + } + else{ + gtk_widget_destroy(GTK_WIDGET(dialog)); + remmina_plugin_window = NULL; + } + if (found == FALSE){ + remmina_plugin_manager_download_result_dialog(remmina_plugin_window, "No plugins selected for download\n"); + } +} + +void remmina_plugin_manager_show(GtkWindow *parent) +{ + TRACE_CALL(__func__); + GtkWidget *dialog; + GtkWidget *scrolledwindow; + GtkWidget *tree, *available_tree; + GtkWidget *label = gtk_label_new("Downloading..."); + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkListStore *store, *available_store; + SignalData* data = NULL; + + if (remmina_plugin_signal_data == NULL){ + data = malloc(sizeof(SignalData)); + } + else{ + data = remmina_plugin_signal_data; + + } + + if (remmina_plugin_window == NULL){ + dialog = gtk_dialog_new_with_buttons(_("Plugins"), parent, GTK_DIALOG_DESTROY_WITH_PARENT, _("_Download"), 1, _("Close"), 2, NULL); + GtkWidget *spinner = gtk_spinner_new(); + + gtk_window_set_default_size(GTK_WINDOW(dialog), 800, 600); + + scrolledwindow = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(scrolledwindow); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), scrolledwindow, TRUE, TRUE, 0); + + tree = gtk_tree_view_new(); + available_tree = gtk_tree_view_new(); + GtkWidget* tree_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add(GTK_CONTAINER(scrolledwindow), tree_box); + + gtk_box_pack_end(GTK_BOX(tree_box), available_tree, TRUE, TRUE, 0); + GtkLabel *label_available = (GtkLabel*)gtk_label_new("Available"); + GtkLabel *label_installed = (GtkLabel*)gtk_label_new("Installed"); + gtk_widget_set_halign(GTK_WIDGET(label_available), GTK_ALIGN_START); + gtk_widget_set_halign(GTK_WIDGET(label_installed), GTK_ALIGN_START); + gtk_box_pack_end(GTK_BOX(tree_box), GTK_WIDGET(label_available), TRUE, TRUE, 1); + gtk_box_pack_end(GTK_BOX(tree_box), tree, TRUE, TRUE, 10); + gtk_box_pack_end(GTK_BOX(tree_box), GTK_WIDGET(label_installed), TRUE, TRUE, 1); + + gtk_widget_show(tree); + gtk_widget_show(available_tree); + gtk_widget_show(tree_box); + gtk_widget_show(GTK_WIDGET(label_available)); + gtk_widget_show(GTK_WIDGET(label_installed)); + + store = gtk_list_store_new(6, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + available_store = gtk_list_store_new(6, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + + g_ptr_array_foreach(remmina_plugin_table, (GFunc)remmina_plugin_manager_show_for_each, store); + g_ptr_array_foreach(remmina_available_plugin_table, (GFunc)remmina_plugin_manager_show_for_each_available, available_store); + gtk_tree_view_set_model(GTK_TREE_VIEW(tree), GTK_TREE_MODEL(store)); + gtk_tree_view_set_model(GTK_TREE_VIEW(available_tree), GTK_TREE_MODEL(available_store)); + + renderer = gtk_cell_renderer_toggle_new(); + column = gtk_tree_view_column_new_with_attributes(_("Install"), renderer, "active", 0, NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, 0); + gtk_tree_view_append_column(GTK_TREE_VIEW(available_tree), column); + g_signal_connect (renderer, "toggled", G_CALLBACK (remmina_plugin_manager_toggle_checkbox), available_store); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "text", 1, NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, 0); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "text", 1, NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, 0); + gtk_tree_view_append_column(GTK_TREE_VIEW(available_tree), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Type"), renderer, "text", 2, NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, 1); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Type"), renderer, "text", 2, NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, 1); + gtk_tree_view_append_column(GTK_TREE_VIEW(available_tree), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Description"), renderer, "text", 3, NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, 2); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Description"), renderer, "text", 3, NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, 2); + gtk_tree_view_append_column(GTK_TREE_VIEW(available_tree), column); + + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Version"), renderer, "text", 5, NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, 4); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Version"), renderer, "text", 5, NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, 4); + gtk_tree_view_append_column(GTK_TREE_VIEW(available_tree), column); + + data->store = available_store; + data->label = label; + data->spinner = spinner; + data->downloading = FALSE; + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(remmina_plugin_manager_on_response), data); + + gtk_box_pack_end(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), spinner, FALSE, FALSE, 0); + + } + else { + return; + } + remmina_plugin_window = (GtkDialog*)dialog; + remmina_plugin_signal_data = data; + + gtk_widget_show(GTK_WIDGET(dialog)); +} + +RemminaFilePlugin* remmina_plugin_manager_get_import_file_handler(const gchar *file) +{ + TRACE_CALL(__func__); + RemminaFilePlugin *plugin; + gsize i; + + for (i = 0; i < remmina_plugin_table->len; i++) { + plugin = (RemminaFilePlugin*)g_ptr_array_index(remmina_plugin_table, i); + + if (plugin->type != REMMINA_PLUGIN_TYPE_FILE) + continue; + + if (plugin->import_test_func(plugin, file)) { + return plugin; + } + } + return NULL; +} + +RemminaFilePlugin* remmina_plugin_manager_get_export_file_handler(RemminaFile *remminafile) +{ + TRACE_CALL(__func__); + RemminaFilePlugin *plugin; + guint i; + + for (i = 0; i < remmina_plugin_table->len; i++) { + plugin = (RemminaFilePlugin*)g_ptr_array_index(remmina_plugin_table, i); + if (plugin->type != REMMINA_PLUGIN_TYPE_FILE) + continue; + if (plugin->export_test_func(plugin, remminafile)) { + return plugin; + } + } + return NULL; +} + +RemminaSecretPlugin* remmina_plugin_manager_get_secret_plugin(void) +{ + TRACE_CALL(__func__); + return remmina_secret_plugin; +} + +gboolean remmina_plugin_manager_query_feature_by_type(RemminaPluginType ptype, const gchar* name, RemminaProtocolFeatureType ftype) +{ + TRACE_CALL(__func__); + const RemminaProtocolFeature *feature; + RemminaProtocolPlugin* plugin; + + plugin = (RemminaProtocolPlugin*)remmina_plugin_manager_get_plugin(ptype, name); + + if (plugin == NULL) { + return FALSE; + } + + for (feature = plugin->features; feature && feature->type; feature++) { + if (feature->type == ftype) + return TRUE; + } + return FALSE; +} + +gboolean remmina_plugin_manager_is_encrypted_setting(RemminaProtocolPlugin *pp, const char *setting) +{ + TRACE_CALL(__func__); + GHashTable *pht; + + if (encrypted_settings_cache == NULL) + return FALSE; + + if (!(pht = g_hash_table_lookup(encrypted_settings_cache, pp->name))) + return FALSE; + + if (!g_hash_table_lookup(pht, setting)) + return FALSE; + + return TRUE; +} |