/* * Remmina - The GTK+ Remote Desktop Client * Copyright (C) 2009-2011 Vic Lee * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo * Copyright (C) 2016-2020 Antenore Gatta, Giovanni Panozzo * * 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 #include #include #include "config.h" #ifdef HAVE_LIBAVAHI_UI #include #endif #include "remmina_public.h" #include "remmina_pref.h" #include "rcw.h" #include "remmina_string_list.h" #include "remmina_pref_dialog.h" #include "remmina_file.h" #include "remmina_file_manager.h" #include "remmina_ssh.h" #include "remmina_widget_pool.h" #include "remmina_plugin_manager.h" #include "remmina_icon.h" #include "remmina_file_editor.h" #include "remmina/remmina_trace_calls.h" G_DEFINE_TYPE(RemminaFileEditor, remmina_file_editor, GTK_TYPE_DIALOG) #ifdef HAVE_LIBSSH static const gchar *charset_list = "ASCII,BIG5," "CP437,CP720,CP737,CP775,CP850,CP852,CP855," "CP857,CP858,CP862,CP866,CP874,CP1125,CP1250," "CP1251,CP1252,CP1253,CP1254,CP1255,CP1256," "CP1257,CP1258," "EUC-JP,EUC-KR,GBK," "ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4," "ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8," "ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-12," "ISO-8859-13,ISO-8859-14,ISO-8859-15,ISO-8859-16," "KOI8-R,SJIS,UTF-8"; #endif static const gchar *server_tips = N_("" "Supported formats\n" "• server\n" "• server:port\n" "• [server]:port" ""); static const gchar *cmd_tips = N_("" "• command in PATH args %h\n" "• /path/to/foo -options %h %u\n" "• %h is substituted with the server name\n" "• %t is substituted with the SSH server name\n" "• %u is substituted with the username\n" "• %U is substituted with the SSH username\n" "• %p is substituted with Remmina profile name\n" "• %g is substituted with Remmina profile group name\n" "• %d is substituted with local datetime in iso8601 format\n" "Do not run in background if you want the command to be executed before connecting.\n" ""); #ifdef HAVE_LIBSSH static const gchar *server_tips2 = N_("" "Supported formats\n" "• :port\n" "• server\n" "• server:port\n" "• [server]:port\n" "• username@server:port (SSH protocol only)" ""); #endif struct _RemminaFileEditorPriv { RemminaFile * remmina_file; RemminaProtocolPlugin * plugin; const gchar * avahi_service_type; GtkWidget * name_entry; GtkWidget * group_combo; GtkWidget * protocol_combo; GtkWidget * precommand_entry; GtkWidget * postcommand_entry; GtkWidget * save_button; GtkWidget * config_box; GtkWidget * config_scrollable; GtkWidget * config_viewport; GtkWidget * config_container; GtkWidget * server_combo; GtkWidget * resolution_iws_radio; GtkWidget * resolution_auto_radio; GtkWidget * resolution_custom_radio; GtkWidget * resolution_custom_combo; GtkWidget * keymap_combo; GtkWidget * ssh_tunnel_enabled_check; GtkWidget * ssh_tunnel_loopback_check; GtkWidget * ssh_tunnel_server_default_radio; GtkWidget * ssh_tunnel_server_custom_radio; GtkWidget * ssh_tunnel_server_entry; GtkWidget * ssh_tunnel_auth_agent_radio; GtkWidget * ssh_tunnel_auth_password_radio; GtkWidget * ssh_tunnel_auth_password; GtkWidget * ssh_tunnel_passphrase; GtkWidget * ssh_tunnel_auth_publickey_radio; GtkWidget * ssh_tunnel_auth_auto_publickey_radio; GtkWidget * ssh_tunnel_username_entry; GtkWidget * ssh_tunnel_privatekey_chooser; GHashTable * setting_widgets; }; static void remmina_file_editor_class_init(RemminaFileEditorClass *klass) { TRACE_CALL(__func__); } #ifdef HAVE_LIBAVAHI_UI static void remmina_file_editor_browse_avahi(GtkWidget *button, RemminaFileEditor *gfe) { TRACE_CALL(__func__); GtkWidget *dialog; gchar *host; dialog = aui_service_dialog_new(_("Choose a Remote Desktop Server"), GTK_WINDOW(gfe), _("_Cancel"), GTK_RESPONSE_CANCEL, _("_OK"), GTK_RESPONSE_ACCEPT, NULL); gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gfe)); aui_service_dialog_set_resolve_service(AUI_SERVICE_DIALOG(dialog), TRUE); aui_service_dialog_set_resolve_host_name(AUI_SERVICE_DIALOG(dialog), TRUE); aui_service_dialog_set_browse_service_types(AUI_SERVICE_DIALOG(dialog), gfe->priv->avahi_service_type, NULL); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { host = g_strdup_printf("[%s]:%i", aui_service_dialog_get_host_name(AUI_SERVICE_DIALOG(dialog)), aui_service_dialog_get_port(AUI_SERVICE_DIALOG(dialog))); } else { host = NULL; } gtk_widget_destroy(dialog); if (host) { gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(gfe->priv->server_combo))), host); g_free(host); } } #endif static void remmina_file_editor_on_realize(GtkWidget *widget, gpointer user_data) { TRACE_CALL(__func__); RemminaFileEditor *gfe; GtkWidget *defaultwidget; gfe = REMMINA_FILE_EDITOR(widget); defaultwidget = gfe->priv->server_combo; if (defaultwidget) { if (GTK_IS_EDITABLE(defaultwidget)) gtk_editable_select_region(GTK_EDITABLE(defaultwidget), 0, -1); gtk_widget_grab_focus(defaultwidget); } } static void remmina_file_editor_destroy(GtkWidget *widget, gpointer data) { TRACE_CALL(__func__); remmina_file_free(REMMINA_FILE_EDITOR(widget)->priv->remmina_file); g_hash_table_destroy(REMMINA_FILE_EDITOR(widget)->priv->setting_widgets); g_free(REMMINA_FILE_EDITOR(widget)->priv); } static void remmina_file_editor_button_on_toggled(GtkToggleButton *togglebutton, GtkWidget *widget) { TRACE_CALL(__func__); gtk_widget_set_sensitive(widget, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton))); } static void remmina_file_editor_create_notebook_container(RemminaFileEditor *gfe) { TRACE_CALL(__func__); /* Create the notebook */ gfe->priv->config_container = gtk_notebook_new(); gfe->priv->config_viewport = gtk_viewport_new(NULL, NULL); gfe->priv->config_scrollable = gtk_scrolled_window_new(NULL, NULL); gtk_container_set_border_width(GTK_CONTAINER(gfe->priv->config_scrollable), 2); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gfe->priv->config_scrollable), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_widget_show(gfe->priv->config_scrollable); gtk_container_add(GTK_CONTAINER(gfe->priv->config_viewport), gfe->priv->config_container); gtk_container_set_border_width(GTK_CONTAINER(gfe->priv->config_viewport), 2); gtk_widget_show(gfe->priv->config_viewport); gtk_container_add(GTK_CONTAINER(gfe->priv->config_scrollable), gfe->priv->config_viewport); gtk_container_set_border_width(GTK_CONTAINER(gfe->priv->config_container), 2); gtk_widget_show(gfe->priv->config_container); gtk_container_add(GTK_CONTAINER(gfe->priv->config_box), gfe->priv->config_scrollable); } static GtkWidget *remmina_file_editor_create_notebook_tab(RemminaFileEditor *gfe, const gchar *stock_id, const gchar *label, gint rows, gint cols) { TRACE_CALL(__func__); GtkWidget *tablabel; GtkWidget *tabbody; GtkWidget *grid; GtkWidget *widget; tablabel = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_widget_show(tablabel); widget = gtk_image_new_from_icon_name(stock_id, GTK_ICON_SIZE_BUTTON); gtk_box_pack_start(GTK_BOX(tablabel), widget, FALSE, FALSE, 0); gtk_widget_show(widget); widget = gtk_label_new(label); gtk_box_pack_start(GTK_BOX(tablabel), widget, FALSE, FALSE, 0); gtk_widget_show(widget); tabbody = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); gtk_widget_show(tabbody); gtk_notebook_append_page(GTK_NOTEBOOK(gfe->priv->config_container), tabbody, tablabel); grid = gtk_grid_new(); gtk_widget_show(grid); gtk_grid_set_row_spacing(GTK_GRID(grid), 8); gtk_grid_set_column_spacing(GTK_GRID(grid), 8); gtk_container_set_border_width(GTK_CONTAINER(grid), 15); gtk_box_pack_start(GTK_BOX(tabbody), grid, FALSE, FALSE, 0); return grid; } #ifdef HAVE_LIBSSH static void remmina_file_editor_ssh_tunnel_server_custom_radio_on_toggled(GtkToggleButton *togglebutton, RemminaFileEditor *gfe) { TRACE_CALL(__func__); gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_server_entry), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_enabled_check)) && (gfe->priv->ssh_tunnel_server_custom_radio == NULL || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_server_custom_radio))) ); } static void remmina_file_editor_ssh_tunnel_auth_publickey_radio_on_toggled(GtkToggleButton *togglebutton, RemminaFileEditor *gfe) { TRACE_CALL(__func__); gboolean b; const gchar *s; b = ((!gfe->priv->ssh_tunnel_enabled_check || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_enabled_check))) && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_auth_publickey_radio))); gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_privatekey_chooser), b); if (b && (s = remmina_file_get_string(gfe->priv->remmina_file, "ssh_tunnel_privatekey"))) gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(gfe->priv->ssh_tunnel_privatekey_chooser), s); } static void remmina_file_editor_ssh_tunnel_enabled_check_on_toggled(GtkToggleButton *togglebutton, RemminaFileEditor *gfe, RemminaProtocolSSHSetting ssh_setting) { TRACE_CALL(__func__); RemminaFileEditorPriv *priv = gfe->priv; gboolean enabled = TRUE; gchar *p; const gchar *cp; if (gfe->priv->ssh_tunnel_enabled_check) { enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_enabled_check)); if (gfe->priv->ssh_tunnel_loopback_check) gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_loopback_check), enabled); if (gfe->priv->ssh_tunnel_server_default_radio) gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_server_default_radio), enabled); if (gfe->priv->ssh_tunnel_server_custom_radio) gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_server_custom_radio), enabled); remmina_file_editor_ssh_tunnel_server_custom_radio_on_toggled(NULL, gfe); p = remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->protocol_combo)); //if (!(g_strcmp0(p, "SFTP") == 0 || g_strcmp0(p, "SSH") == 0)) { gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_username_entry), enabled); gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_auth_agent_radio), enabled); gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_auth_password_radio), enabled); gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_auth_password), enabled); gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_auth_publickey_radio), enabled); gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_auth_auto_publickey_radio), enabled); //} g_free(p); } remmina_file_editor_ssh_tunnel_auth_publickey_radio_on_toggled(NULL, gfe); if (gfe->priv->ssh_tunnel_username_entry) if (enabled && gtk_entry_get_text(GTK_ENTRY(gfe->priv->ssh_tunnel_username_entry)) [0] == '\0') { cp = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_username"); gtk_entry_set_text(GTK_ENTRY(gfe->priv->ssh_tunnel_username_entry), cp ? cp : ""); } if (gfe->priv->ssh_tunnel_auth_password) { if (enabled && gtk_entry_get_text(GTK_ENTRY(gfe->priv->ssh_tunnel_auth_password)) [0] == '\0') { cp = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_password"); gtk_entry_set_text(GTK_ENTRY(gfe->priv->ssh_tunnel_auth_password), cp ? cp : ""); } } if (gfe->priv->ssh_tunnel_passphrase) { if (enabled && gtk_entry_get_text(GTK_ENTRY(gfe->priv->ssh_tunnel_passphrase)) [0] == '\0') { cp = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_passphrase"); gtk_entry_set_text(GTK_ENTRY(gfe->priv->ssh_tunnel_passphrase), cp ? cp : ""); } } } static void remmina_file_editor_create_ssh_privatekey(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint column) { TRACE_CALL(__func__); gchar *s; GtkWidget *widget; GtkWidget *dialog; const gchar *ssh_privatekey; RemminaFileEditorPriv *priv = gfe->priv; widget = gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON(priv->ssh_tunnel_auth_password_radio), _("SSH identity file")); g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(remmina_file_editor_ssh_tunnel_auth_publickey_radio_on_toggled), gfe); priv->ssh_tunnel_auth_publickey_radio = widget; gtk_widget_show(widget); gtk_grid_attach(GTK_GRID(grid), widget, 0, row + 22, 1, 1); dialog = gtk_file_chooser_dialog_new(_("SSH identity file"), GTK_WINDOW(gfe), GTK_FILE_CHOOSER_ACTION_OPEN, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Open"), GTK_RESPONSE_ACCEPT, NULL); widget = gtk_file_chooser_button_new_with_dialog(dialog); #ifdef SNAP_BUILD s = g_strdup_printf("%s/.ssh", g_getenv("SNAP_USER_COMMON")); #else s = g_strdup_printf("%s/.ssh", g_get_home_dir()); #endif if (g_file_test(s, G_FILE_TEST_IS_DIR)) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(widget), s); g_free(s); gtk_widget_show(widget); gtk_grid_attach(GTK_GRID(grid), widget, column + 1, row + 22, 1, 1); priv->ssh_tunnel_privatekey_chooser = widget; ssh_privatekey = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_privatekey"); if (ssh_privatekey && g_file_test(ssh_privatekey, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(priv->ssh_tunnel_privatekey_chooser), ssh_privatekey); else remmina_file_set_string(priv->remmina_file, "ssh_tunnel_privatekey", NULL); } #endif static void remmina_file_editor_create_server(RemminaFileEditor *gfe, const RemminaProtocolSetting *setting, GtkWidget *grid, gint row) { TRACE_CALL(__func__); RemminaProtocolPlugin *plugin = gfe->priv->plugin; GtkWidget *widget; #ifdef HAVE_LIBAVAHI_UI GtkWidget *hbox; #endif gchar *s; widget = gtk_label_new(_("Server")); gtk_widget_show(widget); gtk_widget_set_valign(widget, GTK_ALIGN_START); gtk_widget_set_halign(widget, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, row + 1); s = remmina_pref_get_recent(plugin->name); widget = remmina_public_create_combo_entry(s, remmina_file_get_string(gfe->priv->remmina_file, "server"), TRUE); gtk_widget_set_hexpand(widget, TRUE); gtk_widget_show(widget); gtk_widget_set_tooltip_markup(widget, _(server_tips)); gtk_entry_set_activates_default(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(widget))), TRUE); gfe->priv->server_combo = widget; g_free(s); #ifdef HAVE_LIBAVAHI_UI if (setting->opt1) { gfe->priv->avahi_service_type = (const gchar *)setting->opt1; hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_widget_show(hbox); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); widget = gtk_button_new_with_label("…"); s = g_strdup_printf(_("Browse the network to find a %s server"), plugin->name); gtk_widget_set_tooltip_text(widget, s); g_free(s); gtk_widget_show(widget); gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_browse_avahi), gfe); gtk_grid_attach(GTK_GRID(grid), hbox, 1, row, 1, 1); } else #endif { gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1); } } static GtkWidget *remmina_file_editor_create_password(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value) { TRACE_CALL(__func__); GtkWidget *widget; widget = gtk_label_new(label); gtk_widget_show(widget); #if GTK_CHECK_VERSION(3, 12, 0) gtk_widget_set_margin_end(widget, 40); #else gtk_widget_set_margin_right(widget, 40); #endif gtk_widget_set_valign(widget, GTK_ALIGN_START); gtk_widget_set_halign(widget, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1); widget = gtk_entry_new(); gtk_widget_show(widget); gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1); gtk_entry_set_max_length(GTK_ENTRY(widget), 100); gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE); gtk_widget_set_hexpand(widget, TRUE); gtk_entry_set_activates_default(GTK_ENTRY(widget), TRUE); if (value) gtk_entry_set_text(GTK_ENTRY(widget), value); return widget; } static void remmina_file_editor_update_resolution(GtkWidget *widget, RemminaFileEditor *gfe) { TRACE_CALL(__func__); gchar *res_str; res_str = g_strdup_printf("%dx%d", remmina_file_get_int(gfe->priv->remmina_file, "resolution_width", 0), remmina_file_get_int(gfe->priv->remmina_file, "resolution_height", 0) ); remmina_public_load_combo_text_d(gfe->priv->resolution_custom_combo, remmina_pref.resolutions, res_str, NULL); g_free(res_str); } static void remmina_file_editor_browse_resolution(GtkWidget *button, RemminaFileEditor *gfe) { TRACE_CALL(__func__); GtkDialog *dialog = remmina_string_list_new(FALSE, NULL); remmina_string_list_set_validation_func(remmina_public_resolution_validation_func); remmina_string_list_set_text(remmina_pref.resolutions, TRUE); remmina_string_list_set_titles(_("Resolutions"), _("Configure the available resolutions")); gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gfe)); gtk_dialog_run(dialog); g_free(remmina_pref.resolutions); remmina_pref.resolutions = remmina_string_list_get_text(); g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(remmina_file_editor_update_resolution), gfe); gtk_widget_destroy(GTK_WIDGET(dialog)); } static void remmina_file_editor_create_resolution(RemminaFileEditor *gfe, const RemminaProtocolSetting *setting, GtkWidget *grid, gint row) { TRACE_CALL(__func__); GtkWidget *widget; GtkWidget *hbox; int resolution_w, resolution_h; gchar *res_str; RemminaProtocolWidgetResolutionMode res_mode; res_mode = remmina_file_get_int(gfe->priv->remmina_file, "resolution_mode", RES_INVALID); resolution_w = remmina_file_get_int(gfe->priv->remmina_file, "resolution_width", -1); resolution_h = remmina_file_get_int(gfe->priv->remmina_file, "resolution_height", -1); /* If resolution_mode is non-existent (-1), then we try to calculate it * as we did before having resolution_mode */ if (res_mode == RES_INVALID) { if (resolution_w <= 0 || resolution_h <= 0) res_mode = RES_USE_INITIAL_WINDOW_SIZE; else res_mode = RES_USE_CUSTOM; } if (res_mode == RES_USE_CUSTOM) res_str = g_strdup_printf("%dx%d", resolution_w, resolution_h); else res_str = NULL; widget = gtk_label_new(_("Resolution")); gtk_widget_show(widget); gtk_widget_set_valign(widget, GTK_ALIGN_START); gtk_widget_set_halign(widget, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); widget = gtk_radio_button_new_with_label(NULL, _("Use initial window size")); gtk_widget_show(widget); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); gfe->priv->resolution_iws_radio = widget; widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(gfe->priv->resolution_iws_radio), _("Use client resolution")); gtk_widget_show(widget); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); gfe->priv->resolution_auto_radio = widget; gtk_grid_attach(GTK_GRID(grid), hbox, 1, row, 1, 1); gtk_widget_show(hbox); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_widget_show(hbox); gtk_grid_attach(GTK_GRID(grid), hbox, 1, row + 1, 1, 1); widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(gfe->priv->resolution_iws_radio), _("Custom")); gtk_widget_show(widget); gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0); gfe->priv->resolution_custom_radio = widget; widget = remmina_public_create_combo_text_d(remmina_pref.resolutions, res_str, NULL); gtk_widget_show(widget); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); gfe->priv->resolution_custom_combo = widget; widget = gtk_button_new_with_label("…"); gtk_widget_show(widget); gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_browse_resolution), gfe); g_signal_connect(G_OBJECT(gfe->priv->resolution_custom_radio), "toggled", G_CALLBACK(remmina_file_editor_button_on_toggled), gfe->priv->resolution_custom_combo); if (res_mode == RES_USE_CUSTOM) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_custom_radio), TRUE); else if (res_mode == RES_USE_CLIENT) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_auto_radio), TRUE); else gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_iws_radio), TRUE); gtk_widget_set_sensitive(gfe->priv->resolution_custom_combo, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_custom_radio))); g_free(res_str); } static GtkWidget *remmina_file_editor_create_text(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value) { TRACE_CALL(__func__); GtkWidget *widget; widget = gtk_label_new(label); gtk_widget_show(widget); #if GTK_CHECK_VERSION(3, 12, 0) gtk_widget_set_margin_end(widget, 40); #else gtk_widget_set_margin_right(widget, 40); #endif gtk_widget_set_valign(widget, GTK_ALIGN_START); gtk_widget_set_halign(widget, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1); widget = gtk_entry_new(); gtk_widget_show(widget); gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1); gtk_entry_set_max_length(GTK_ENTRY(widget), 300); gtk_widget_set_hexpand(widget, TRUE); if (value) gtk_entry_set_text(GTK_ENTRY(widget), value); return widget; } static GtkWidget *remmina_file_editor_create_select(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gpointer *list, const gchar *value) { TRACE_CALL(__func__); GtkWidget *widget; widget = gtk_label_new(label); gtk_widget_show(widget); gtk_widget_set_valign(widget, GTK_ALIGN_START); gtk_widget_set_halign(widget, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1); widget = remmina_public_create_combo_map(list, value, FALSE, gfe->priv->plugin->domain); gtk_widget_show(widget); gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1); return widget; } static GtkWidget *remmina_file_editor_create_combo(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *list, const gchar *value) { TRACE_CALL(__func__); GtkWidget *widget; widget = gtk_label_new(label); gtk_widget_show(widget); gtk_widget_set_valign(widget, GTK_ALIGN_START); gtk_widget_set_halign(widget, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1); widget = remmina_public_create_combo_entry(list, value, FALSE); gtk_widget_show(widget); gtk_widget_set_hexpand(widget, TRUE); gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1); return widget; } static GtkWidget *remmina_file_editor_create_check(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint top, const gchar *label, gboolean value) { TRACE_CALL(__func__); GtkWidget *widget; widget = gtk_check_button_new_with_label(label); gtk_widget_show(widget); gtk_grid_set_row_spacing(GTK_GRID(grid), 1); gtk_grid_attach(GTK_GRID(grid), widget, top, row, 1, 1); if (value) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE); return widget; } static GtkWidget * remmina_file_editor_create_chooser(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gint type) { TRACE_CALL(__func__); GtkWidget *check; GtkWidget *widget; GtkWidget *hbox; widget = gtk_label_new(label); gtk_widget_show(widget); gtk_widget_set_valign(widget, GTK_ALIGN_START); gtk_widget_set_halign(widget, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_widget_show(hbox); gtk_grid_attach(GTK_GRID(grid), hbox, 1, row, 1, 1); check = gtk_check_button_new(); gtk_widget_show(check); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), (value && value[0] == '/')); gtk_box_pack_start(GTK_BOX(hbox), check, FALSE, FALSE, 0); widget = gtk_file_chooser_button_new(label, type); gtk_widget_show(widget); if (value) gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(widget), value); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(remmina_file_editor_button_on_toggled), widget); remmina_file_editor_button_on_toggled(GTK_TOGGLE_BUTTON(check), widget); return widget; } static void remmina_file_editor_create_settings(RemminaFileEditor *gfe, GtkWidget *grid, const RemminaProtocolSetting *settings) { TRACE_CALL(__func__); RemminaFileEditorPriv *priv = gfe->priv; GtkWidget *widget; gint grid_row = 0; gint grid_column = 0; gchar **strarr; gchar *setting_name; while (settings->type != REMMINA_PROTOCOL_SETTING_TYPE_END) { setting_name = (gchar *)(remmina_plugin_manager_get_canonical_setting_name(settings)); switch (settings->type) { case REMMINA_PROTOCOL_SETTING_TYPE_SERVER: remmina_file_editor_create_server(gfe, settings, grid, grid_row); break; case REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD: widget = remmina_file_editor_create_password(gfe, grid, grid_row, 0, g_dgettext(priv->plugin->domain, settings->label), remmina_file_get_string(priv->remmina_file, setting_name)); g_hash_table_insert(priv->setting_widgets, setting_name, widget); grid_row++; break; case REMMINA_PROTOCOL_SETTING_TYPE_RESOLUTION: remmina_file_editor_create_resolution(gfe, settings, grid, grid_row); grid_row += 1; break; case REMMINA_PROTOCOL_SETTING_TYPE_KEYMAP: strarr = remmina_pref_keymap_groups(); priv->keymap_combo = remmina_file_editor_create_select(gfe, grid, grid_row + 1, 0, _("Keyboard mapping"), (const gpointer *)strarr, remmina_file_get_string(priv->remmina_file, "keymap")); g_strfreev(strarr); grid_row++; break; case REMMINA_PROTOCOL_SETTING_TYPE_TEXT: widget = remmina_file_editor_create_text(gfe, grid, grid_row, 0, g_dgettext(priv->plugin->domain, settings->label), remmina_file_get_string(priv->remmina_file, setting_name)); g_hash_table_insert(priv->setting_widgets, setting_name, widget); if (settings->opt2) gtk_widget_set_tooltip_text(widget, (const gchar *)settings->opt2); grid_row++; break; case REMMINA_PROTOCOL_SETTING_TYPE_SELECT: widget = remmina_file_editor_create_select(gfe, grid, grid_row, 0, g_dgettext(priv->plugin->domain, settings->label), (const gpointer *)settings->opt1, remmina_file_get_string(priv->remmina_file, setting_name)); g_hash_table_insert(priv->setting_widgets, setting_name, widget); if (settings->opt2) gtk_widget_set_tooltip_text(widget, (const gchar *)settings->opt2); break; case REMMINA_PROTOCOL_SETTING_TYPE_COMBO: widget = remmina_file_editor_create_combo(gfe, grid, grid_row, 0, g_dgettext(priv->plugin->domain, settings->label), (const gchar *)settings->opt1, remmina_file_get_string(priv->remmina_file, setting_name)); g_hash_table_insert(priv->setting_widgets, setting_name, widget); if (settings->opt2) gtk_widget_set_tooltip_text(widget, (const gchar *)settings->opt2); break; case REMMINA_PROTOCOL_SETTING_TYPE_CHECK: widget = remmina_file_editor_create_check(gfe, grid, grid_row, grid_column, g_dgettext(priv->plugin->domain, settings->label), remmina_file_get_int(priv->remmina_file, setting_name, FALSE)); g_hash_table_insert(priv->setting_widgets, setting_name, widget); if (settings->opt2) gtk_widget_set_tooltip_text(widget, (const gchar *)settings->opt2); break; case REMMINA_PROTOCOL_SETTING_TYPE_FILE: widget = remmina_file_editor_create_chooser(gfe, grid, grid_row, 0, g_dgettext(priv->plugin->domain, settings->label), remmina_file_get_string(priv->remmina_file, setting_name), GTK_FILE_CHOOSER_ACTION_OPEN); g_hash_table_insert(priv->setting_widgets, setting_name, widget); if (settings->opt2) gtk_widget_set_tooltip_text(widget, (const gchar *)settings->opt2); break; case REMMINA_PROTOCOL_SETTING_TYPE_FOLDER: widget = remmina_file_editor_create_chooser(gfe, grid, grid_row, 0, g_dgettext(priv->plugin->domain, settings->label), remmina_file_get_string(priv->remmina_file, setting_name), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); g_hash_table_insert(priv->setting_widgets, setting_name, widget); if (settings->opt2) gtk_widget_set_tooltip_text(widget, (const gchar *)settings->opt2); break; default: break; } /* If the setting wants compactness, move to the next column */ if (settings->compact) grid_column++; /* Add a new settings row and move to the first column * if the setting doesn’t want the compactness * or we already have two columns */ if (!settings->compact || grid_column > 1) { grid_row++; grid_column = 0; } settings++; } } static void remmina_file_editor_create_ssh_tunnel_tab(RemminaFileEditor *gfe, RemminaProtocolSSHSetting ssh_setting) { TRACE_CALL(__func__); #ifdef HAVE_LIBSSH RemminaFileEditorPriv *priv = gfe->priv; GtkWidget *grid; GtkWidget *widget; const gchar *cs; gchar *s; gchar *p; gint row = 0; if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_NONE) return; /* The SSH tab (implementation) */ grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("SSH Tunnel"), 9, 3); widget = gtk_toggle_button_new_with_label(_("Enable SSH tunnel")); gtk_widget_set_halign(widget, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), widget, 0, 0, 1, 1); g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(remmina_file_editor_ssh_tunnel_enabled_check_on_toggled), gfe); priv->ssh_tunnel_enabled_check = widget; widget = gtk_check_button_new_with_label(_("Tunnel via loopback address")); gtk_grid_attach(GTK_GRID(grid), widget, 1, 0, 2, 1); priv->ssh_tunnel_loopback_check = widget; row++; /* SSH Server group */ switch (ssh_setting) { case REMMINA_PROTOCOL_SSH_SETTING_TUNNEL: s = g_strdup_printf(_("Same server at port %i"), DEFAULT_SSH_PORT); widget = gtk_radio_button_new_with_label(NULL, s); g_free(s); gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 3, 1); priv->ssh_tunnel_server_default_radio = widget; row++; widget = gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON(priv->ssh_tunnel_server_default_radio), _("Custom")); gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1); g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(remmina_file_editor_ssh_tunnel_server_custom_radio_on_toggled), gfe); priv->ssh_tunnel_server_custom_radio = widget; widget = gtk_entry_new(); gtk_entry_set_max_length(GTK_ENTRY(widget), 100); gtk_widget_set_tooltip_markup(widget, _(server_tips2)); gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 2, 1); priv->ssh_tunnel_server_entry = widget; row++; break; case REMMINA_PROTOCOL_SSH_SETTING_REVERSE_TUNNEL: priv->ssh_tunnel_server_default_radio = NULL; priv->ssh_tunnel_server_custom_radio = NULL; priv->ssh_tunnel_server_entry = remmina_file_editor_create_text(gfe, grid, 1, 0, _("Server"), NULL); gtk_widget_set_tooltip_markup(priv->ssh_tunnel_server_entry, _(server_tips)); row++; break; case REMMINA_PROTOCOL_SSH_SETTING_SSH: case REMMINA_PROTOCOL_SSH_SETTING_SFTP: priv->ssh_tunnel_server_default_radio = NULL; priv->ssh_tunnel_server_custom_radio = NULL; priv->ssh_tunnel_server_entry = NULL; break; default: break; } p = remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->protocol_combo)); if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_SFTP) { widget = remmina_file_editor_create_text(gfe, grid, row + 8, 1, _("Startup path"), NULL); cs = remmina_file_get_string(priv->remmina_file, "execpath"); gtk_entry_set_text(GTK_ENTRY(widget), cs ? cs : ""); g_hash_table_insert(priv->setting_widgets, "execpath", widget); row++; } /* SSH Authentication frame */ //if (!(g_strcmp0(p, "SFTP") == 0 || g_strcmp0(p, "SSH") == 0)) { remmina_public_create_group(GTK_GRID(grid), _("SSH Authentication"), row + 8, 6, 1); row++; if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_TUNNEL || ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_REVERSE_TUNNEL) { priv->ssh_tunnel_username_entry = remmina_file_editor_create_text(gfe, grid, row + 10, 0, _("Username"), NULL); row++; } widget = gtk_radio_button_new_with_label(NULL, _("SSH agent (automatic)")); gtk_grid_attach(GTK_GRID(grid), widget, 0, row + 19, 1, 1); priv->ssh_tunnel_auth_agent_radio = widget; row++; widget = gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON(priv->ssh_tunnel_auth_agent_radio), _("Password")); gtk_grid_attach(GTK_GRID(grid), widget, 0, row + 21, 1, 1); priv->ssh_tunnel_auth_password_radio = widget; widget = gtk_entry_new(); gtk_grid_attach(GTK_GRID(grid), widget, 1, row + 21, 2, 1); gtk_entry_set_max_length(GTK_ENTRY(widget), 300); gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE); gtk_widget_set_hexpand(widget, TRUE); priv->ssh_tunnel_auth_password = widget; row++; widget = gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON(priv->ssh_tunnel_auth_password_radio), _("Public key (automatic)")); gtk_grid_attach(GTK_GRID(grid), widget, 0, row + 22, 1, 1); priv->ssh_tunnel_auth_auto_publickey_radio = widget; row++; remmina_file_editor_create_ssh_privatekey(gfe, grid, row + 1, 0); //} row++; widget = gtk_label_new(_("Password to unlock private key")); gtk_grid_attach(GTK_GRID(grid), widget, 0, row + 23, 1, 1); priv->ssh_tunnel_auth_password_radio = widget; widget = gtk_entry_new(); gtk_grid_attach(GTK_GRID(grid), widget, 1, row + 23, 2, 1); gtk_entry_set_max_length(GTK_ENTRY(widget), 300); gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE); gtk_widget_set_hexpand(widget, TRUE); priv->ssh_tunnel_passphrase = widget; row++; /* Set the values */ cs = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_server"); if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_TUNNEL) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_enabled_check), remmina_file_get_int(priv->remmina_file, "ssh_tunnel_enabled", FALSE)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_loopback_check), remmina_file_get_int(priv->remmina_file, "ssh_tunnel_loopback", FALSE)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cs ? priv->ssh_tunnel_server_custom_radio : priv->ssh_tunnel_server_default_radio), TRUE); gtk_entry_set_text(GTK_ENTRY(priv->ssh_tunnel_server_entry), cs ? cs : ""); } else if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_REVERSE_TUNNEL) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_enabled_check), remmina_file_get_int(priv->remmina_file, "ssh_tunnel_enabled", FALSE)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_loopback_check), remmina_file_get_int(priv->remmina_file, "ssh_tunnel_loopback", FALSE)); gtk_entry_set_text(GTK_ENTRY(priv->ssh_tunnel_server_entry), cs ? cs : ""); } //if (!(g_strcmp0(p, "SFTP") == 0 || g_strcmp0(p, "SSH") == 0)) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON( remmina_file_get_int(priv->remmina_file, "ssh_tunnel_auth", 0) == SSH_AUTH_PUBLICKEY ? priv->ssh_tunnel_auth_publickey_radio : remmina_file_get_int(priv->remmina_file, "ssh_tunnel_auth", 0) == SSH_AUTH_AUTO_PUBLICKEY ? priv->ssh_tunnel_auth_auto_publickey_radio : remmina_file_get_int(priv->remmina_file, "ssh_tunnel_auth", 0) == SSH_AUTH_AGENT ? priv->ssh_tunnel_auth_agent_radio : priv->ssh_tunnel_auth_password_radio), TRUE); remmina_file_editor_ssh_tunnel_enabled_check_on_toggled(NULL, gfe, ssh_setting); //} gtk_widget_show_all(grid); g_free(p); #endif } static void remmina_file_editor_create_all_settings(RemminaFileEditor *gfe) { TRACE_CALL(__func__); RemminaFileEditorPriv *priv = gfe->priv; GtkWidget *grid; static const RemminaProtocolSetting autostart_settings[] = { { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "enable-autostart", N_("Turn on \"Autostart\" for this profile"), FALSE, NULL, NULL }, { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL } }; remmina_file_editor_create_notebook_container(gfe); /* The Basic tab */ if (priv->plugin->basic_settings) { grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Basic"), 20, 2); remmina_file_editor_create_settings(gfe, grid, priv->plugin->basic_settings); } /* The Advanced tab */ if (priv->plugin->advanced_settings) { grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Advanced"), 20, 2); remmina_file_editor_create_settings(gfe, grid, priv->plugin->advanced_settings); } /* Autostart tab */ grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Autostart"), 20, 2); remmina_file_editor_create_settings(gfe, grid, autostart_settings); /* The SSH tab */ remmina_file_editor_create_ssh_tunnel_tab(gfe, priv->plugin->ssh_setting); } static void remmina_file_editor_protocol_combo_on_changed(GtkComboBox *combo, RemminaFileEditor *gfe) { TRACE_CALL(__func__); RemminaFileEditorPriv *priv = gfe->priv; gchar *protocol; if (priv->config_container) { gtk_widget_destroy(priv->config_container); priv->config_container = NULL; gtk_widget_destroy(priv->config_viewport); priv->config_viewport = NULL; gtk_widget_destroy(priv->config_scrollable); priv->config_scrollable = NULL; } priv->server_combo = NULL; priv->resolution_iws_radio = NULL; priv->resolution_auto_radio = NULL; priv->resolution_custom_radio = NULL; priv->resolution_custom_combo = NULL; priv->keymap_combo = NULL; priv->ssh_tunnel_enabled_check = NULL; priv->ssh_tunnel_loopback_check = NULL; priv->ssh_tunnel_server_default_radio = NULL; priv->ssh_tunnel_server_custom_radio = NULL; priv->ssh_tunnel_server_entry = NULL; priv->ssh_tunnel_username_entry = NULL; priv->ssh_tunnel_auth_agent_radio = NULL; priv->ssh_tunnel_auth_password_radio = NULL; priv->ssh_tunnel_auth_publickey_radio = NULL; priv->ssh_tunnel_auth_auto_publickey_radio = NULL; priv->ssh_tunnel_privatekey_chooser = NULL; g_hash_table_remove_all(priv->setting_widgets); protocol = remmina_public_combo_get_active_text(combo); if (protocol) { priv->plugin = (RemminaProtocolPlugin *)remmina_plugin_manager_get_plugin(REMMINA_PLUGIN_TYPE_PROTOCOL, protocol); g_free(protocol); remmina_file_editor_create_all_settings(gfe); } } static void remmina_file_editor_save_ssh_tunnel_tab(RemminaFileEditor *gfe) { TRACE_CALL(__func__); RemminaFileEditorPriv *priv = gfe->priv; gboolean ssh_tunnel_enabled; int ssh_tunnel_auth; ssh_tunnel_enabled = (priv->ssh_tunnel_enabled_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_enabled_check)) : FALSE); remmina_file_set_int(priv->remmina_file, "ssh_tunnel_loopback", (priv->ssh_tunnel_loopback_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_loopback_check)) : FALSE)); remmina_file_set_int(priv->remmina_file, "ssh_tunnel_enabled", ssh_tunnel_enabled); remmina_file_set_string(priv->remmina_file, "ssh_tunnel_username", (ssh_tunnel_enabled ? gtk_entry_get_text(GTK_ENTRY(priv->ssh_tunnel_username_entry)) : NULL)); remmina_file_set_string( priv->remmina_file, "ssh_tunnel_server", (ssh_tunnel_enabled && priv->ssh_tunnel_server_entry && (priv->ssh_tunnel_server_custom_radio == NULL || gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(priv->ssh_tunnel_server_custom_radio))) ? gtk_entry_get_text(GTK_ENTRY(priv->ssh_tunnel_server_entry)) : NULL)); ssh_tunnel_auth = (priv->ssh_tunnel_auth_publickey_radio && gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(priv->ssh_tunnel_auth_publickey_radio)) ? SSH_AUTH_PUBLICKEY : priv->ssh_tunnel_auth_auto_publickey_radio && gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(priv->ssh_tunnel_auth_auto_publickey_radio)) ? SSH_AUTH_AUTO_PUBLICKEY : priv->ssh_tunnel_auth_agent_radio && gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(priv->ssh_tunnel_auth_agent_radio)) ? SSH_AUTH_AGENT : SSH_AUTH_PASSWORD); remmina_file_set_int( priv->remmina_file, "ssh_tunnel_auth", ssh_tunnel_auth ); remmina_file_set_string( priv->remmina_file, "ssh_tunnel_privatekey", (priv->ssh_tunnel_privatekey_chooser ? gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->ssh_tunnel_privatekey_chooser)) : NULL)); remmina_file_set_string( priv->remmina_file, "ssh_tunnel_password", (ssh_tunnel_enabled && (ssh_tunnel_auth == SSH_AUTH_PASSWORD)) ? gtk_entry_get_text(GTK_ENTRY(priv->ssh_tunnel_auth_password)) : NULL); remmina_file_set_string( priv->remmina_file, "ssh_tunnel_passphrase", (ssh_tunnel_enabled && (ssh_tunnel_auth == SSH_AUTH_PUBLICKEY || ssh_tunnel_auth == SSH_AUTH_AUTO_PUBLICKEY)) ? gtk_entry_get_text(GTK_ENTRY(priv->ssh_tunnel_passphrase)) : NULL); } static void remmina_file_editor_update_settings(RemminaFileEditor *gfe) { TRACE_CALL(__func__); RemminaFileEditorPriv *priv = gfe->priv; GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, priv->setting_widgets); while (g_hash_table_iter_next(&iter, &key, &value)) { if (GTK_IS_ENTRY(value)) { remmina_file_set_string(priv->remmina_file, (gchar *)key, gtk_entry_get_text(GTK_ENTRY(value))); } else if (GTK_IS_COMBO_BOX(value)) { remmina_file_set_string_ref(priv->remmina_file, (gchar *)key, remmina_public_combo_get_active_text(GTK_COMBO_BOX(value))); } else if (GTK_IS_FILE_CHOOSER(value)) { remmina_file_set_string( priv->remmina_file, (gchar *)key, gtk_widget_get_sensitive(GTK_WIDGET(value)) ? gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(value)) : NULL); } else if (GTK_IS_TOGGLE_BUTTON(value)) { remmina_file_set_int(priv->remmina_file, (gchar *)key, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(value))); } } } static void remmina_file_editor_update(RemminaFileEditor *gfe) { TRACE_CALL(__func__); int res_w, res_h; gchar *custom_resolution; RemminaProtocolWidgetResolutionMode res_mode; RemminaFileEditorPriv *priv = gfe->priv; remmina_file_set_string(priv->remmina_file, "name", gtk_entry_get_text(GTK_ENTRY(priv->name_entry))); remmina_file_set_string_ref(priv->remmina_file, "group", (priv->group_combo ? remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->group_combo)) : NULL)); remmina_file_set_string_ref(priv->remmina_file, "protocol", remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->protocol_combo))); remmina_file_set_string(priv->remmina_file, "precommand", gtk_entry_get_text(GTK_ENTRY(priv->precommand_entry))); remmina_file_set_string(priv->remmina_file, "postcommand", gtk_entry_get_text(GTK_ENTRY(priv->postcommand_entry))); remmina_file_set_string_ref(priv->remmina_file, "server", (priv->server_combo ? remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->server_combo)) : NULL)); if (priv->resolution_auto_radio) { if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->resolution_auto_radio))) { /* Resolution is set to auto (which means: Use client fullscreen resolution, aka use client resolution) */ res_w = res_h = 0; res_mode = RES_USE_CLIENT; } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->resolution_iws_radio))) { /* Resolution is set to initial window size */ res_w = res_h = 0; res_mode = RES_USE_INITIAL_WINDOW_SIZE; } else { /* Resolution is set to a value from the list */ custom_resolution = remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->resolution_custom_combo)); if (remmina_public_split_resolution_string(custom_resolution, &res_w, &res_h)) res_mode = RES_USE_CUSTOM; else res_mode = RES_USE_INITIAL_WINDOW_SIZE; g_free(custom_resolution); } remmina_file_set_int(priv->remmina_file, "resolution_mode", res_mode); remmina_file_set_int(priv->remmina_file, "resolution_width", res_w); remmina_file_set_int(priv->remmina_file, "resolution_height", res_h); } if (priv->keymap_combo) remmina_file_set_string_ref(priv->remmina_file, "keymap", remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->keymap_combo))); remmina_file_editor_save_ssh_tunnel_tab(gfe); remmina_file_editor_update_settings(gfe); } static void remmina_file_editor_on_default(GtkWidget *button, RemminaFileEditor *gfe) { TRACE_CALL(__func__); RemminaFile *gf; GtkWidget *dialog; remmina_file_editor_update(gfe); gf = remmina_file_dup(gfe->priv->remmina_file); remmina_file_set_filename(gf, remmina_pref_file); /* Clear properties that should never be default */ remmina_file_set_string(gf, "name", NULL); remmina_file_set_string(gf, "server", NULL); remmina_file_set_string(gf, "password", NULL); remmina_file_set_string(gf, "precommand", NULL); remmina_file_set_string(gf, "postcommand", NULL); remmina_file_set_string(gf, "ssh_tunnel_server", NULL); remmina_file_set_string(gf, "ssh_tunnel_password", NULL); remmina_file_set_string(gf, "ssh_tunnel_passphrase", NULL); remmina_file_save(gf); remmina_file_free(gf); dialog = gtk_message_dialog_new(GTK_WINDOW(gfe), GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, _("Default settings saved.")); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); } static void remmina_file_editor_on_save(GtkWidget *button, RemminaFileEditor *gfe) { TRACE_CALL(__func__); remmina_file_editor_update(gfe); remmina_file_editor_file_save(gfe); remmina_file_save(gfe->priv->remmina_file); remmina_icon_populate_menu(); gtk_widget_destroy(GTK_WIDGET(gfe)); } static void remmina_file_editor_on_connect(GtkWidget *button, RemminaFileEditor *gfe) { TRACE_CALL(__func__); RemminaFile *gf; remmina_file_editor_update(gfe); gf = remmina_file_dup(gfe->priv->remmina_file); /* Put server into name for "Quick Connect" */ if (remmina_file_get_filename(gf) == NULL) remmina_file_set_string(gf, "name", remmina_file_get_string(gf, "server")); gtk_widget_destroy(GTK_WIDGET(gfe)); gf->prevent_saving = TRUE; rcw_open_from_file(gf); } static void remmina_file_editor_on_save_connect(GtkWidget *button, RemminaFileEditor *gfe) { TRACE_CALL(__func__); /** @TODO: Call remmina_file_editor_on_save */ RemminaFile *gf; remmina_file_editor_update(gfe); remmina_file_editor_file_save(gfe); remmina_file_save(gfe->priv->remmina_file); remmina_icon_populate_menu(); gf = remmina_file_dup(gfe->priv->remmina_file); /* Put server into name for Quick Connect */ if (remmina_file_get_filename(gf) == NULL) remmina_file_set_string(gf, "name", remmina_file_get_string(gf, "server")); gtk_widget_destroy(GTK_WIDGET(gfe)); rcw_open_from_file(gf); } static void remmina_file_editor_on_cancel(GtkWidget *button, RemminaFileEditor *gfe) { TRACE_CALL(__func__); gtk_widget_destroy(GTK_WIDGET(gfe)); } static void remmina_file_editor_init(RemminaFileEditor *gfe) { TRACE_CALL(__func__); RemminaFileEditorPriv *priv; GtkWidget *widget; priv = g_new0(RemminaFileEditorPriv, 1); gfe->priv = priv; /* Create the editor dialog */ gtk_window_set_title(GTK_WINDOW(gfe), _("Remote Desktop Preference")); widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("_Cancel")), GTK_RESPONSE_CANCEL); g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_cancel), gfe); /* Default button */ widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("Save as Default")), GTK_RESPONSE_OK); gtk_widget_set_tooltip_text(GTK_WIDGET(widget), _("Use the current settings as the default for all new connection profiles")); g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_default), gfe); widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("_Save")), GTK_RESPONSE_APPLY); g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_save), gfe); gtk_widget_set_sensitive(widget, FALSE); priv->save_button = widget; widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("Connect")), GTK_RESPONSE_ACCEPT); g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_connect), gfe); widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("_Save and Connect")), GTK_RESPONSE_OK); gtk_widget_set_can_default(widget, TRUE); g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_save_connect), gfe); gtk_dialog_set_default_response(GTK_DIALOG(gfe), GTK_RESPONSE_OK); gtk_window_set_default_size(GTK_WINDOW(gfe), 800, 600); g_signal_connect(G_OBJECT(gfe), "destroy", G_CALLBACK(remmina_file_editor_destroy), NULL); g_signal_connect(G_OBJECT(gfe), "realize", G_CALLBACK(remmina_file_editor_on_realize), NULL); priv->setting_widgets = g_hash_table_new(g_str_hash, g_str_equal); remmina_widget_pool_register(GTK_WIDGET(gfe)); } static gboolean remmina_file_editor_iterate_protocol(gchar *protocol, RemminaPlugin *plugin, gpointer data) { TRACE_CALL(__func__); RemminaFileEditor *gfe = REMMINA_FILE_EDITOR(data); GtkListStore *store; GtkTreeIter iter; gboolean first; store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(gfe->priv->protocol_combo))); first = !gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, protocol, 1, g_dgettext(plugin->domain, plugin->description), 2, ((RemminaProtocolPlugin *)plugin)->icon_name, -1); if (first || g_strcmp0(protocol, remmina_file_get_string(gfe->priv->remmina_file, "protocol")) == 0) gtk_combo_box_set_active_iter(GTK_COMBO_BOX(gfe->priv->protocol_combo), &iter); return FALSE; } void remmina_file_editor_check_profile(RemminaFileEditor *gfe) { TRACE_CALL(__func__); RemminaFileEditorPriv *priv; priv = gfe->priv; gtk_widget_set_sensitive(priv->group_combo, TRUE); gtk_widget_set_sensitive(priv->save_button, TRUE); } static void remmina_file_editor_entry_on_changed(GtkEditable *editable, RemminaFileEditor *gfe) { TRACE_CALL(__func__); RemminaFileEditorPriv *priv; priv = gfe->priv; if (remmina_file_get_filename(priv->remmina_file) == NULL) { remmina_file_generate_filename(priv->remmina_file); /* TODO: Probably to be removed */ remmina_file_editor_check_profile(gfe); } else { remmina_file_delete(remmina_file_get_filename(priv->remmina_file)); remmina_file_generate_filename(priv->remmina_file); remmina_file_editor_check_profile(gfe); } } void remmina_file_editor_file_save(RemminaFileEditor *gfe) { TRACE_CALL(__func__); RemminaFileEditorPriv *priv; priv = gfe->priv; if (remmina_file_get_filename(priv->remmina_file) == NULL) { remmina_file_generate_filename(priv->remmina_file); } else { remmina_file_delete(remmina_file_get_filename(priv->remmina_file)); remmina_file_generate_filename(priv->remmina_file); } } GtkWidget *remmina_file_editor_new_from_file(RemminaFile *remminafile) { TRACE_CALL(__func__); RemminaFileEditor *gfe; RemminaFileEditorPriv *priv; GtkWidget *grid; GtkWidget *widget; gchar *groups; gchar *s; const gchar *cs; gfe = REMMINA_FILE_EDITOR(g_object_new(REMMINA_TYPE_FILE_EDITOR, NULL)); priv = gfe->priv; priv->remmina_file = remminafile; if (remmina_file_get_filename(remminafile) == NULL) gtk_dialog_set_response_sensitive(GTK_DIALOG(gfe), GTK_RESPONSE_APPLY, FALSE); /* Create the "Profile" group on the top (for name and protocol) */ grid = gtk_grid_new(); gtk_widget_show(grid); gtk_grid_set_row_spacing(GTK_GRID(grid), 4); gtk_grid_set_column_spacing(GTK_GRID(grid), 8); gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); gtk_container_set_border_width(GTK_CONTAINER(grid), 8); gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(gfe))), grid, FALSE, FALSE, 2); //remmina_public_create_group(GTK_GRID(grid), _("Profile"), 0, 4, 3); /* Profile: Name */ widget = gtk_label_new(_("Name")); gtk_widget_show(widget); gtk_widget_set_valign(widget, GTK_ALIGN_START); gtk_widget_set_halign(widget, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), widget, 0, 3, 2, 1); gtk_grid_set_column_spacing(GTK_GRID(grid), 10); widget = gtk_entry_new(); gtk_widget_show(widget); gtk_grid_attach(GTK_GRID(grid), widget, 1, 3, 3, 1); gtk_entry_set_max_length(GTK_ENTRY(widget), 100); priv->name_entry = widget; if (remmina_file_get_filename(remminafile) == NULL) { gtk_entry_set_text(GTK_ENTRY(widget), _("Quick Connect")); #if GTK_CHECK_VERSION(3, 16, 0) gtk_entry_grab_focus_without_selecting(GTK_ENTRY(widget)); #endif g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(remmina_file_editor_entry_on_changed), gfe); } else { cs = remmina_file_get_string(remminafile, "name"); gtk_entry_set_text(GTK_ENTRY(widget), cs ? cs : ""); } /* Profile: Group */ widget = gtk_label_new(_("Group")); gtk_widget_show(widget); gtk_widget_set_valign(widget, GTK_ALIGN_START); gtk_widget_set_halign(widget, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), widget, 0, 6, 2, 1); groups = remmina_file_manager_get_groups(); priv->group_combo = remmina_public_create_combo_entry(groups, remmina_file_get_string(remminafile, "group"), FALSE); g_free(groups); gtk_widget_show(priv->group_combo); gtk_grid_attach(GTK_GRID(grid), priv->group_combo, 1, 6, 3, 1); gtk_widget_set_sensitive(priv->group_combo, FALSE); s = g_strdup_printf(_("Use '%s' as subgroup delimiter"), "/"); gtk_widget_set_tooltip_text(priv->group_combo, s); g_free(s); /* Profile: Protocol */ widget = gtk_label_new(_("Protocol")); gtk_widget_show(widget); gtk_widget_set_valign(widget, GTK_ALIGN_START); gtk_widget_set_halign(widget, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), widget, 0, 9, 2, 1); widget = remmina_public_create_combo(TRUE); gtk_widget_show(widget); gtk_grid_attach(GTK_GRID(grid), widget, 1, 9, 3, 1); priv->protocol_combo = widget; remmina_plugin_manager_for_each_plugin(REMMINA_PLUGIN_TYPE_PROTOCOL, remmina_file_editor_iterate_protocol, gfe); g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(remmina_file_editor_protocol_combo_on_changed), gfe); /* Prior connection command */ widget = gtk_label_new(_("Pre-command")); gtk_widget_show(widget); gtk_widget_set_valign(widget, GTK_ALIGN_START); gtk_widget_set_halign(widget, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), widget, 0, 12, 3, 1); gtk_grid_set_column_spacing(GTK_GRID(grid), 10); widget = gtk_entry_new(); gtk_widget_show(widget); gtk_grid_attach(GTK_GRID(grid), widget, 1, 12, 3, 1); gtk_entry_set_max_length(GTK_ENTRY(widget), 200); priv->precommand_entry = widget; cs = remmina_file_get_string(remminafile, "precommand"); gtk_entry_set_text(GTK_ENTRY(widget), cs ? cs : ""); gtk_entry_set_placeholder_text(GTK_ENTRY(widget), "command %h %u %t %U %p %g --option"); gtk_widget_set_tooltip_markup(widget, _(cmd_tips)); /* POST connection command */ widget = gtk_label_new(_("Post-command")); gtk_widget_show(widget); gtk_widget_set_valign(widget, GTK_ALIGN_START); gtk_widget_set_halign(widget, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), widget, 0, 15, 3, 1); gtk_grid_set_column_spacing(GTK_GRID(grid), 10); widget = gtk_entry_new(); gtk_widget_show(widget); gtk_grid_attach(GTK_GRID(grid), widget, 1, 15, 3, 1); gtk_entry_set_max_length(GTK_ENTRY(widget), 200); priv->postcommand_entry = widget; cs = remmina_file_get_string(remminafile, "postcommand"); gtk_entry_set_text(GTK_ENTRY(widget), cs ? cs : ""); gtk_entry_set_placeholder_text(GTK_ENTRY(widget), "/path/to/command -opt1 arg %h %u %t -opt2 %U %p %g"); gtk_widget_set_tooltip_markup(widget, _(cmd_tips)); /* Create the "Preference" frame */ widget = gtk_event_box_new(); gtk_widget_show(widget); gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(gfe))), widget, TRUE, TRUE, 2); priv->config_box = widget; priv->config_container = NULL; priv->config_scrollable = NULL; remmina_file_editor_protocol_combo_on_changed(GTK_COMBO_BOX(priv->protocol_combo), gfe); remmina_file_editor_check_profile(gfe); return GTK_WIDGET(gfe); } GtkWidget *remmina_file_editor_new(void) { TRACE_CALL(__func__); return remmina_file_editor_new_full(NULL, NULL); } GtkWidget *remmina_file_editor_new_full(const gchar *server, const gchar *protocol) { TRACE_CALL(__func__); RemminaFile *remminafile; remminafile = remmina_file_new(); if (server) remmina_file_set_string(remminafile, "server", server); if (protocol) remmina_file_set_string(remminafile, "protocol", protocol); return remmina_file_editor_new_from_file(remminafile); } GtkWidget *remmina_file_editor_new_copy(const gchar *filename) { TRACE_CALL(__func__); RemminaFile *remminafile; GtkWidget *dialog; remminafile = remmina_file_copy(filename); if (remminafile) { return remmina_file_editor_new_from_file(remminafile); } else { dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Could not find the file \"%s\"."), filename); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return NULL; } } GtkWidget *remmina_file_editor_new_from_filename(const gchar *filename) { TRACE_CALL(__func__); RemminaFile *remminafile; GtkWidget *dialog; remminafile = remmina_file_manager_load_file(filename); if (remminafile) { return remmina_file_editor_new_from_file(remminafile); } else { dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Could not find the file \"%s\"."), filename); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return NULL; } }