summaryrefslogtreecommitdiffstats
path: root/src/remmina_mpchange.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/remmina_mpchange.c')
-rw-r--r--src/remmina_mpchange.c518
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);
+
+}