diff options
Diffstat (limited to 'src/remmina_mpchange.c')
-rw-r--r-- | src/remmina_mpchange.c | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/src/remmina_mpchange.c b/src/remmina_mpchange.c new file mode 100644 index 0000000..a5266b8 --- /dev/null +++ b/src/remmina_mpchange.c @@ -0,0 +1,518 @@ +/* + * 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-2023 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 <gtk/gtk.h> +#include <glib/gi18n.h> +#include "config.h" +#include "remmina_mpchange.h" +#include "remmina_file.h" +#include "remmina_file_manager.h" +#include "remmina_pref.h" +#include "remmina_public.h" +#include "remmina_main.h" +#include "remmina_plugin_manager.h" +#include "remmina_log.h" +#include "remmina/remmina_trace_calls.h" + +#define GET_DIALOG_OBJECT(object_name) gtk_builder_get_object(bu, object_name) + +struct mpchanger_params { + gchar *username; // New username + gchar *domain; // New domain + gchar *password; // New password + gchar *group; + gchar *gatewayusername; + gchar *gatewaydomain; + gchar *gatewaypassword; + + GtkEntry *eGroup, *eUsername, *eDomain, *ePassword1, *ePassword2; + GtkEntry *eGatewayUsername, *eGatewayDomain, *eGatewayPassword1, *eGatewayPassword2; + GtkListStore* store; + GtkDialog* dialog; + GtkTreeView* table; + GtkButton* btnDoChange; + GtkLabel* statusLabel; + + GtkTreeIter iter; + int changed_passwords_count; + guint sid; + guint searchentrychange_timeout_source_id; +}; + +enum { + COL_F = 0, + COL_NAME, + COL_GROUP, + COL_USERNAME, + COL_GATEWAY_USERNAME, + COL_FILENAME, + NUM_COLS +}; + +static gboolean remmina_mpchange_fieldcompare(const gchar *needle, const gchar *haystack, int *matchcount) +{ + TRACE_CALL(__func__); + + if (needle[0] == 0) { + (*matchcount)++; + return TRUE; + } + + if (haystack[0] == 0 || strstr(haystack, needle) == NULL){ + return FALSE; + } + + (*matchcount)++; + return TRUE; + +} + +static void remmina_mpchange_file_list_callback(RemminaFile *remminafile, gpointer user_data) +{ + TRACE_CALL(__func__); + GtkListStore* store; + GtkTreeIter iter; + int matchcount; + const gchar *username, *domain, *group, *gatewayusername, *gatewaydomain; + + gchar* s; + gchar* t; + struct mpchanger_params* mpcp; + + mpcp = (struct mpchanger_params*)user_data; + store = GTK_LIST_STORE(mpcp->store); + + + username = remmina_file_get_string(remminafile, "username"); + domain = remmina_file_get_string(remminafile, "domain"); + group = remmina_file_get_string(remminafile, "group"); + gatewayusername = remmina_file_get_string(remminafile, "gateway_username"); + gatewaydomain = remmina_file_get_string(remminafile, "gateway_domain"); + + if (username == NULL) + username = ""; + + if (domain == NULL) + domain = ""; + + if (group == NULL) + group = ""; + + if (gatewayusername == NULL) + gatewayusername = ""; + + if (gatewaydomain == NULL) + gatewaydomain = ""; + + matchcount = 0; + if (!remmina_mpchange_fieldcompare(mpcp->username, username, &matchcount)) + return; + if (!remmina_mpchange_fieldcompare(mpcp->domain, domain, &matchcount)) + return; + if (!remmina_mpchange_fieldcompare(mpcp->group, group, &matchcount)) + return; + if (!remmina_mpchange_fieldcompare(mpcp->gatewayusername, gatewayusername, &matchcount)) + return; + if (!remmina_mpchange_fieldcompare(mpcp->gatewaydomain, gatewaydomain, &matchcount)) + return; + + gtk_list_store_append(store, &iter); + s = g_strdup_printf("%s\\%s", domain, username); + t = g_strdup_printf("%s\\%s", gatewaydomain, gatewayusername); + gtk_list_store_set(store, &iter, + COL_F, matchcount >= 5 ? TRUE : FALSE, + COL_NAME, remmina_file_get_string(remminafile, "name"), + COL_GROUP, group, + COL_USERNAME, s, + COL_GATEWAY_USERNAME, t, + COL_FILENAME, remminafile->filename, + -1); + g_free(s); + g_free(t); + +} + +static void remmina_mpchange_checkbox_toggle(GtkCellRendererToggle *cell, gchar *path_string, gpointer user_data) +{ + TRACE_CALL(__func__); + GtkTreeIter iter; + struct mpchanger_params* mpcp = (struct mpchanger_params*)user_data; + GtkTreePath *path; + + gboolean a = gtk_cell_renderer_toggle_get_active(cell); + path = gtk_tree_path_new_from_string(path_string); + gtk_tree_model_get_iter(GTK_TREE_MODEL(mpcp->store), &iter, path); + gtk_tree_path_free(path); + gtk_list_store_set(mpcp->store, &iter, COL_F, !a, -1); +} + +static void remmina_mpchange_dochange(gchar* fname, struct mpchanger_params* mpcp) +{ + TRACE_CALL(__func__); + + RemminaFile* remminafile; + + remminafile = remmina_file_load(fname); + if (remminafile) { + if(mpcp->password[0] != 0){ + remmina_file_store_secret_plugin_password(remminafile, "password", mpcp->password); + } + if(mpcp->gatewaypassword[0] != 0){ + remmina_file_store_secret_plugin_password(remminafile, "gateway_password", mpcp->gatewaypassword); + } + remmina_file_free(remminafile); + mpcp->changed_passwords_count++; + } + +} + +static void enable_inputs(struct mpchanger_params* mpcp, gboolean ena) +{ + gtk_widget_set_sensitive(GTK_WIDGET(mpcp->eGroup), ena); + gtk_widget_set_sensitive(GTK_WIDGET(mpcp->eUsername), ena); + gtk_widget_set_sensitive(GTK_WIDGET(mpcp->eDomain), ena); + gtk_widget_set_sensitive(GTK_WIDGET(mpcp->ePassword1), ena); + gtk_widget_set_sensitive(GTK_WIDGET(mpcp->ePassword2), ena); + gtk_widget_set_sensitive(GTK_WIDGET(mpcp->eGatewayUsername), ena); + gtk_widget_set_sensitive(GTK_WIDGET(mpcp->eGatewayDomain), ena); + gtk_widget_set_sensitive(GTK_WIDGET(mpcp->eGatewayPassword1), ena); + gtk_widget_set_sensitive(GTK_WIDGET(mpcp->eGatewayPassword2), ena); + gtk_widget_set_sensitive(GTK_WIDGET(mpcp->btnDoChange), ena); + gtk_widget_set_sensitive(GTK_WIDGET(mpcp->table), ena); +} + +static gboolean changenext(gpointer user_data) +{ + TRACE_CALL(__func__); + struct mpchanger_params* mpcp = (struct mpchanger_params*)user_data; + gchar* fname; + gboolean sel; + + gtk_tree_model_get(GTK_TREE_MODEL(mpcp->store), &mpcp->iter, COL_F, &sel, -1); + gtk_tree_model_get(GTK_TREE_MODEL(mpcp->store), &mpcp->iter, COL_FILENAME, &fname, -1); + if (sel) { + remmina_mpchange_dochange(fname, mpcp); + } + g_free(fname); + + if (gtk_tree_model_iter_next(GTK_TREE_MODEL(mpcp->store), &mpcp->iter)) { + return G_SOURCE_CONTINUE; + }else { + gtk_dialog_response(mpcp->dialog, 1); + mpcp->sid = 0; + return G_SOURCE_REMOVE; + } +} + +static void remmina_mpchange_dochange_clicked(GtkButton *btn, gpointer user_data) +{ + TRACE_CALL(__func__); + struct mpchanger_params* mpcp = (struct mpchanger_params*)user_data; + const gchar *passwd1, *passwd2, *gatewaypasswd1, *gatewaypasswd2; + + if (mpcp->searchentrychange_timeout_source_id) { + g_source_remove(mpcp->searchentrychange_timeout_source_id); + mpcp->searchentrychange_timeout_source_id = 0; + } + + if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(mpcp->store), &mpcp->iter)) + return; + + passwd1 = gtk_entry_get_text(mpcp->ePassword1); + passwd2 = gtk_entry_get_text(mpcp->ePassword2); + + if (g_strcmp0(passwd1, passwd2) != 0) { + GtkWidget *msgDialog; + msgDialog = gtk_message_dialog_new(GTK_WINDOW(mpcp->dialog), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("The passwords do not match")); + gtk_dialog_run(GTK_DIALOG(msgDialog)); + gtk_widget_destroy(msgDialog); + return; + } + gatewaypasswd1 = gtk_entry_get_text(mpcp->eGatewayPassword1); + gatewaypasswd2 = gtk_entry_get_text(mpcp->eGatewayPassword2); + if (g_strcmp0(gatewaypasswd1, gatewaypasswd2) != 0) { + GtkWidget *msgDialog; + msgDialog = gtk_message_dialog_new(GTK_WINDOW(mpcp->dialog), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("The Gateway passwords do not match")); + gtk_dialog_run(GTK_DIALOG(msgDialog)); + gtk_widget_destroy(msgDialog); + return; + } + + g_free(mpcp->password); + mpcp->password = g_strdup(passwd1); + mpcp->changed_passwords_count = 0; + + g_free(mpcp->gatewaypassword); + mpcp->gatewaypassword = g_strdup(gatewaypasswd1); + mpcp->changed_passwords_count = 0; + + gtk_label_set_text(mpcp->statusLabel, _("Resetting passwords, please wait…")); + + enable_inputs(mpcp, FALSE); + mpcp->sid = g_idle_add(changenext, (gpointer)mpcp); + +} + +static gboolean remmina_mpchange_searchfield_changed_to(gpointer user_data) +{ + TRACE_CALL(__func__); + struct mpchanger_params *mpcp = (struct mpchanger_params *)user_data; + const gchar *s; + + if (mpcp->searchentrychange_timeout_source_id) { + g_source_remove(mpcp->searchentrychange_timeout_source_id); + mpcp->searchentrychange_timeout_source_id = 0; + } + + s = gtk_entry_get_text(mpcp->eGroup); + g_free(mpcp->group); + mpcp->group = g_strdup(s); + + s = gtk_entry_get_text(mpcp->eDomain); + g_free(mpcp->domain); + mpcp->domain = g_strdup(s); + + s = gtk_entry_get_text(mpcp->eUsername); + g_free(mpcp->username); + mpcp->username = g_strdup(s); + + s = gtk_entry_get_text(mpcp->eGatewayDomain); + g_free(mpcp->gatewaydomain); + mpcp->gatewaydomain = g_strdup(s); + + s = gtk_entry_get_text(mpcp->eGatewayUsername); + g_free(mpcp->gatewayusername); + mpcp->gatewayusername = g_strdup(s); + + if (mpcp->store != NULL) { + gtk_tree_view_set_model(mpcp->table, NULL); + } + mpcp->store = gtk_list_store_new(NUM_COLS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + + if (mpcp->group[0] != 0 || mpcp->domain[0] != 0 || mpcp->username[0] != 0 || mpcp->gatewayusername[0] != 0 || mpcp->gatewaydomain[0] != 0) + remmina_file_manager_iterate((GFunc)remmina_mpchange_file_list_callback, (gpointer)mpcp); + + gtk_tree_view_set_model(mpcp->table, GTK_TREE_MODEL(mpcp->store)); + + return G_SOURCE_CONTINUE; // Source already remove at the beginning + +} + +static void remmina_mpchange_searchfield_changed(GtkSearchEntry *se, gpointer user_data) +{ + TRACE_CALL(__func__); + struct mpchanger_params *mpcp = (struct mpchanger_params *)user_data; + + if (mpcp->searchentrychange_timeout_source_id) { + g_source_remove(mpcp->searchentrychange_timeout_source_id); + mpcp->searchentrychange_timeout_source_id = 0; + } + + mpcp->searchentrychange_timeout_source_id = g_timeout_add(500, remmina_mpchange_searchfield_changed_to, user_data); +} + + +static void remmina_mpchange_stopsearch(GtkSearchEntry *entry, gpointer user_data) +{ + TRACE_CALL(__func__); + /* The stop-search signal is emitted when pressing Esc on a GtkSearchEntry. We end the dialog. */ + struct mpchanger_params *mpcp = (struct mpchanger_params *)user_data; + gtk_dialog_response(mpcp->dialog, 1); +} + +static gboolean remmina_file_multipasswd_changer_mt(gpointer d) +{ + TRACE_CALL(__func__); + struct mpchanger_params *mpcp = d; + GtkBuilder* bu; + GtkDialog* dialog; + GtkWindow* mainwindow; + GtkCellRendererToggle *toggle; + RemminaSecretPlugin *secret_plugin; + char *initerror; + + mainwindow = remmina_main_get_window(); + + /* The multiple password changer works only when a secrecy plugin is available */ + initerror = NULL; + secret_plugin = remmina_plugin_manager_get_secret_plugin(); + if (secret_plugin == NULL) { + initerror = _("The multi password changer requires a secrecy plugin.\n"); + }else { + if (!secret_plugin->is_service_available(secret_plugin)) { + initerror = _("The multi password changer requires a secrecy service.\n"); + } + } + if (initerror) { + GtkWidget *msgDialog; + msgDialog = gtk_message_dialog_new(mainwindow, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, + "%s", initerror); + gtk_dialog_run(GTK_DIALOG(msgDialog)); + gtk_widget_destroy(msgDialog); + return FALSE; + } + + + bu = remmina_public_gtk_builder_new_from_resource("/org/remmina/Remmina/src/../data/ui/remmina_mpc.glade"); + if (!bu) { + REMMINA_DEBUG("Unable to load the multiple password changer Glade file interface\n"); + return FALSE; + } + + dialog = GTK_DIALOG(gtk_builder_get_object(bu, "MPCDialog")); + mpcp->dialog = dialog; + if (mainwindow) + gtk_window_set_transient_for(GTK_WINDOW(dialog), mainwindow); + + + mpcp->eGroup = GTK_ENTRY(GET_DIALOG_OBJECT("groupEntry")); + gtk_entry_set_text(mpcp->eGroup, mpcp->group); + g_signal_connect(G_OBJECT(mpcp->eGroup), "changed", G_CALLBACK(remmina_mpchange_searchfield_changed), (gpointer)mpcp); + g_signal_connect(G_OBJECT(mpcp->eGroup), "stop-search", G_CALLBACK(remmina_mpchange_stopsearch), (gpointer)mpcp); + + mpcp->eUsername = GTK_ENTRY(GET_DIALOG_OBJECT("usernameEntry")); + gtk_entry_set_text(mpcp->eUsername, mpcp->username); + g_signal_connect(G_OBJECT(mpcp->eUsername), "changed", G_CALLBACK(remmina_mpchange_searchfield_changed), (gpointer)mpcp); + + mpcp->eGatewayUsername = GTK_ENTRY(GET_DIALOG_OBJECT("gatewayUsernameEntry")); + gtk_entry_set_text(mpcp->eGatewayUsername, mpcp->gatewayusername); + g_signal_connect(G_OBJECT(mpcp->eGatewayUsername), "changed", G_CALLBACK(remmina_mpchange_searchfield_changed), (gpointer)mpcp); + + mpcp->eDomain = GTK_ENTRY(GET_DIALOG_OBJECT("domainEntry")); + gtk_entry_set_text(mpcp->eDomain, mpcp->domain); + g_signal_connect(G_OBJECT(mpcp->eDomain), "changed", G_CALLBACK(remmina_mpchange_searchfield_changed), (gpointer)mpcp); + + mpcp->eGatewayDomain = GTK_ENTRY(GET_DIALOG_OBJECT("gatewayDomainEntry")); + gtk_entry_set_text(mpcp->eGatewayDomain, mpcp->gatewaydomain); + g_signal_connect(G_OBJECT(mpcp->eGatewayDomain), "changed", G_CALLBACK(remmina_mpchange_searchfield_changed), (gpointer)mpcp); + + mpcp->ePassword1 = GTK_ENTRY(GET_DIALOG_OBJECT("password1Entry")); + gtk_entry_set_text(mpcp->ePassword1, mpcp->password); + + mpcp->eGatewayPassword1 = GTK_ENTRY(GET_DIALOG_OBJECT("gatewayPassword1Entry")); + gtk_entry_set_text(mpcp->eGatewayPassword1, mpcp->gatewaypassword); + + mpcp->ePassword2 = GTK_ENTRY(GET_DIALOG_OBJECT("password2Entry")); + gtk_entry_set_text(mpcp->ePassword2, mpcp->password); + + mpcp->eGatewayPassword2 = GTK_ENTRY(GET_DIALOG_OBJECT("gatewayPassword2Entry")); + gtk_entry_set_text(mpcp->eGatewayPassword2, mpcp->gatewaypassword); + + mpcp->statusLabel = GTK_LABEL(GET_DIALOG_OBJECT("statusLabel")); + + + + + mpcp->store = NULL; + + mpcp->table = GTK_TREE_VIEW(GET_DIALOG_OBJECT("profchangelist")); + + /* Fire a fake searchfield changed, so a new list store is created */ + remmina_mpchange_searchfield_changed(NULL, (gpointer)mpcp); + + toggle = GTK_CELL_RENDERER_TOGGLE(GET_DIALOG_OBJECT("cellrenderertoggle1")); + g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(remmina_mpchange_checkbox_toggle), (gpointer)mpcp); + + mpcp->btnDoChange = GTK_BUTTON(GET_DIALOG_OBJECT("btnDoChange")); + g_signal_connect(mpcp->btnDoChange, "clicked", G_CALLBACK(remmina_mpchange_dochange_clicked), (gpointer)mpcp); + + gtk_dialog_run(dialog); + gtk_widget_destroy(GTK_WIDGET(dialog)); + + if (mpcp->sid) { + g_source_remove(mpcp->sid); + mpcp->sid = 0; + } + + if (mpcp->searchentrychange_timeout_source_id) { + g_source_remove(mpcp->searchentrychange_timeout_source_id); + mpcp->searchentrychange_timeout_source_id = 0; + } + + if (mpcp->changed_passwords_count) { + GtkWidget *msgDialog; + msgDialog = gtk_message_dialog_new(GTK_WINDOW(mpcp->dialog), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + ngettext("%d password changed.", "%d passwords changed.", mpcp->changed_passwords_count), mpcp->changed_passwords_count); + gtk_dialog_run(GTK_DIALOG(msgDialog)); + gtk_widget_destroy(msgDialog); + } + + // Free data + g_free(mpcp->username); + g_free(mpcp->password); + g_free(mpcp->domain); + g_free(mpcp->group); + g_free(mpcp->gatewayusername); + g_free(mpcp->gatewaypassword); + g_free(mpcp->gatewaydomain); + g_free(mpcp); + return FALSE; +} + + +void +remmina_mpchange_schedule(gboolean has_domain, const gchar *group, const gchar *domain, const gchar *username, const gchar *password, const gchar *gatewayusername, const gchar *gatewaydomain, const gchar *gatewaypassword) +{ + // We could also be called in a subthread after a successful connection + // (not currently implemented) + // So we just schedule the multipassword changer to be executed on + // the main thread + + TRACE_CALL(__func__); + struct mpchanger_params *mpcp; + + mpcp = g_malloc0(sizeof(struct mpchanger_params)); + mpcp->username = g_strdup(username); + mpcp->password = g_strdup(password); + mpcp->domain = g_strdup(domain); + mpcp->group = g_strdup(group); + mpcp->gatewayusername = g_strdup(gatewayusername); + mpcp->gatewaypassword = g_strdup(gatewaypassword); + mpcp->gatewaydomain = g_strdup(gatewaydomain); + gdk_threads_add_idle(remmina_file_multipasswd_changer_mt, (gpointer)mpcp); + +} |