Remmina - The GTK+ Remote Desktop Client  v1.4.2
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
remmina_mpchange.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2009-2011 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2020 Antenore Gatta, Giovanni Panozzo
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * In addition, as a special exception, the copyright holders give
23  * permission to link the code of portions of this program with the
24  * OpenSSL library under certain conditions as described in each
25  * individual source file, and distribute linked combinations
26  * including the two.
27  * You must obey the GNU General Public License in all respects
28  * for all of the code used other than OpenSSL. * If you modify
29  * file(s) with this exception, you may extend this exception to your
30  * version of the file(s), but you are not obligated to do so. * If you
31  * do not wish to do so, delete this exception statement from your
32  * version. * If you delete this exception statement from all source
33  * files in the program, then also delete it here.
34  *
35  */
36 
37 #include <gtk/gtk.h>
38 #include <glib/gi18n.h>
39 #include "config.h"
40 #include "remmina_mpchange.h"
41 #include "remmina_file.h"
42 #include "remmina_file_manager.h"
43 #include "remmina_pref.h"
44 #include "remmina_public.h"
45 #include "remmina_main.h"
46 #include "remmina_plugin_manager.h"
47 #include "remmina_log.h"
49 
50 #define GET_DIALOG_OBJECT(object_name) gtk_builder_get_object(bu, object_name)
51 
53  gchar *username; // New username
54  gchar *domain; // New domain
55  gchar *password; // New password
56  gchar *group;
57 
58  GtkEntry *eGroup, *eUsername, *eDomain;
59  GtkEntry *ePassword1, *ePassword2;
60  GtkListStore* store;
61  GtkDialog* dialog;
62  GtkTreeView* table;
63  GtkButton* btnDoChange;
64  GtkLabel* statusLabel;
65 
66  GtkTreeIter iter;
68  guint sid;
70 };
71 
72 enum {
73  COL_F = 0,
79 };
80 
81 static gboolean remmina_mpchange_fieldcompare(const gchar *needle, const gchar *haystack, int *matchcount)
82 {
83  TRACE_CALL(__func__);
84 
85  if (needle[0] == 0) {
86  (*matchcount)++;
87  return TRUE;
88  }
89 
90  if (strcasecmp(needle, haystack) != 0)
91  return FALSE;
92 
93  (*matchcount)++;
94  return TRUE;
95 
96 }
97 
98 static void remmina_mpchange_file_list_callback(RemminaFile *remminafile, gpointer user_data)
99 {
100  TRACE_CALL(__func__);
101  GtkListStore* store;
102  GtkTreeIter iter;
103  int matchcount;
104  const gchar *username, *domain, *group;
105 
106  gchar* s;
107  struct mpchanger_params* mpcp;
108 
109  mpcp = (struct mpchanger_params*)user_data;
110  store = GTK_LIST_STORE(mpcp->store);
111 
112 
113  username = remmina_file_get_string(remminafile, "username");
114  domain = remmina_file_get_string(remminafile, "domain");
115  group = remmina_file_get_string(remminafile, "group");
116 
117  if (username == NULL)
118  username = "";
119 
120  if (domain == NULL)
121  domain = "";
122 
123  if (group == NULL)
124  group = "";
125 
126  matchcount = 0;
127  if (!remmina_mpchange_fieldcompare(mpcp->username, username, &matchcount))
128  return;
129  if (!remmina_mpchange_fieldcompare(mpcp->domain, domain, &matchcount))
130  return;
131  if (!remmina_mpchange_fieldcompare(mpcp->group, group, &matchcount))
132  return;
133 
134  gtk_list_store_append(store, &iter);
135  s = g_strdup_printf("%s\\%s", domain, username);
136 
137  gtk_list_store_set(store, &iter,
138  COL_F, matchcount >= 3 ? TRUE : FALSE,
139  COL_NAME, remmina_file_get_string(remminafile, "name"),
140  COL_GROUP, group,
141  COL_USERNAME, s,
142  COL_FILENAME, remminafile->filename,
143  -1);
144  g_free(s);
145 
146 }
147 
148 static void remmina_mpchange_checkbox_toggle(GtkCellRendererToggle *cell, gchar *path_string, gpointer user_data)
149 {
150  TRACE_CALL(__func__);
151  GtkTreeIter iter;
152  struct mpchanger_params* mpcp = (struct mpchanger_params*)user_data;
153  GtkTreePath *path;
154 
155  gboolean a = gtk_cell_renderer_toggle_get_active(cell);
156  path = gtk_tree_path_new_from_string(path_string);
157  gtk_tree_model_get_iter(GTK_TREE_MODEL(mpcp->store), &iter, path);
158  gtk_tree_path_free(path);
159  gtk_list_store_set(mpcp->store, &iter, COL_F, !a, -1);
160 }
161 
162 static void remmina_mpchange_dochange(gchar* fname, struct mpchanger_params* mpcp)
163 {
164  TRACE_CALL(__func__);
165 
166  RemminaFile* remminafile;
167 
168  remminafile = remmina_file_load(fname);
169  if (remminafile) {
170  remmina_file_store_secret_plugin_password(remminafile, "password", mpcp->password);
171  remmina_file_free(remminafile);
172  mpcp->changed_passwords_count++;
173  }
174 
175 }
176 
177 static void enable_inputs(struct mpchanger_params* mpcp, gboolean ena)
178 {
179  gtk_widget_set_sensitive(GTK_WIDGET(mpcp->eGroup), ena);
180  gtk_widget_set_sensitive(GTK_WIDGET(mpcp->eUsername), ena);
181  gtk_widget_set_sensitive(GTK_WIDGET(mpcp->eDomain), ena);
182  gtk_widget_set_sensitive(GTK_WIDGET(mpcp->ePassword1), ena);
183  gtk_widget_set_sensitive(GTK_WIDGET(mpcp->ePassword2), ena);
184  gtk_widget_set_sensitive(GTK_WIDGET(mpcp->btnDoChange), ena);
185  gtk_widget_set_sensitive(GTK_WIDGET(mpcp->table), ena);
186 }
187 
188 static gboolean changenext(gpointer user_data)
189 {
190  TRACE_CALL(__func__);
191  struct mpchanger_params* mpcp = (struct mpchanger_params*)user_data;
192  gchar* fname;
193  gboolean sel;
194 
195  gtk_tree_model_get(GTK_TREE_MODEL(mpcp->store), &mpcp->iter, COL_F, &sel, -1);
196  gtk_tree_model_get(GTK_TREE_MODEL(mpcp->store), &mpcp->iter, COL_FILENAME, &fname, -1);
197  if (sel) {
198  remmina_mpchange_dochange(fname, mpcp);
199  }
200  g_free(fname);
201 
202  if (gtk_tree_model_iter_next(GTK_TREE_MODEL(mpcp->store), &mpcp->iter)) {
203  return G_SOURCE_CONTINUE;
204  }else {
205  gtk_dialog_response(mpcp->dialog, 1);
206  mpcp->sid = 0;
207  return G_SOURCE_REMOVE;
208  }
209 }
210 
211 static void remmina_mpchange_dochange_clicked(GtkButton *btn, gpointer user_data)
212 {
213  TRACE_CALL(__func__);
214  struct mpchanger_params* mpcp = (struct mpchanger_params*)user_data;
215  const gchar *passwd1, *passwd2;
216 
218  g_source_remove(mpcp->searchentrychange_timeout_source_id);
220  }
221 
222  if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(mpcp->store), &mpcp->iter))
223  return;
224 
225  passwd1 = gtk_entry_get_text(mpcp->ePassword1);
226  passwd2 = gtk_entry_get_text(mpcp->ePassword2);
227 
228  if (g_strcmp0(passwd1, passwd2) != 0) {
229  GtkWidget *msgDialog;
230  msgDialog = gtk_message_dialog_new(GTK_WINDOW(mpcp->dialog),
231  GTK_DIALOG_DESTROY_WITH_PARENT,
232  GTK_MESSAGE_ERROR,
233  GTK_BUTTONS_CLOSE,
234  _("The passwords do not match"));
235  gtk_dialog_run(GTK_DIALOG(msgDialog));
236  gtk_widget_destroy(msgDialog);
237  return;
238  }
239 
240  g_free(mpcp->password);
241  mpcp->password = g_strdup(passwd1);
242  mpcp->changed_passwords_count = 0;
243 
244  gtk_label_set_text(mpcp->statusLabel, _("Resetting passwords, please wait…"));
245 
246  enable_inputs(mpcp, FALSE);
247  mpcp->sid = g_idle_add(changenext, (gpointer)mpcp);
248 
249 }
250 
251 static gboolean remmina_mpchange_searchfield_changed_to(gpointer user_data)
252 {
253  TRACE_CALL(__func__);
254  struct mpchanger_params *mpcp = (struct mpchanger_params *)user_data;
255  const gchar *s;
256 
258  g_source_remove(mpcp->searchentrychange_timeout_source_id);
260  }
261 
262  s = gtk_entry_get_text(mpcp->eGroup);
263  g_free(mpcp->group);
264  mpcp->group = g_strdup(s);
265 
266  s = gtk_entry_get_text(mpcp->eDomain);
267  g_free(mpcp->domain);
268  mpcp->domain = g_strdup(s);
269 
270  s = gtk_entry_get_text(mpcp->eUsername);
271  g_free(mpcp->username);
272  mpcp->username = g_strdup(s);
273 
274  if (mpcp->store != NULL) {
275  gtk_tree_view_set_model(mpcp->table, NULL);
276  }
277  mpcp->store = gtk_list_store_new(NUM_COLS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
278 
279  if (mpcp->group[0] != 0 || mpcp->domain[0] != 0 || mpcp->username[0] != 0)
281 
282  gtk_tree_view_set_model(mpcp->table, GTK_TREE_MODEL(mpcp->store));
283 
284  return G_SOURCE_CONTINUE; // Source already remove at the beginning
285 
286 }
287 
288 static void remmina_mpchange_searchfield_changed(GtkSearchEntry *se, gpointer user_data)
289 {
290  TRACE_CALL(__func__);
291  struct mpchanger_params *mpcp = (struct mpchanger_params *)user_data;
292 
294  g_source_remove(mpcp->searchentrychange_timeout_source_id);
296  }
297 
299 }
300 
301 
302 static void remmina_mpchange_stopsearch(GtkSearchEntry *entry, gpointer user_data)
303 {
304  TRACE_CALL(__func__);
305  /* The stop-search signal is emitted when pressing Esc on a GtkSearchEntry. We end the dialog. */
306  struct mpchanger_params *mpcp = (struct mpchanger_params *)user_data;
307  gtk_dialog_response(mpcp->dialog, 1);
308 }
309 
310 static gboolean remmina_file_multipasswd_changer_mt(gpointer d)
311 {
312  TRACE_CALL(__func__);
313  struct mpchanger_params *mpcp = d;
314  GtkBuilder* bu;
315  GtkDialog* dialog;
316  GtkWindow* mainwindow;
317  GtkCellRendererToggle *toggle;
318  RemminaSecretPlugin *secret_plugin;
319  char *initerror;
320 
321  mainwindow = remmina_main_get_window();
322 
323  /* The multiple password changer works only when a secrecy plugin is available */
324  initerror = NULL;
326  if (secret_plugin == NULL) {
327  initerror = _("The multi password changer requires a secrecy plugin.\n");
328  }else {
329  if (!secret_plugin->is_service_available()) {
330  initerror = _("The multi password changer requires a secrecy service.\n");
331  }
332  }
333  if (initerror) {
334  GtkWidget *msgDialog;
335  msgDialog = gtk_message_dialog_new(mainwindow, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
336  "%s", initerror);
337  gtk_dialog_run(GTK_DIALOG(msgDialog));
338  gtk_widget_destroy(msgDialog);
339  return FALSE;
340  }
341 
342 
343  bu = remmina_public_gtk_builder_new_from_file("remmina_mpc.glade");
344  if (!bu) {
345  remmina_debug("Unable to load the multiple password changer glade file interface\n");
346  return FALSE;
347  }
348 
349  dialog = GTK_DIALOG(gtk_builder_get_object(bu, "MPCDialog"));
350  mpcp->dialog = dialog;
351  if (mainwindow)
352  gtk_window_set_transient_for(GTK_WINDOW(dialog), mainwindow);
353 
354 
355  mpcp->eGroup = GTK_ENTRY(GET_DIALOG_OBJECT("groupEntry"));
356  gtk_entry_set_text(mpcp->eGroup, mpcp->group);
357  g_signal_connect(G_OBJECT(mpcp->eGroup), "changed", G_CALLBACK(remmina_mpchange_searchfield_changed), (gpointer)mpcp);
358  g_signal_connect(G_OBJECT(mpcp->eGroup), "stop-search", G_CALLBACK(remmina_mpchange_stopsearch), (gpointer)mpcp);
359 
360  mpcp->eUsername = GTK_ENTRY(GET_DIALOG_OBJECT("usernameEntry"));
361  gtk_entry_set_text(mpcp->eUsername, mpcp->username);
362  g_signal_connect(G_OBJECT(mpcp->eUsername), "changed", G_CALLBACK(remmina_mpchange_searchfield_changed), (gpointer)mpcp);
363 
364  mpcp->eDomain = GTK_ENTRY(GET_DIALOG_OBJECT("domainEntry"));
365  gtk_entry_set_text(mpcp->eDomain, mpcp->domain);
366  g_signal_connect(G_OBJECT(mpcp->eDomain), "changed", G_CALLBACK(remmina_mpchange_searchfield_changed), (gpointer)mpcp);
367 
368  mpcp->ePassword1 = GTK_ENTRY(GET_DIALOG_OBJECT("password1Entry"));
369  gtk_entry_set_text(mpcp->ePassword1, mpcp->password);
370 
371  mpcp->ePassword2 = GTK_ENTRY(GET_DIALOG_OBJECT("password2Entry"));
372  gtk_entry_set_text(mpcp->ePassword2, mpcp->password);
373 
374  mpcp->statusLabel = GTK_LABEL(GET_DIALOG_OBJECT("statusLabel"));
375 
376 
377  mpcp->store = NULL;
378 
379  mpcp->table = GTK_TREE_VIEW(GET_DIALOG_OBJECT("profchangelist"));
380 
381  /* Fire a fake searchfield changed, so a new list store is created */
382  remmina_mpchange_searchfield_changed(NULL, (gpointer)mpcp);
383 
384  toggle = GTK_CELL_RENDERER_TOGGLE(GET_DIALOG_OBJECT("cellrenderertoggle1"));
385  g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(remmina_mpchange_checkbox_toggle), (gpointer)mpcp);
386 
387  mpcp->btnDoChange = GTK_BUTTON(GET_DIALOG_OBJECT("btnDoChange"));
388  g_signal_connect(mpcp->btnDoChange, "clicked", G_CALLBACK(remmina_mpchange_dochange_clicked), (gpointer)mpcp);
389 
390  gtk_dialog_run(dialog);
391  gtk_widget_destroy(GTK_WIDGET(dialog));
392 
393  if (mpcp->sid) {
394  g_source_remove(mpcp->sid);
395  mpcp->sid = 0;
396  }
397 
398  if (mpcp->searchentrychange_timeout_source_id) {
399  g_source_remove(mpcp->searchentrychange_timeout_source_id);
400  mpcp->searchentrychange_timeout_source_id = 0;
401  }
402 
403  if (mpcp->changed_passwords_count) {
404  GtkWidget *msgDialog;
405  msgDialog = gtk_message_dialog_new(GTK_WINDOW(mpcp->dialog),
406  GTK_DIALOG_DESTROY_WITH_PARENT,
407  GTK_MESSAGE_INFO,
408  GTK_BUTTONS_OK,
409  ngettext("%d password changed.", "%d passwords changed.", mpcp->changed_passwords_count), mpcp->changed_passwords_count);
410  gtk_dialog_run(GTK_DIALOG(msgDialog));
411  gtk_widget_destroy(msgDialog);
412  }
413 
414  // Free data
415  g_free(mpcp->username);
416  g_free(mpcp->password);
417  g_free(mpcp->domain);
418  g_free(mpcp->group);
419  g_free(mpcp);
420  return FALSE;
421 }
422 
423 
424 void
425 remmina_mpchange_schedule(gboolean has_domain, const gchar *group, const gchar *domain, const gchar *username, const gchar *password)
426 {
427  // We could also be called in a subthread after a successful connection
428  // (not currently implemented)
429  // So we just schedule the multipassword changer to be executed on
430  // the main thread
431 
432  TRACE_CALL(__func__);
433  struct mpchanger_params *mpcp;
434 
435  mpcp = g_malloc0(sizeof(struct mpchanger_params));
436  mpcp->username = g_strdup(username);
437  mpcp->password = g_strdup(password);
438  mpcp->domain = g_strdup(domain);
439  mpcp->group = g_strdup(group);
440  gdk_threads_add_idle(remmina_file_multipasswd_changer_mt, (gpointer)mpcp);
441 
442 }
443 
GtkButton * btnDoChange
RemminaFile * remmina_file_load(const gchar *filename)
Definition: remmina_file.c:327
void remmina_file_free(RemminaFile *remminafile)
Definition: remmina_file.c:548
const gchar * remmina_file_get_string(RemminaFile *remminafile, const gchar *setting)
Definition: remmina_file.c:449
guint searchentrychange_timeout_source_id
static void remmina_mpchange_stopsearch(GtkSearchEntry *entry, gpointer user_data)
GtkWindow * remmina_main_get_window()
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:41
GtkDialog * dialog
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
GtkBuilder * remmina_public_gtk_builder_new_from_file(gchar *filename)
GtkLabel * statusLabel
static gboolean remmina_mpchange_fieldcompare(const gchar *needle, const gchar *haystack, int *matchcount)
gboolean(* is_service_available)(void)
Definition: plugin.h:138
static void remmina_mpchange_file_list_callback(RemminaFile *remminafile, gpointer user_data)
static void remmina_mpchange_searchfield_changed(GtkSearchEntry *se, gpointer user_data)
void remmina_debug(const gchar *fmt,...)
Print a string in the Remmina Debug Windows and in the terminal.
Definition: remmina_log.c:194
GtkTreeView * table
static gboolean remmina_file_multipasswd_changer_mt(gpointer d)
void remmina_mpchange_schedule(gboolean has_domain, const gchar *group, const gchar *domain, const gchar *username, const gchar *password)
GtkEntry * eUsername
GtkEntry * ePassword1
static gboolean remmina_mpchange_searchfield_changed_to(gpointer user_data)
static void enable_inputs(struct mpchanger_params *mpcp, gboolean ena)
static void remmina_mpchange_dochange(gchar *fname, struct mpchanger_params *mpcp)
void remmina_file_store_secret_plugin_password(RemminaFile *remminafile, const gchar *key, const gchar *value)
Definition: remmina_file.c:660
GtkListStore * store
static void remmina_mpchange_dochange_clicked(GtkButton *btn, gpointer user_data)
static void remmina_mpchange_checkbox_toggle(GtkCellRendererToggle *cell, gchar *path_string, gpointer user_data)
static gboolean changenext(gpointer user_data)
GtkEntry * ePassword2
gint remmina_file_manager_iterate(GFunc func, gpointer user_data)