From e25aa175bdbd1822659830d5ff2e81d08687e633 Mon Sep 17 00:00:00 2001 From: Antenore Gatta Date: Mon, 10 Jul 2023 13:46:18 +0000 Subject: Automatic doc build by remmina-ci --- public/rcw_8c_source.html | 2 +- public/remmina_8c_source.html | 2 +- public/remmina__applet__menu__item_8c_source.html | 2 +- public/remmina__exec_8c_source.html | 6 ++--- public/remmina__file_8c_source.html | 8 +++---- public/remmina__file__editor_8c_source.html | 6 ++--- public/remmina__file__manager_8c_source.html | 2 +- public/remmina__main_8c_source.html | 12 +++++----- public/remmina__mpchange_8c_source.html | 2 +- public/remmina__plugin__manager_8c.html | 26 ++++++++++----------- public/remmina__plugin__manager_8c_source.html | 28 +++++++++++------------ public/remmina__plugin__manager_8h.html | 22 +++++++++--------- public/remmina__plugin__manager_8h_source.html | 22 +++++++++--------- public/remmina__pref__dialog_8c_source.html | 2 +- public/remmina__protocol__widget_8c_source.html | 2 +- public/remmina__stats_8c_source.html | 2 +- 16 files changed, 73 insertions(+), 73 deletions(-) (limited to 'public') diff --git a/public/rcw_8c_source.html b/public/rcw_8c_source.html index 698473b58..3cda9ec01 100644 --- a/public/rcw_8c_source.html +++ b/public/rcw_8c_source.html @@ -265,7 +265,7 @@ $(document).ready(function(){initNavTree('rcw_8c_source.html','');});
static gboolean rcw_on_enter_notify_event(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
Definition: rcw.c:2809
gpointer opt3
Definition: types.h:77
void remmina_applet_menu_set_hide_count(RemminaAppletMenu *menu, gboolean hide_count)
-
gboolean remmina_plugin_manager_query_feature_by_type(RemminaPluginType ptype, const gchar *name, RemminaProtocolFeatureType ftype)
+
gboolean remmina_plugin_manager_query_feature_by_type(RemminaPluginType ptype, const gchar *name, RemminaProtocolFeatureType ftype)
static void rcw_on_page_removed(GtkNotebook *notebook, GtkWidget *child, guint page_num, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3673
static RemminaConnectionWindow * rcw_new(gboolean fullscreen, int full_screen_target_monitor)
Definition: rcw.c:3380
static gboolean rcw_on_leave_notify_event(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
Definition: rcw.c:2822
diff --git a/public/remmina_8c_source.html b/public/remmina_8c_source.html index a2e457379..30268f485 100644 --- a/public/remmina_8c_source.html +++ b/public/remmina_8c_source.html @@ -98,7 +98,7 @@ $(document).ready(function(){initNavTree('remmina_8c_source.html','');});
Definition: remmina_exec.h:55
Definition: remmina_exec.h:43
-
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
+
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
gboolean(* is_service_available)(struct _RemminaSecretPlugin *instance)
Definition: plugin.h:144
void remmina_masterthread_exec_save_main_thread_id()
void remmina_sftp_plugin_register(void)
diff --git a/public/remmina__applet__menu__item_8c_source.html b/public/remmina__applet__menu__item_8c_source.html index d3ffa8150..0701201e6 100644 --- a/public/remmina__applet__menu__item_8c_source.html +++ b/public/remmina__applet__menu__item_8c_source.html @@ -106,7 +106,7 @@ $(document).ready(function(){initNavTree('remmina__applet__menu__item_8c_source.
gchar * name
-
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
+
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
GtkWidget * remmina_applet_menu_item_new(RemminaAppletMenuItemType item_type,...)
const gchar * icon_name
Definition: plugin.h:72
diff --git a/public/remmina__exec_8c_source.html b/public/remmina__exec_8c_source.html index 5f26a6017..752836c21 100644 --- a/public/remmina__exec_8c_source.html +++ b/public/remmina__exec_8c_source.html @@ -113,7 +113,7 @@ $(document).ready(function(){initNavTree('remmina__exec_8c_source.html','');});
Definition: remmina_exec.h:48
void(* entry_func)(struct _RemminaEntryPlugin *instance)
Definition: plugin.h:97
Definition: remmina_exec.h:61
-
void remmina_plugin_manager_show(GtkWindow *parent)
+
void remmina_plugin_manager_show(GtkWindow *parent)
Definition: remmina_exec.h:44
static void remmina_exec_autostart_cb(RemminaFile *remminafile, gpointer user_data)
Definition: remmina_exec.c:210
@@ -139,7 +139,7 @@ $(document).ready(function(){initNavTree('remmina__exec_8c_source.html','');});
void remmina_about_open(GtkWindow *parent)
Definition: remmina_about.c:44
Definition: remmina_exec.h:60
-
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
+
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
static void remmina_exec_connect(const gchar *data)
Definition: remmina_exec.c:221
Definition: remmina_exec.h:53
@@ -155,7 +155,7 @@ $(document).ready(function(){initNavTree('remmina__exec_8c_source.html','');});
RemminaFile * remmina_file_manager_load_file(const gchar *filename)
static void newline_remove(char *s)
Definition: remmina_exec.c:155
gint remmina_unlock_new(GtkWindow *parent)
-
void remmina_plugin_manager_show_stdout()
+
void remmina_plugin_manager_show_stdout()
RemminaCommandType
Definition: remmina_exec.h:42
gint remmina_widget_pool_foreach(RemminaWidgetPoolForEachFunc callback, gpointer data)
Definition: remmina_exec.h:47
diff --git a/public/remmina__file_8c_source.html b/public/remmina__file_8c_source.html index 27065f480..d68066f5e 100644 --- a/public/remmina__file_8c_source.html +++ b/public/remmina__file_8c_source.html @@ -87,9 +87,9 @@ $(document).ready(function(){initNavTree('remmina__file_8c_source.html','');});
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-2023 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 "config.h"
38 
39 
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <sys/stat.h>
43 #include <locale.h>
44 #include <langinfo.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <time.h>
48 #include <unistd.h>
49 #include <utime.h>
50 
51 #include <gtk/gtk.h>
52 #include <glib/gi18n.h>
53 #include <glib/gstdio.h>
54 
56 #include "remmina_crypt.h"
57 #include "remmina_file_manager.h"
58 #include "remmina_log.h"
59 #include "remmina_main.h"
61 #include "remmina_plugin_manager.h"
62 #include "remmina_pref.h"
63 #include "remmina_public.h"
64 #include "remmina_sodium.h"
65 #include "remmina_utils.h"
66 
67 #define MIN_WINDOW_WIDTH 10
68 #define MIN_WINDOW_HEIGHT 10
69 
70 #define KEYFILE_GROUP_REMMINA "remmina"
71 #define KEYFILE_GROUP_STATE "Remmina Connection States"
72 
73 static struct timespec times[2];
74 
75 static RemminaFile *
77 {
78  TRACE_CALL(__func__);
79  RemminaFile *remminafile;
80 
81  remminafile = g_new0(RemminaFile, 1);
82  remminafile->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
83  remminafile->states = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
84  /* spsettings contains settings that are loaded from the secure_plugin.
85  * it’s used by remmina_file_store_secret_plugin_password() to know
86  * where to change */
87  remminafile->spsettings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
88  remminafile->prevent_saving = FALSE;
89  return remminafile;
90 }
91 
94 {
95  TRACE_CALL(__func__);
96  RemminaFile *remminafile;
97 
98  /* Try to load from the preference file for default settings first */
99  remminafile = remmina_file_load(remmina_pref_file);
100 
101  if (remminafile) {
102  g_free(remminafile->filename);
103  remminafile->filename = NULL;
104  } else {
105  remminafile = remmina_file_new_empty();
106  }
107 
108  return remminafile;
109 }
110 
115 {
116  TRACE_CALL(__func__);
117 
127  gchar *invalid_chars = "\\%|/$?<>:*. \"";
128  GString *filenamestr;
129  const gchar *s;
130 
131 
132  /* functions we can use
133  * g_strstrip( string )
134  * Removes leading and trailing whitespace from a string
135  * g_strdelimit (str, invalid_chars, '-'))
136  * Convert each invalid_chars in a hyphen
137  * g_ascii_strdown(string)
138  * all lowercase
139  * To be safe we should remove control characters as well (but I'm lazy)
140  * https://rosettacode.org/wiki/Strip_control_codes_and_extended_characters_from_a_string#C
141  * g_utf8_strncpy (gchar *dest, const gchar *src, gsize n);
142  * copies a given number of characters instead of a given number of bytes. The src string must be valid UTF-8 encoded text.
143  * g_utf8_validate (const gchar *str, gssize max_len, const gchar **end);
144  * Validates UTF-8 encoded text.
145  */
146 
147  //g_free(remminafile->filename), remminafile->filename = NULL;
148 
149  filenamestr = g_string_new(g_strdup_printf("%s",
151  if ((s = remmina_file_get_string(remminafile, "name")) == NULL) s = "name";
152  if (g_strstr_len(filenamestr->str, -1, "%N") != NULL)
153  remmina_utils_string_replace_all(filenamestr, "%N", s);
154 
155  if ((s = remmina_file_get_string(remminafile, "group")) == NULL) s = "group";
156  if (g_strstr_len(filenamestr->str, -1, "%G") != NULL)
157  remmina_utils_string_replace_all(filenamestr, "%G", s);
158 
159  if ((s = remmina_file_get_string(remminafile, "protocol")) == NULL) s = "proto";
160  if (g_strstr_len(filenamestr->str, -1, "%P") != NULL)
161  remmina_utils_string_replace_all(filenamestr, "%P", s);
162 
163  if ((s = remmina_file_get_string(remminafile, "server")) == NULL) s = "host";
164  if (g_strstr_len(filenamestr->str, -1, "%h") != NULL)
165  remmina_utils_string_replace_all(filenamestr, "%h", s);
166 
167  s = NULL;
168 
169  g_autofree gchar *filename = g_strdelimit(g_ascii_strdown(g_strstrip(g_string_free(filenamestr, FALSE)), -1),
170  invalid_chars, '-');
171 
172  GDir *dir = g_dir_open(remmina_file_get_datadir(), 0, NULL);
173 
174  if (dir != NULL)
175  remminafile->filename = g_strdup_printf("%s/%s.remmina", remmina_file_get_datadir(), filename);
176  else
177  remminafile->filename = NULL;
178  g_dir_close(dir);
179 
180 }
181 
182 void remmina_file_set_filename(RemminaFile *remminafile, const gchar *filename)
183 {
184  TRACE_CALL(__func__);
185  g_free(remminafile->filename);
186  remminafile->filename = g_strdup(filename);
187 }
188 
190 {
191  TRACE_CALL(__func__);
192 
193  if (!remminafile)
194  return;
195  else
196  g_free(remminafile->statefile);
197 
198  gchar *basename = g_path_get_basename(remminafile->filename);
199  gchar *cachedir = g_build_path("/", g_get_user_cache_dir(), "remmina", NULL);
200  GString *fname = g_string_new(basename);
201 
202  remminafile->statefile = g_strdup_printf("%s/%s.state", cachedir, fname->str);
203 
204  g_free(cachedir);
205  g_string_free(fname, TRUE);
206  g_free(basename);
207 }
208 
209 const gchar *
211 {
212  TRACE_CALL(__func__);
213  return remminafile->filename;
214 }
215 
216 RemminaFile *
217 remmina_file_copy(const gchar *filename)
218 {
219  TRACE_CALL(__func__);
220  RemminaFile *remminafile;
221  gchar *buf;
222 
223  remminafile = remmina_file_load(filename);
224  buf = g_strdup_printf( "COPY %s",
225  remmina_file_get_string(remminafile, "name"));
226  remmina_file_set_string(remminafile, "name", buf);
227  g_free(buf);
228 
229  if (remminafile)
230  remmina_file_generate_filename(remminafile);
231 
232  return remminafile;
233 }
234 
235 const RemminaProtocolSetting *find_protocol_setting(const gchar *name, RemminaProtocolPlugin *protocol_plugin)
236 {
237  TRACE_CALL(__func__);
238  const RemminaProtocolSetting *setting_iter;
239 
240  if (protocol_plugin == NULL)
241  return NULL;
242 
243  setting_iter = protocol_plugin->basic_settings;
244  if (setting_iter) {
245  while (setting_iter->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
246  if (strcmp(name, remmina_plugin_manager_get_canonical_setting_name(setting_iter)) == 0)
247  return setting_iter;
248  setting_iter++;
249  }
250  }
251 
252  setting_iter = protocol_plugin->advanced_settings;
253  if (setting_iter) {
254  while (setting_iter->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
255  if (strcmp(name, remmina_plugin_manager_get_canonical_setting_name(setting_iter)) == 0)
256  return setting_iter;
257  setting_iter++;
258  }
259  }
260 
261  return NULL;
262 }
263 
264 
265 static void upgrade_sshkeys_202001_mig_common_setting(RemminaFile *remminafile, gboolean protocol_is_ssh, gboolean ssh_enabled, gchar *suffix)
266 {
267  gchar *src_key;
268  gchar *dst_key;
269  const gchar *val;
270 
271  src_key = g_strdup_printf("ssh_%s", suffix);
272  dst_key = g_strdup_printf("ssh_tunnel_%s", suffix);
273 
274  val = remmina_file_get_string(remminafile, src_key);
275  if (!val) {
276  g_free(dst_key);
277  g_free(src_key);
278  return;
279  }
280 
281  if (ssh_enabled && val && val[0] != 0)
282  remmina_file_set_string(remminafile, dst_key, val);
283 
284  if (!protocol_is_ssh)
285  remmina_file_set_string(remminafile, src_key, NULL);
286 
287  g_free(dst_key);
288  g_free(src_key);
289 }
290 
291 static void upgrade_sshkeys_202001(RemminaFile *remminafile)
292 {
293  TRACE_CALL(__func__);
294 
295  gboolean protocol_is_ssh;
296  gboolean ssh_enabled;
297  const gchar *val;
298 
299  if (remmina_file_get_string(remminafile, "ssh_enabled")) {
300  /* Upgrade ssh params from remmina pre 1.4 */
301 
302  ssh_enabled = remmina_file_get_int(remminafile, "ssh_enabled", 0);
303  val = remmina_file_get_string(remminafile, "protocol");
304  protocol_is_ssh = (strcmp(val, "SSH") == 0);
305 
306  upgrade_sshkeys_202001_mig_common_setting(remminafile, protocol_is_ssh, ssh_enabled, "stricthostkeycheck");
307  upgrade_sshkeys_202001_mig_common_setting(remminafile, protocol_is_ssh, ssh_enabled, "kex_algorithms");
308  upgrade_sshkeys_202001_mig_common_setting(remminafile, protocol_is_ssh, ssh_enabled, "hostkeytypes");
309  upgrade_sshkeys_202001_mig_common_setting(remminafile, protocol_is_ssh, ssh_enabled, "ciphers");
310  upgrade_sshkeys_202001_mig_common_setting(remminafile, protocol_is_ssh, ssh_enabled, "proxycommand");
311  upgrade_sshkeys_202001_mig_common_setting(remminafile, protocol_is_ssh, ssh_enabled, "passphrase");
312  upgrade_sshkeys_202001_mig_common_setting(remminafile, protocol_is_ssh, ssh_enabled, "auth");
313  upgrade_sshkeys_202001_mig_common_setting(remminafile, protocol_is_ssh, ssh_enabled, "privatekey");
314 
315  val = remmina_file_get_string(remminafile, "ssh_loopback");
316  if (val) {
317  remmina_file_set_string(remminafile, "ssh_tunnel_loopback", val);
318  remmina_file_set_string(remminafile, "ssh_loopback", NULL);
319  }
320 
321  val = remmina_file_get_string(remminafile, "ssh_username");
322  if (val) {
323  remmina_file_set_string(remminafile, "ssh_tunnel_username", val);
324  if (protocol_is_ssh)
325  remmina_file_set_string(remminafile, "username", val);
326  remmina_file_set_string(remminafile, "ssh_username", NULL);
327  }
328 
329  val = remmina_file_get_string(remminafile, "ssh_password");
330  if (val) {
331  remmina_file_set_string(remminafile, "ssh_tunnel_password", val);
332  if (protocol_is_ssh)
333  remmina_file_set_string(remminafile, "password", val);
334  remmina_file_set_string(remminafile, "ssh_password", NULL);
335  }
336 
337  val = remmina_file_get_string(remminafile, "ssh_server");
338  if (val) {
339  remmina_file_set_string(remminafile, "ssh_tunnel_server", val);
340  remmina_file_set_string(remminafile, "ssh_server", NULL);
341  }
342 
343  /* Real key removal will be done by remmina_file_save() */
344 
345  remmina_file_set_int(remminafile, "ssh_tunnel_enabled", ssh_enabled);
346  }
347 }
348 
349 RemminaFile *
350 remmina_file_load(const gchar *filename)
351 {
352  TRACE_CALL(__func__);
353  GKeyFile *gkeyfile;
354  RemminaFile *remminafile;
355  gchar *key;
356  gchar *s;
357  RemminaProtocolPlugin *protocol_plugin;
358  RemminaSecretPlugin *secret_plugin;
359  gboolean secret_service_available;
360  int w, h;
361 
362  gkeyfile = g_key_file_new();
363 
364  if (g_file_test(filename, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) {
365  if (!g_key_file_load_from_file(gkeyfile, filename, G_KEY_FILE_NONE, NULL)) {
366  g_key_file_free(gkeyfile);
367  REMMINA_DEBUG("Unable to load remmina profile file %s: g_key_file_load_from_file() returned NULL.\n", filename);
368  return NULL;
369  }
370  }
371 
372  if (!g_key_file_has_key(gkeyfile, KEYFILE_GROUP_REMMINA, "name", NULL)) {
373 
374  REMMINA_DEBUG("Unable to load remmina profile file %s: cannot find key name= in section remmina.\n", filename);
375  remminafile = NULL;
376  remmina_file_set_statefile(remminafile);
377 
378  g_key_file_free(gkeyfile);
379 
380  return remminafile;
381  }
382  remminafile = remmina_file_new_empty();
383 
384  protocol_plugin = NULL;
385 
386  /* Identify the protocol plugin and get pointers to its RemminaProtocolSetting structs */
387  gchar *proto = g_key_file_get_string(gkeyfile, KEYFILE_GROUP_REMMINA, "protocol", NULL);
388  if (proto) {
390  g_free(proto);
391  }
392 
394  secret_service_available = secret_plugin && secret_plugin->is_service_available(secret_plugin);
395 
396  remminafile->filename = g_strdup(filename);
397  gsize nkeys = 0;
398  gint keyindex;
399  GError *err = NULL;
400  gchar **keys = g_key_file_get_keys(gkeyfile, KEYFILE_GROUP_REMMINA, &nkeys, &err);
401  if (keys == NULL) {
402  g_clear_error(&err);
403  }
404  for (keyindex = 0; keyindex < nkeys; ++keyindex) {
405  key = keys[keyindex];
406  /* It may contain an encrypted password
407  * - password = . // secret_service
408  * - password = $argon2id$v=19$m=262144,t=3,p=… // libsodium
409  */
410  if (protocol_plugin && remmina_plugin_manager_is_encrypted_setting(protocol_plugin, key)) {
411  s = g_key_file_get_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, NULL);
412 #if 0
413  switch (remmina_pref.enc_mode) {
417 #if SODIUM_VERSION_INT >= 90200
418 #endif
419  break;
420  case RM_ENC_MODE_GCRYPT:
421  break;
422  case RM_ENC_MODE_SECRET:
423  default:
424  break;
425  }
426 #endif
427  if ((g_strcmp0(s, ".") == 0) && (secret_service_available)) {
428  gchar *sec = secret_plugin->get_password(secret_plugin, remminafile, key);
429  remmina_file_set_string(remminafile, key, sec);
430  /* Annotate in spsettings that this value comes from secret_plugin */
431  g_hash_table_insert(remminafile->spsettings, g_strdup(key), NULL);
432  g_free(sec);
433  } else {
434  gchar *decrypted;
435  decrypted = remmina_crypt_decrypt(s);
436  remmina_file_set_string(remminafile, key, decrypted);
437  g_free(decrypted);
438  }
439  g_free(s), s = NULL;
440  } else {
441  /* If we find "resolution", then we split it in two */
442  if (strcmp(key, "resolution") == 0) {
443  gchar *resolution_str = g_key_file_get_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, NULL);
444  if (remmina_public_split_resolution_string(resolution_str, &w, &h)) {
445  gchar *buf;
446  buf = g_strdup_printf("%i", w); remmina_file_set_string(remminafile, "resolution_width", buf); g_free(buf);
447  buf = g_strdup_printf("%i", h); remmina_file_set_string(remminafile, "resolution_height", buf); g_free(buf);
448  } else {
449  remmina_file_set_string(remminafile, "resolution_width", NULL);
450  remmina_file_set_string(remminafile, "resolution_height", NULL);
451  }
452  g_free(resolution_str);
453  } else {
454  gchar *value;
455  value = g_key_file_get_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, NULL);
456  remmina_file_set_string(remminafile, key, value);
457  g_free(value);
458  }
459  }
460  }
461 
462  upgrade_sshkeys_202001(remminafile);
463  g_strfreev(keys);
464  remmina_file_set_statefile(remminafile);
465  g_key_file_free(gkeyfile);
466  return remminafile;
467 }
468 
469 void remmina_file_set_string(RemminaFile *remminafile, const gchar *setting, const gchar *value)
470 {
471  TRACE_CALL(__func__);
472 
473  /* Note: setting and value are copied on the heap, so it is responsibility of the caller
474  * to deallocate them when returning from remmina_file_set_string() if needed */
475 
477  /* Allow the execution of this function from a non main thread
478  * (plugins needs it to have user credentials)*/
480  d = (RemminaMTExecData *)g_malloc(sizeof(RemminaMTExecData));
481  d->func = FUNC_FILE_SET_STRING;
482  d->p.file_set_string.remminafile = remminafile;
483  d->p.file_set_string.setting = setting;
484  d->p.file_set_string.value = value;
486  g_free(d);
487  return;
488  }
489 
490  if (value) {
491  /* We refuse to accept to set the "resolution" field */
492  if (strcmp(setting, "resolution") == 0) {
493  // TRANSLATORS: This is a message that pops up when an external Remmina plugin tries to set the window resolution using a legacy parameter.
494  const gchar *message = _("Using the «resolution» parameter in the Remmina preferences file is deprecated.\n");
495  REMMINA_CRITICAL(message);
497  return;
498  }
499  g_hash_table_insert(remminafile->settings, g_strdup(setting), g_strdup(value));
500  } else {
501  g_hash_table_insert(remminafile->settings, g_strdup(setting), g_strdup(""));
502  }
503 }
504 
505 void remmina_file_set_state(RemminaFile *remminafile, const gchar *setting, const gchar *value)
506 {
507  TRACE_CALL(__func__);
508 
509  if (value && value[0] != 0)
510  g_hash_table_insert(remminafile->states, g_strdup(setting), g_strdup(value));
511  else
512  g_hash_table_insert(remminafile->states, g_strdup(setting), g_strdup(""));
513 }
514 
515 const gchar *
516 remmina_file_get_string(RemminaFile *remminafile, const gchar *setting)
517 {
518  TRACE_CALL(__func__);
519  gchar *value;
520 
521  /* Returned value is a pointer to the string stored on the hash table,
522  * please do not free it or the hash table will contain invalid pointer */
524  /* Allow the execution of this function from a non main thread
525  * (plugins needs it to have user credentials)*/
527  const gchar *retval;
528  d = (RemminaMTExecData *)g_malloc(sizeof(RemminaMTExecData));
529  d->func = FUNC_FILE_GET_STRING;
530  d->p.file_get_string.remminafile = remminafile;
531  d->p.file_get_string.setting = setting;
533  retval = d->p.file_get_string.retval;
534  g_free(d);
535  return retval;
536  }
537 
538  if (strcmp(setting, "resolution") == 0) {
539  // TRANSLATORS: This is a message that pop-up when an external Remmina plugin tries to set the windows resolution using a legacy parameter.
540  const gchar *message = _("Using the «resolution» parameter in the Remmina preferences file is deprecated.\n");
541  REMMINA_CRITICAL(message);
543  return NULL;
544  }
545 
546  value = (gchar *)g_hash_table_lookup(remminafile->settings, setting);
547  return value && value[0] ? value : NULL;
548 }
549 
550 gchar *
551 remmina_file_get_secret(RemminaFile *remminafile, const gchar *setting)
552 {
553  TRACE_CALL(__func__);
554 
555  /* This function is in the RemminaPluginService table, we cannot remove it
556  * without breaking plugin API */
557  g_warning("remmina_file_get_secret(remminafile,“%s”) is deprecated and must not be called. Use remmina_file_get_string() and do not deallocate returned memory.\n", setting);
558  return g_strdup(remmina_file_get_string(remminafile, setting));
559 }
560 
561 gchar *remmina_file_format_properties(RemminaFile *remminafile, const gchar *setting)
562 {
563  gchar *res = NULL;
564  GString *fmt_str;
565  GDateTime *now;
566  gchar *date_str = NULL;
567 
568  fmt_str = g_string_new(setting);
569  remmina_utils_string_replace_all(fmt_str, "%h", remmina_file_get_string(remminafile, "server"));
570  remmina_utils_string_replace_all(fmt_str, "%t", remmina_file_get_string(remminafile, "ssh_tunnel_server"));
571  remmina_utils_string_replace_all(fmt_str, "%u", remmina_file_get_string(remminafile, "username"));
572  remmina_utils_string_replace_all(fmt_str, "%U", remmina_file_get_string(remminafile, "ssh_tunnel_username"));
573  remmina_utils_string_replace_all(fmt_str, "%p", remmina_file_get_string(remminafile, "name"));
574  remmina_utils_string_replace_all(fmt_str, "%g", remmina_file_get_string(remminafile, "group"));
575 
576  now = g_date_time_new_now_local();
577  date_str = g_date_time_format(now, "%FT%TZ");
578  remmina_utils_string_replace_all(fmt_str, "%d", date_str);
579  g_free(date_str);
580 
581  res = g_string_free(fmt_str, FALSE);
582  return res;
583 }
584 
585 void remmina_file_set_int(RemminaFile *remminafile, const gchar *setting, gint value)
586 {
587  TRACE_CALL(__func__);
588  if (remminafile)
589  g_hash_table_insert(remminafile->settings,
590  g_strdup(setting),
591  g_strdup_printf("%i", value));
592 }
593 
594 void remmina_file_set_state_int(RemminaFile *remminafile, const gchar *setting, gint value)
595 {
596  TRACE_CALL(__func__);
597  if (remminafile)
598  g_hash_table_insert(remminafile->states,
599  g_strdup(setting),
600  g_strdup_printf("%i", value));
601 }
602 
603 gint remmina_file_get_int(RemminaFile *remminafile, const gchar *setting, gint default_value)
604 {
605  TRACE_CALL(__func__);
606  gchar *value;
607  gint r;
608 
609  value = g_hash_table_lookup(remminafile->settings, setting);
610  r = value == NULL ? default_value : (value[0] == 't' ? TRUE : atoi(value));
611  // TOO verbose: REMMINA_DEBUG ("Integer value is: %d", r);
612  return r;
613 }
614 
615 gint remmina_file_get_state_int(RemminaFile *remminafile, const gchar *setting, gint default_value)
616 {
617  TRACE_CALL(__func__);
618  gchar *value;
619  gint r;
620 
621  value = g_hash_table_lookup(remminafile->states, setting);
622  r = value == NULL ? default_value : (value[0] == 't' ? TRUE : atoi(value));
623  // TOO verbose: REMMINA_DEBUG ("Integer value is: %d", r);
624  return r;
625 }
626 
627 // sscanf uses the set language to convert the float.
628 // therefore '.' and ',' cannot be used interchangeably.
629 gdouble remmina_file_get_double(RemminaFile * remminafile,
630  const gchar * setting,
631  gdouble default_value)
632 {
633  TRACE_CALL(__func__);
634  gchar *value;
635 
636  value = g_hash_table_lookup(remminafile->settings, setting);
637  if (!value)
638  return default_value;
639 
640  // str to double.
641  // https://stackoverflow.com/questions/10075294/converting-string-to-a-double-variable-in-c
642  gdouble d;
643  gint ret = sscanf(value, "%lf", &d);
644 
645  if (ret != 1)
646  // failed.
647  d = default_value;
648 
649  // TOO VERBOSE: REMMINA_DEBUG("Double value is: %lf", d);
650  return d;
651 }
652 
653 // sscanf uses the set language to convert the float.
654 // therefore '.' and ',' cannot be used interchangeably.
656  const gchar * setting,
657  gdouble default_value)
658 {
659  TRACE_CALL(__func__);
660  gchar *value;
661 
662  value = g_hash_table_lookup(remminafile->states, setting);
663  if (!value)
664  return default_value;
665 
666  // str to double.
667  // https://stackoverflow.com/questions/10075294/converting-string-to-a-double-variable-in-c
668  gdouble d;
669  gint ret = sscanf(value, "%lf", &d);
670 
671  if (ret != 1)
672  // failed.
673  d = default_value;
674 
675  // TOO VERBOSE: REMMINA_DEBUG("Double value is: %lf", d);
676  return d;
677 }
678 
679 static GKeyFile *
681 {
682  TRACE_CALL(__func__);
683  GKeyFile *gkeyfile;
684 
685  if (remminafile->filename == NULL)
686  return NULL;
687  gkeyfile = g_key_file_new();
688  if (!g_key_file_load_from_file(gkeyfile, remminafile->filename, G_KEY_FILE_NONE, NULL)) {
689  /* it will fail if it’s a new file, but shouldn’t matter. */
690  }
691  return gkeyfile;
692 }
693 
694 static GKeyFile *
696 {
697  TRACE_CALL(__func__);
698  GKeyFile *gkeyfile;
699 
700  if (remminafile->statefile == NULL)
701  return NULL;
702  gkeyfile = g_key_file_new();
703  if (!g_key_file_load_from_file(gkeyfile, remminafile->statefile, G_KEY_FILE_NONE, NULL)) {
704  /* it will fail if it’s a new file, but shouldn’t matter. */
705  }
706  return gkeyfile;
707 }
708 
709 void remmina_file_free(RemminaFile *remminafile)
710 {
711  TRACE_CALL(__func__);
712  if (remminafile == NULL)
713  return;
714 
715  if (remminafile->filename)
716  g_free(remminafile->filename);
717  if (remminafile->statefile)
718  g_free(remminafile->statefile);
719  if (remminafile->settings)
720  g_hash_table_destroy(remminafile->settings);
721  if (remminafile->spsettings)
722  g_hash_table_destroy(remminafile->spsettings);
723  if (remminafile->states)
724  g_hash_table_destroy(remminafile->states);
725 
726  g_free(remminafile);
727 }
728 
729 
730 void remmina_file_save(RemminaFile *remminafile)
731 {
732  TRACE_CALL(__func__);
733  RemminaSecretPlugin *secret_plugin;
734  gboolean secret_service_available;
735  RemminaProtocolPlugin *protocol_plugin;
736  GHashTableIter iter;
737  const gchar *key, *value;
738  gchar *s, *proto, *content;
739  gint nopasswdsave;
740  GKeyFile *gkeyfile;
741  GKeyFile *gkeystate;
742  gsize length = 0;
743  GError *err = NULL;
744 
745  if (remminafile->prevent_saving)
746  return;
747 
748  if ((gkeyfile = remmina_file_get_keyfile(remminafile)) == NULL)
749  return;
750 
751  if ((gkeystate = remmina_file_get_keystate(remminafile)) == NULL)
752  return;
753 
754  REMMINA_DEBUG("Saving profile");
755  /* get disablepasswordstoring */
756  nopasswdsave = remmina_file_get_int(remminafile, "disablepasswordstoring", 0);
757  /* Identify the protocol plugin and get pointers to its RemminaProtocolSetting structs */
758  proto = (gchar *)g_hash_table_lookup(remminafile->settings, "protocol");
759  if (proto) {
761  } else {
762  REMMINA_CRITICAL("Saving settings for unknown protocol:", proto);
763  protocol_plugin = NULL;
764  }
765 
767  secret_service_available = secret_plugin && secret_plugin->is_service_available(secret_plugin);
768 
769  g_hash_table_iter_init(&iter, remminafile->settings);
770  while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value)) {
771  if (remmina_plugin_manager_is_encrypted_setting(protocol_plugin, key)) {
772  if (remminafile->filename && g_strcmp0(remminafile->filename, remmina_pref_file)) {
773  if (secret_service_available && nopasswdsave == 0) {
774  REMMINA_DEBUG("We have a secret and disablepasswordstoring=0");
775  if (value && value[0]) {
776  if (g_strcmp0(value, ".") != 0)
777  secret_plugin->store_password(secret_plugin, remminafile, key, value);
778  g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, ".");
779  } else {
780  g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, "");
781  secret_plugin->delete_password(secret_plugin, remminafile, key);
782  }
783  } else {
784  REMMINA_DEBUG("We have a password and disablepasswordstoring=0");
785  if (value && value[0] && nopasswdsave == 0) {
786  s = remmina_crypt_encrypt(value);
787  g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, s);
788  g_free(s);
789  } else {
790  g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, "");
791  }
792  }
793  if (secret_service_available && nopasswdsave == 1) {
794  if (value && value[0]) {
795  if (g_strcmp0(value, ".") != 0) {
796  REMMINA_DEBUG("Deleting the secret in the keyring as disablepasswordstoring=1");
797  secret_plugin->delete_password(secret_plugin, remminafile, key);
798  g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, ".");
799  }
800  }
801  }
802  }
803  } else {
804  g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, value);
805  }
806  }
807 
808  /* Avoid storing redundant and deprecated "resolution" field */
809  g_key_file_remove_key(gkeyfile, KEYFILE_GROUP_REMMINA, "resolution", NULL);
810 
811  /* Delete old pre-1.4 ssh keys */
812  g_key_file_remove_key(gkeyfile, KEYFILE_GROUP_REMMINA, "ssh_enabled", NULL);
813  g_key_file_remove_key(gkeyfile, KEYFILE_GROUP_REMMINA, "save_ssh_server", NULL);
814  g_key_file_remove_key(gkeyfile, KEYFILE_GROUP_REMMINA, "save_ssh_username", NULL);
815 
816  /* Store gkeyfile to disk (password are already sent to keyring) */
817  content = g_key_file_to_data(gkeyfile, &length, NULL);
818 
819  if (g_file_set_contents(remminafile->filename, content, length, &err))
820  REMMINA_DEBUG("Profile saved");
821  else
822  REMMINA_WARNING("Remmina connection profile cannot be saved, with error %d (%s)", err->code, err->message);
823  if (err != NULL)
824  g_error_free(err);
825 
826  g_free(content), content = NULL;
827  /* Saving states */
828  g_hash_table_iter_init(&iter, remminafile->states);
829  while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value))
830  g_key_file_set_string(gkeyfile, KEYFILE_GROUP_STATE, key, value);
831  content = g_key_file_to_data(gkeystate, &length, NULL);
832  if (g_file_set_contents(remminafile->statefile, content, length, &err))
833  REMMINA_DEBUG("Connection profile states saved");
834  else
835  REMMINA_WARNING("Remmina connection profile cannot be saved, with error %d (%s)", err->code, err->message);
836  if (err != NULL)
837  g_error_free(err);
838  g_free(content), content = NULL;
839  g_key_file_free(gkeyfile);
840  g_key_file_free(gkeystate);
841 
844 }
845 
846 void remmina_file_store_secret_plugin_password(RemminaFile *remminafile, const gchar *key, const gchar *value)
847 {
848  TRACE_CALL(__func__);
849 
850  /* Only change the password in the keyring. This function
851  * is a shortcut which avoids updating of date/time of .pref file
852  * when possible, and is used by the mpchanger */
853  RemminaSecretPlugin *plugin;
854 
855  if (g_hash_table_lookup_extended(remminafile->spsettings, g_strdup(key), NULL, NULL)) {
857  plugin->store_password(plugin, remminafile, key, value);
858  } else {
859  remmina_file_set_string(remminafile, key, value);
860  remmina_file_save(remminafile);
861  }
862 }
863 
864 RemminaFile *
866 {
867  TRACE_CALL(__func__);
868  RemminaFile *dupfile;
869  GHashTableIter iter;
870  const gchar *key, *value;
871 
872  dupfile = remmina_file_new_empty();
873  dupfile->filename = g_strdup(remminafile->filename);
874 
875  g_hash_table_iter_init(&iter, remminafile->settings);
876  while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value))
877  remmina_file_set_string(dupfile, key, value);
878 
880  remmina_file_touch(dupfile);
881  return dupfile;
882 }
883 
884 const gchar *
886 {
887  TRACE_CALL(__func__);
888  RemminaProtocolPlugin *plugin;
889 
891  remmina_file_get_string(remminafile, "protocol"));
892  if (!plugin)
893  return g_strconcat(REMMINA_APP_ID, "-symbolic", NULL);
894 
895  return remmina_file_get_int(remminafile, "ssh_tunnel_enabled", FALSE) ? plugin->icon_name_ssh : plugin->icon_name;
896 }
897 
898 RemminaFile *
899 remmina_file_dup_temp_protocol(RemminaFile *remminafile, const gchar *new_protocol)
900 {
901  TRACE_CALL(__func__);
902  RemminaFile *tmp;
903 
904  tmp = remmina_file_dup(remminafile);
905  g_free(tmp->filename);
906  tmp->filename = NULL;
907  remmina_file_set_string(tmp, "protocol", new_protocol);
908  return tmp;
909 }
910 
911 void remmina_file_delete(const gchar *filename)
912 {
913  TRACE_CALL(__func__);
914  RemminaFile *remminafile;
915 
916  remminafile = remmina_file_load(filename);
917  if (remminafile) {
918  remmina_file_unsave_passwords(remminafile);
919  remmina_file_free(remminafile);
920  }
921  g_unlink(filename);
922 }
923 
924 const gchar *
925 remmina_file_get_state(RemminaFile *remminafile, const gchar *setting)
926 {
927  TRACE_CALL(__func__);
928  g_autoptr(GError) error = NULL;
929  g_autoptr(GKeyFile) key_file = g_key_file_new();
930 
931  if (!g_key_file_load_from_file(key_file, remminafile->statefile, G_KEY_FILE_NONE, &error)) {
932  if (!g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
933  REMMINA_CRITICAL("Could not load the state file. %s", error->message);
934  return NULL;
935  }
936 
937  g_autofree gchar *val = g_key_file_get_string(key_file, KEYFILE_GROUP_STATE, setting, &error);
938 
939  if (val == NULL &&
940  !g_error_matches(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
941  REMMINA_CRITICAL("Could not find \"%s\" in the \"%s\" state file. %s",
942  setting, remminafile->statefile, error->message);
943  return NULL;
944  }
945  return val && val[0] ? val : NULL;
946 }
947 
949 {
950  TRACE_CALL(__func__);
951 
952  g_autoptr(GKeyFile) key_statefile = g_key_file_new();
953  g_autoptr(GKeyFile) key_remminafile = g_key_file_new();
954  GError *error = NULL;
955 
956  const gchar *date = NULL;
957  GDateTime *d = g_date_time_new_now_utc();
958 
959  date = g_strdup_printf("%d%02d%02d",
960  g_date_time_get_year(d),
961  g_date_time_get_month(d),
962  g_date_time_get_day_of_month(d));
963 
964  g_key_file_set_string(key_statefile, KEYFILE_GROUP_STATE, "last_success", date);
965 
966  REMMINA_DEBUG("State file %s.", remminafile->statefile);
967  if (!g_key_file_save_to_file(key_statefile, remminafile->statefile, &error)) {
968  REMMINA_CRITICAL("Could not save the key file. %s", error->message);
969  g_error_free(error);
970  error = NULL;
971  return;
972  }
973  /* Delete old pre-1.5 keys */
974  g_key_file_remove_key(key_remminafile, KEYFILE_GROUP_REMMINA, "last_success", NULL);
975  REMMINA_DEBUG("Last connection made on %s.", date);
976 }
977 
979 {
980  /* Delete all saved secrets for this profile */
981 
982  TRACE_CALL(__func__);
983  const RemminaProtocolSetting *setting_iter;
984  RemminaProtocolPlugin *protocol_plugin;
985  gchar *proto;
986 
987  protocol_plugin = NULL;
988 
989  remmina_file_set_string(remminafile, "password", NULL);
990 
991  proto = (gchar *)g_hash_table_lookup(remminafile->settings, "protocol");
992  if (proto) {
994  if (protocol_plugin) {
995  setting_iter = protocol_plugin->basic_settings;
996  if (setting_iter) {
997  while (setting_iter->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
998  // TOO VERBOSE: g_debug("setting name: %s", setting_iter->name);
999  if (setting_iter->name == NULL)
1000  g_error("Internal error: a setting name in protocol plugin %s is null. Please fix RemminaProtocolSetting struct content.", proto);
1001  else
1002  if (remmina_plugin_manager_is_encrypted_setting(protocol_plugin, setting_iter->name))
1004  setting_iter++;
1005  }
1006  }
1007  setting_iter = protocol_plugin->advanced_settings;
1008  if (setting_iter) {
1009  while (setting_iter->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
1010  if (remmina_plugin_manager_is_encrypted_setting(protocol_plugin, setting_iter->name))
1012  setting_iter++;
1013  }
1014  }
1015  remmina_file_save(remminafile);
1016  }
1017  }
1018 }
1019 
1030 gchar *
1032 {
1033  TRACE_CALL(__func__);
1034 
1035  GFile *file;
1036  GFileInfo *info;
1037 
1038  struct timeval tv;
1039  struct tm *ptm;
1040  char time_string[256];
1041  gchar *tmps;
1042 
1043  guint64 mtime;
1044 
1045  if (remminafile->statefile)
1046  //REMMINA_DEBUG ("remminafile->statefile: %s", remminafile->statefile);
1047  file = g_file_new_for_path(remminafile->statefile);
1048  else
1049  file = g_file_new_for_path(remminafile->filename);
1050 
1051  info = g_file_query_info(file,
1052  G_FILE_ATTRIBUTE_TIME_MODIFIED,
1053  G_FILE_QUERY_INFO_NONE,
1054  NULL,
1055  NULL);
1056 
1057  g_object_unref(file);
1058 
1059  if (info == NULL) {
1060  //REMMINA_DEBUG("could not get time info");
1061 
1062  // The BDAY "Fri, 16 Oct 2009 07:04:46 GMT"
1063  mtime = 1255676686;
1064  const gchar *last_success = remmina_file_get_string(remminafile, "last_success");
1065  if (last_success) {
1066  //REMMINA_DEBUG ("Last success is %s", last_success);
1067  GDateTime *dt;
1068  tmps = g_strconcat(last_success, "T00:00:00Z", NULL);
1069  dt = g_date_time_new_from_iso8601(tmps, NULL);
1070  g_free(tmps);
1071  if (dt) {
1072  //REMMINA_DEBUG("Converting last_success");
1073  tmps = g_date_time_format(dt, "%s");
1074  mtime = g_ascii_strtoull(tmps, NULL, 10);
1075  g_free(tmps);
1076  g_date_time_unref(dt);
1077  } else {
1078  //REMMINA_DEBUG("dt was null");
1079  mtime = 191543400;
1080  }
1081  }
1082  } else {
1083  mtime = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
1084  g_object_unref(info);
1085  }
1086 
1087  tv.tv_sec = mtime;
1088 
1089  ptm = localtime(&tv.tv_sec);
1090  strftime(time_string, sizeof(time_string), "%F - %T", ptm);
1091 
1092  gchar *modtime_string = g_locale_to_utf8(time_string, -1, NULL, NULL, NULL);
1093 
1094  return modtime_string;
1095 }
1096 
1104 void
1106 {
1107  TRACE_CALL(__func__);
1108  int fd;
1109  struct stat st;
1110  int r;
1111 
1112  if ((r = stat(remminafile->statefile, &st)) < 0) {
1113  if (errno != ENOENT)
1114  REMMINA_DEBUG("stat %s:", remminafile->statefile);
1115  } else if (!r) {
1116 #ifdef __APPLE__
1117  times[0] = st.st_atimespec;
1118  times[1] = st.st_mtimespec;
1119 #else
1120  times[0] = st.st_atim;
1121  times[1] = st.st_mtim;
1122 #endif
1123  if (utimensat(AT_FDCWD, remminafile->statefile, times, 0) < 0)
1124  REMMINA_DEBUG("utimensat %s:", remminafile->statefile);
1125  return;
1126  }
1127 
1128  if ((fd = open(remminafile->statefile, O_CREAT | O_EXCL, 0644)) < 0)
1129  REMMINA_DEBUG("open %s:", remminafile->statefile);
1130  close(fd);
1131 
1132  remmina_file_touch(remminafile);
1133 }
RemminaFile * remmina_file_load(const gchar *filename)
Definition: remmina_file.c:350
-
const gchar * remmina_plugin_manager_get_canonical_setting_name(const RemminaProtocolSetting *setting)
+
const gchar * remmina_plugin_manager_get_canonical_setting_name(const RemminaProtocolSetting *setting)
void remmina_file_free(RemminaFile *remminafile)
Definition: remmina_file.c:709
-
gboolean remmina_plugin_manager_is_encrypted_setting(RemminaProtocolPlugin *pp, const char *setting)
+
gboolean remmina_plugin_manager_is_encrypted_setting(RemminaProtocolPlugin *pp, const char *setting)
const gchar * remmina_file_get_string(RemminaFile *remminafile, const gchar *setting)
Definition: remmina_file.c:516
@@ -107,7 +107,7 @@ $(document).ready(function(){initNavTree('remmina__file_8c_source.html','');}); -
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
+
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
gboolean(* is_service_available)(struct _RemminaSecretPlugin *instance)
Definition: plugin.h:144
void(* delete_password)(struct _RemminaSecretPlugin *instance, RemminaFile *remminafile, const gchar *key)
Definition: plugin.h:147
gchar * remmina_file_get_datetime(RemminaFile *remminafile)
Return the string date of the last time a Remmina state file has been modified.
@@ -159,7 +159,7 @@ $(document).ready(function(){initNavTree('remmina__file_8c_source.html','');});
guint remmina_utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
Replaces all occurrences of needle in haystack with replace.
void remmina_file_store_secret_plugin_password(RemminaFile *remminafile, const gchar *key, const gchar *value)
Definition: remmina_file.c:846
-
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
+
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
const RemminaProtocolSetting * advanced_settings
Definition: plugin.h:75
diff --git a/public/remmina__file__editor_8c_source.html b/public/remmina__file__editor_8c_source.html index 987cbf894..482957323 100644 --- a/public/remmina__file__editor_8c_source.html +++ b/public/remmina__file__editor_8c_source.html @@ -88,7 +88,7 @@ $(document).ready(function(){initNavTree('remmina__file__editor_8c_source.html',
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-2023 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 <ctype.h>
38 #include "config.h"
39 #ifdef HAVE_LIBAVAHI_UI
40 #include <avahi-ui/avahi-ui.h>
41 #endif
42 #include "remmina_public.h"
43 #include "remmina_pref.h"
44 #include "rcw.h"
46 #include "remmina_file.h"
47 #include "remmina_file_editor.h"
48 #include "remmina_file_manager.h"
49 #include "remmina_icon.h"
50 #include "remmina_main.h"
51 #include "remmina_plugin_manager.h"
52 #include "remmina_pref_dialog.h"
53 #include "remmina_ssh.h"
54 #include "remmina_string_list.h"
55 #include "remmina_unlock.h"
56 #include "remmina_widget_pool.h"
57 
58 G_DEFINE_TYPE(RemminaFileEditor, remmina_file_editor, GTK_TYPE_DIALOG)
59 
60 static const gchar *server_tips = N_("<big>"
61  "Supported formats\n"
62  "• server\n"
63  "• server[:port]\n"
64  "VNC additional formats\n"
65  "• ID:repeater ID number\n"
66  "• unix:///path/socket.sock"
67  "</big>");
68 
69 static const gchar *cmd_tips = N_("<big>"
70  "• command in PATH args %h\n"
71  "• /path/to/foo -options %h %u\n"
72  "• %h is substituted with the server name\n"
73  "• %t is substituted with the SSH server name\n"
74  "• %u is substituted with the username\n"
75  "• %U is substituted with the SSH username\n"
76  "• %p is substituted with Remmina profile name\n"
77  "• %g is substituted with Remmina profile group name\n"
78  "• %d is substituted with local date and time in ISO 8601 format\n"
79  "Do not run in background if you want the command to be executed before connecting.\n"
80  "</big>");
81 
82 #ifdef HAVE_LIBSSH
83 static const gchar *server_tips2 = N_("<big>"
84  "Supported formats\n"
85  "• server\n"
86  "• server[:port]\n"
87  "• username@server[:port] (SSH protocol only)"
88  "</big>");
89 #endif
90 
91 struct _RemminaFileEditorPriv {
92  RemminaFile * remmina_file;
93  RemminaProtocolPlugin * plugin;
94  const gchar * avahi_service_type;
95 
96  GtkWidget * name_entry;
97  GtkWidget * labels_entry;
98  GtkWidget * group_combo;
99  GtkWidget * protocol_combo;
100  GtkWidget * save_button;
101 
102  GtkWidget * config_box;
103  GtkWidget * config_scrollable;
104  GtkWidget * config_viewport;
105  GtkWidget * config_container;
106 
107  GtkWidget * server_combo;
108  GtkWidget * resolution_iws_radio;
109  GtkWidget * resolution_auto_radio;
110  GtkWidget * resolution_custom_radio;
111  GtkWidget * resolution_custom_combo;
112  GtkWidget * keymap_combo;
113 
114  GtkWidget * behavior_autostart_check;
115  GtkWidget * behavior_precommand_entry;
116  GtkWidget * behavior_postcommand_entry;
117  GtkWidget * behavior_lock_check;
118 
119  GtkWidget * ssh_tunnel_enabled_check;
120  GtkWidget * ssh_tunnel_loopback_check;
121  GtkWidget * ssh_tunnel_server_default_radio;
122  GtkWidget * ssh_tunnel_server_custom_radio;
123  GtkWidget * ssh_tunnel_server_entry;
124  GtkWidget * ssh_tunnel_auth_agent_radio;
125  GtkWidget * ssh_tunnel_auth_password_radio;
126  GtkWidget * ssh_tunnel_auth_password;
127  GtkWidget * ssh_tunnel_passphrase;
128  GtkWidget * ssh_tunnel_auth_publickey_radio;
129  GtkWidget * ssh_tunnel_auth_auto_publickey_radio;
130  GtkWidget * ssh_tunnel_auth_combo;
131  GtkWidget * ssh_tunnel_username_entry;
132  GtkWidget * ssh_tunnel_privatekey_chooser;
133  GtkWidget * ssh_tunnel_certfile_chooser;
134 
135  GHashTable * setting_widgets;
136 };
137 
139 {
140  TRACE_CALL(__func__);
141 }
142 
159  GtkWidget * failed_widget,
160  GError * err)
161 {
162  if (!err) {
163  err = NULL; // g_set_error doesn't like overwriting errors.
164  g_set_error(&err, 1, 1, _("Input is invalid."));
165  }
166 
167  if (!gfe || !failed_widget) {
168  g_critical("(%s): Parameters RemminaFileEditor 'gfe' or "
169  "GtkWidget* 'failed_widget' are 'NULL'!",
170  __func__);
171  return;
172  }
173 
174  gint widget_width = gtk_widget_get_allocated_width(failed_widget);
175  gint widget_height = gtk_widget_get_allocated_height(failed_widget);
176 
177  GtkWidget *err_label = gtk_label_new("");
178  GtkWidget *alert_icon = NULL;
179  GtkWindow *err_window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
180  GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
181  GdkWindow *window = gtk_widget_get_window(failed_widget);
182 
183  GtkAllocation allocation;
184  gint failed_widget_x, failed_widget_y;
185 
186  gchar *markup = g_strdup_printf("<span size='large'>%s</span>", err->message);
187 
188  // Setup err_window
189  gtk_window_set_decorated(err_window, FALSE);
190  gtk_window_set_type_hint(err_window, GDK_WINDOW_TYPE_HINT_TOOLTIP);
191  gtk_window_set_default_size(err_window, widget_width, widget_height);
192  gtk_window_set_title(err_window, "Error");
193  gtk_window_set_resizable(err_window, TRUE);
194 
195  // Move err_window under failed_widget
196  gtk_window_set_attached_to(err_window, failed_widget);
197  gtk_window_set_transient_for(err_window, GTK_WINDOW(gfe));
198  gdk_window_get_origin(GDK_WINDOW(window), &failed_widget_x, &failed_widget_y);
199  gtk_widget_get_allocation(failed_widget, &allocation);
200  failed_widget_x += allocation.x;
201  failed_widget_y += allocation.y + allocation.height;
202  gtk_window_move(err_window, failed_widget_x, failed_widget_y);
203 
204  // Setup label
205  gtk_label_set_selectable(GTK_LABEL(err_label), FALSE);
206  gtk_label_set_max_width_chars(GTK_LABEL(err_label), 1);
207  gtk_widget_set_hexpand(GTK_WIDGET(err_label), TRUE);
208  gtk_widget_set_vexpand(GTK_WIDGET(err_label), TRUE);
209  gtk_label_set_ellipsize(GTK_LABEL(err_label), PANGO_ELLIPSIZE_END);
210  gtk_label_set_line_wrap(GTK_LABEL(err_label), TRUE);
211  gtk_label_set_line_wrap_mode(GTK_LABEL(err_label), PANGO_WRAP_WORD_CHAR);
212  gtk_label_set_markup(GTK_LABEL(err_label), markup);
213 
214  alert_icon = gtk_image_new_from_icon_name("dialog-warning-symbolic",
215  GTK_ICON_SIZE_DND);
216 
217  // Fill icon and label into a box.
218  gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(alert_icon), FALSE, FALSE, 0);
219  gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(err_label), TRUE, TRUE, 5);
220 
221  // Attach box to err_window
222  gtk_container_add(GTK_CONTAINER(err_window), GTK_WIDGET(box));
223 
224  // Display everything.
225  gtk_widget_show_all(GTK_WIDGET(err_window));
226 
227  // Mouse click and focus-loss will delete the err_window.
228  g_signal_connect(G_OBJECT(err_window), "focus-out-event",
229  G_CALLBACK(gtk_window_close), NULL);
230  g_signal_connect(G_OBJECT(err_window), "button-press-event",
231  G_CALLBACK(gtk_window_close), NULL);
232 }
233 
234 #ifdef HAVE_LIBAVAHI_UI
235 
236 static void remmina_file_editor_browse_avahi(GtkWidget *button, RemminaFileEditor *gfe)
237 {
238  TRACE_CALL(__func__);
239  GtkWidget *dialog;
240  gchar *host;
241 
242  dialog = aui_service_dialog_new(_("Choose a Remote Desktop Server"),
243  GTK_WINDOW(gfe),
244  _("_Cancel"), GTK_RESPONSE_CANCEL,
245  _("_OK"), GTK_RESPONSE_ACCEPT,
246  NULL);
247 
248  gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gfe));
249  aui_service_dialog_set_resolve_service(AUI_SERVICE_DIALOG(dialog), TRUE);
250  aui_service_dialog_set_resolve_host_name(AUI_SERVICE_DIALOG(dialog), TRUE);
251  aui_service_dialog_set_browse_service_types(AUI_SERVICE_DIALOG(dialog),
252  gfe->priv->avahi_service_type, NULL);
253 
254  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
255  host = g_strdup_printf("[%s]:%i",
256  aui_service_dialog_get_host_name(AUI_SERVICE_DIALOG(dialog)),
257  aui_service_dialog_get_port(AUI_SERVICE_DIALOG(dialog)));
258  } else {
259  host = NULL;
260  }
261  gtk_widget_destroy(dialog);
262 
263  if (host) {
264  gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(gfe->priv->server_combo))), host);
265  g_free(host);
266  }
267 }
268 #endif
269 
270 static void remmina_file_editor_on_realize(GtkWidget *widget, gpointer user_data)
271 {
272  TRACE_CALL(__func__);
273  RemminaFileEditor *gfe;
274  GtkWidget *defaultwidget;
275 
276  gfe = REMMINA_FILE_EDITOR(widget);
277 
278  defaultwidget = gfe->priv->server_combo;
279 
280  if (defaultwidget) {
281  if (GTK_IS_EDITABLE(defaultwidget))
282  gtk_editable_select_region(GTK_EDITABLE(defaultwidget), 0, -1);
283  gtk_widget_grab_focus(defaultwidget);
284  }
285 }
286 
287 static void remmina_file_editor_destroy(GtkWidget *widget, gpointer data)
288 {
289  TRACE_CALL(__func__);
290  remmina_file_free(REMMINA_FILE_EDITOR(widget)->priv->remmina_file);
291  g_hash_table_destroy(REMMINA_FILE_EDITOR(widget)->priv->setting_widgets);
292  g_free(REMMINA_FILE_EDITOR(widget)->priv);
293 }
294 
295 static void remmina_file_editor_button_on_toggled(GtkToggleButton *togglebutton, GtkWidget *widget)
296 {
297  TRACE_CALL(__func__);
298  gtk_widget_set_sensitive(widget, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)));
299 }
300 
302 {
303  TRACE_CALL(__func__);
304  /* Create the notebook */
305  gfe->priv->config_container = gtk_notebook_new();
306  gfe->priv->config_viewport = gtk_viewport_new(NULL, NULL);
307  gfe->priv->config_scrollable = gtk_scrolled_window_new(NULL, NULL);
308  gtk_container_set_border_width(GTK_CONTAINER(gfe->priv->config_scrollable), 2);
309  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gfe->priv->config_scrollable),
310  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
311  gtk_widget_show(gfe->priv->config_scrollable);
312 
313  gtk_container_add(GTK_CONTAINER(gfe->priv->config_viewport), gfe->priv->config_container);
314  gtk_container_set_border_width(GTK_CONTAINER(gfe->priv->config_viewport), 2);
315  gtk_widget_show(gfe->priv->config_viewport);
316  gtk_container_add(GTK_CONTAINER(gfe->priv->config_scrollable), gfe->priv->config_viewport);
317  gtk_container_set_border_width(GTK_CONTAINER(gfe->priv->config_container), 2);
318  gtk_widget_show(gfe->priv->config_container);
319 
320  gtk_container_add(GTK_CONTAINER(gfe->priv->config_box), gfe->priv->config_scrollable);
321 }
322 
324  const gchar *stock_id, const gchar *label, gint rows, gint cols)
325 {
326  TRACE_CALL(__func__);
327  GtkWidget *tablabel;
328  GtkWidget *tabbody;
329  GtkWidget *grid;
330  GtkWidget *widget;
331 
332  tablabel = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
333  gtk_widget_show(tablabel);
334 
335  widget = gtk_image_new_from_icon_name(stock_id, GTK_ICON_SIZE_BUTTON);
336  gtk_box_pack_start(GTK_BOX(tablabel), widget, FALSE, FALSE, 0);
337  gtk_widget_show(widget);
338 
339  widget = gtk_label_new(label);
340  gtk_box_pack_start(GTK_BOX(tablabel), widget, FALSE, FALSE, 0);
341  gtk_widget_show(widget);
342 
343  tabbody = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
344  gtk_widget_show(tabbody);
345  gtk_notebook_append_page(GTK_NOTEBOOK(gfe->priv->config_container), tabbody, tablabel);
346 
347  grid = gtk_grid_new();
348  gtk_widget_show(grid);
349  gtk_grid_set_row_spacing(GTK_GRID(grid), 8);
350  gtk_grid_set_column_spacing(GTK_GRID(grid), 8);
351  gtk_container_set_border_width(GTK_CONTAINER(grid), 15);
352  gtk_box_pack_start(GTK_BOX(tabbody), grid, FALSE, FALSE, 0);
353 
354  return grid;
355 }
356 
357 #ifdef HAVE_LIBSSH
358 
360 {
361  TRACE_CALL(__func__);
362  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_server_entry),
363  gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_enabled_check)) &&
364  (gfe->priv->ssh_tunnel_server_custom_radio == NULL ||
365  gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_server_custom_radio))));
366 }
367 
368 static void remmina_file_editor_ssh_tunnel_enabled_check_on_toggled(GtkToggleButton *togglebutton,
370 {
371  TRACE_CALL(__func__);
372  RemminaFileEditorPriv *priv = gfe->priv;
373  gboolean enabled = TRUE;
374  gchar *p;
375  const gchar *cp;
376  const gchar *s = NULL;
377 
378  if (gfe->priv->ssh_tunnel_enabled_check) {
379  enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_enabled_check));
380  if (gfe->priv->ssh_tunnel_loopback_check)
381  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_loopback_check), enabled);
382  if (gfe->priv->ssh_tunnel_server_default_radio)
383  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_server_default_radio), enabled);
384  if (gfe->priv->ssh_tunnel_server_custom_radio)
385  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_server_custom_radio), enabled);
387  p = remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->protocol_combo));
388  // if (!(g_strcmp0(p, "SFTP") == 0 || g_strcmp0(p, "SSH") == 0)) {
389  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_username_entry), enabled);
390  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_auth_password), enabled);
391  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_auth_combo), enabled);
392  //}
393  g_free(p);
394  }
395  // remmina_file_editor_ssh_tunnel_auth_publickey_radio_on_toggled(NULL, gfe);
396  s = remmina_file_get_string(gfe->priv->remmina_file, "ssh_tunnel_privatekey");
397  if (s)
398  gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(gfe->priv->ssh_tunnel_privatekey_chooser), s);
399  s = remmina_file_get_string(gfe->priv->remmina_file, "ssh_tunnel_certfile");
400  if (s)
401  gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(gfe->priv->ssh_tunnel_certfile_chooser), s);
402 
403  if (gfe->priv->ssh_tunnel_username_entry)
404  if (enabled && gtk_entry_get_text(GTK_ENTRY(gfe->priv->ssh_tunnel_username_entry))[0] == '\0') {
405  cp = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_username");
406  gtk_entry_set_text(GTK_ENTRY(gfe->priv->ssh_tunnel_username_entry), cp ? cp : "");
407  }
408 
409  if (gfe->priv->ssh_tunnel_auth_password) {
410  if (enabled && gtk_entry_get_text(GTK_ENTRY(gfe->priv->ssh_tunnel_auth_password))[0] == '\0') {
411  cp = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_password");
412  gtk_entry_set_text(GTK_ENTRY(gfe->priv->ssh_tunnel_auth_password), cp ? cp : "");
413  }
414  }
415  if (gfe->priv->ssh_tunnel_passphrase) {
416  if (enabled && gtk_entry_get_text(GTK_ENTRY(gfe->priv->ssh_tunnel_passphrase))[0] == '\0') {
417  cp = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_passphrase");
418  gtk_entry_set_text(GTK_ENTRY(gfe->priv->ssh_tunnel_passphrase), cp ? cp : "");
419  }
420  }
421 }
422 
423 #endif
424 
425 static void remmina_file_editor_create_server(RemminaFileEditor *gfe, const RemminaProtocolSetting *setting, GtkWidget *grid,
426  gint row)
427 {
428  TRACE_CALL(__func__);
429  RemminaProtocolPlugin *plugin = gfe->priv->plugin;
430  GtkWidget *widget;
431 #ifdef HAVE_LIBAVAHI_UI
432  GtkWidget *hbox;
433 #endif
434  gchar *s;
435 
436  widget = gtk_label_new(_("Server"));
437  gtk_widget_show(widget);
438  gtk_widget_set_valign(widget, GTK_ALIGN_START);
439  gtk_widget_set_halign(widget, GTK_ALIGN_START);
440  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, row + 1);
441 
442  s = remmina_pref_get_recent(plugin->name);
443  widget = remmina_public_create_combo_entry(s, remmina_file_get_string(gfe->priv->remmina_file, "server"), TRUE);
444  gtk_widget_set_hexpand(widget, TRUE);
445  gtk_widget_show(widget);
446  gtk_widget_set_tooltip_markup(widget, _(server_tips));
447  gtk_entry_set_activates_default(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(widget))), TRUE);
448  gfe->priv->server_combo = widget;
449  g_free(s);
450 
451 #ifdef HAVE_LIBAVAHI_UI
452  if (setting->opt1) {
453  gfe->priv->avahi_service_type = (const gchar *)setting->opt1;
454 
455  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
456  gtk_widget_show(hbox);
457  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
458 
459  widget = gtk_button_new_with_label("…");
460  s = g_strdup_printf(_("Browse the network to find a %s server"), plugin->name);
461  gtk_widget_set_tooltip_text(widget, s);
462  g_free(s);
463  gtk_widget_show(widget);
464  gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
465  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_browse_avahi), gfe);
466 
467  gtk_grid_attach(GTK_GRID(grid), hbox, 1, row, 1, 1);
468  } else
469 #endif
470  {
471  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1);
472  }
473 }
474 
475 
476 static GtkWidget *remmina_file_editor_create_password(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gchar *setting_name)
477 {
478  TRACE_CALL(__func__);
479  GtkWidget *widget;
480 
481  widget = gtk_label_new(label);
482  gtk_widget_show(widget);
483 #if GTK_CHECK_VERSION(3, 12, 0)
484  gtk_widget_set_margin_end(widget, 40);
485 #else
486  gtk_widget_set_margin_right(widget, 40);
487 #endif
488  gtk_widget_set_valign(widget, GTK_ALIGN_START);
489  gtk_widget_set_halign(widget, GTK_ALIGN_START);
490  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
491 
492  widget = gtk_entry_new();
493  gtk_widget_show(widget);
494  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1);
495  gtk_entry_set_max_length(GTK_ENTRY(widget), 0);
496  gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
497  gtk_widget_set_hexpand(widget, TRUE);
498  gtk_entry_set_activates_default(GTK_ENTRY(widget), TRUE);
499  if (setting_name)
500  gtk_widget_set_name(widget, setting_name);
501 
502  if (value)
503  gtk_entry_set_text(GTK_ENTRY(widget), value);
504  /* Password view Toogle*/
505  if (setting_name) {
506  gtk_entry_set_icon_from_icon_name(GTK_ENTRY(widget), GTK_ENTRY_ICON_SECONDARY, "org.remmina.Remmina-password-reveal-symbolic");
507  gtk_entry_set_icon_activatable(GTK_ENTRY(widget), GTK_ENTRY_ICON_SECONDARY, TRUE);
508  g_signal_connect(widget, "icon-press", G_CALLBACK(remmina_main_toggle_password_view), NULL);
509  }
510  return widget;
511 }
512 
513 static void remmina_file_editor_update_resolution(GtkWidget *widget, RemminaFileEditor *gfe)
514 {
515  TRACE_CALL(__func__);
516  gchar *res_str;
517  res_str = g_strdup_printf("%dx%d",
518  remmina_file_get_int(gfe->priv->remmina_file, "resolution_width", 0),
519  remmina_file_get_int(gfe->priv->remmina_file, "resolution_height", 0));
520  remmina_public_load_combo_text_d(gfe->priv->resolution_custom_combo, remmina_pref.resolutions,
521  res_str, NULL);
522  g_free(res_str);
523 }
524 
525 static void remmina_file_editor_browse_resolution(GtkWidget *button, RemminaFileEditor *gfe)
526 {
527  TRACE_CALL(__func__);
528 
529  GtkDialog *dialog = remmina_string_list_new(FALSE, NULL);
532  remmina_string_list_set_titles(_("Resolutions"), _("Configure the available resolutions"));
533  gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gfe));
534  gtk_dialog_run(dialog);
535  g_free(remmina_pref.resolutions);
537  g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(remmina_file_editor_update_resolution), gfe);
538  gtk_widget_destroy(GTK_WIDGET(dialog));
539 }
540 
542  GtkWidget *grid, gint row)
543 {
544  TRACE_CALL(__func__);
545  GtkWidget *widget;
546  GtkWidget *hbox;
547  int resolution_w, resolution_h;
548  gchar *res_str;
550 
551  res_mode = remmina_file_get_int(gfe->priv->remmina_file, "resolution_mode", RES_INVALID);
552  resolution_w = remmina_file_get_int(gfe->priv->remmina_file, "resolution_width", -1);
553  resolution_h = remmina_file_get_int(gfe->priv->remmina_file, "resolution_height", -1);
554 
555  /* If resolution_mode is non-existent (-1), then we try to calculate it
556  * as we did before having resolution_mode */
557  if (res_mode == RES_INVALID) {
558  if (resolution_w <= 0 || resolution_h <= 0)
559  res_mode = RES_USE_INITIAL_WINDOW_SIZE;
560  else
561  res_mode = RES_USE_CUSTOM;
562  }
563  if (res_mode == RES_USE_CUSTOM)
564  res_str = g_strdup_printf("%dx%d", resolution_w, resolution_h);
565  else
566  res_str = NULL;
567 
568  widget = gtk_label_new(_("Resolution"));
569  gtk_widget_show(widget);
570  gtk_widget_set_valign(widget, GTK_ALIGN_START);
571  gtk_widget_set_halign(widget, GTK_ALIGN_START);
572  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
573 
574  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
575  widget = gtk_radio_button_new_with_label(NULL, _("Use initial window size"));
576  gtk_widget_show(widget);
577  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
578  gfe->priv->resolution_iws_radio = widget;
579  widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(gfe->priv->resolution_iws_radio), _("Use client resolution"));
580  gtk_widget_show(widget);
581  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
582  gfe->priv->resolution_auto_radio = widget;
583  gtk_grid_attach(GTK_GRID(grid), hbox, 1, row, 1, 1);
584  gtk_widget_show(hbox);
585 
586  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
587  gtk_widget_show(hbox);
588  gtk_grid_attach(GTK_GRID(grid), hbox, 1, row + 1, 1, 1);
589 
590  widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(gfe->priv->resolution_iws_radio), _("Custom"));
591  gtk_widget_show(widget);
592  gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
593  gfe->priv->resolution_custom_radio = widget;
594 
596  gtk_widget_show(widget);
597  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
598  gfe->priv->resolution_custom_combo = widget;
599 
600  widget = gtk_button_new_with_label("…");
601  gtk_widget_show(widget);
602  gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
603  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_browse_resolution), gfe);
604 
605  g_signal_connect(G_OBJECT(gfe->priv->resolution_custom_radio), "toggled",
606  G_CALLBACK(remmina_file_editor_button_on_toggled), gfe->priv->resolution_custom_combo);
607 
608  if (res_mode == RES_USE_CUSTOM)
609  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_custom_radio), TRUE);
610  else if (res_mode == RES_USE_CLIENT)
611  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_auto_radio), TRUE);
612  else
613  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_iws_radio), TRUE);
614 
615  gtk_widget_set_sensitive(gfe->priv->resolution_custom_combo, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_custom_radio)));
616 
617  g_free(res_str);
618 }
619 
620 static GtkWidget *remmina_file_editor_create_text2(RemminaFileEditor *gfe, GtkWidget *grid,
621  gint row, gint col, const gchar *label, const gchar *value, gint left,
622  gint right, gchar *setting_name)
623 {
624  TRACE_CALL(__func__);
625  GtkWidget *widget;
626 
627  widget = gtk_label_new(label);
628  gtk_widget_show(widget);
629 #if GTK_CHECK_VERSION(3, 12, 0)
630  gtk_widget_set_margin_start(widget, left);
631  gtk_widget_set_margin_end(widget, right);
632 #else
633  gtk_widget_set_margin_left(widget, left);
634  gtk_widget_set_margin_right(widget, right);
635 #endif
636  gtk_widget_set_valign(widget, GTK_ALIGN_START);
637  gtk_widget_set_halign(widget, GTK_ALIGN_START);
638  gtk_grid_attach(GTK_GRID(grid), widget, col, row, 1, 1);
639 
640  widget = gtk_entry_new();
641  gtk_widget_show(widget);
642  gtk_grid_attach(GTK_GRID(grid), widget, col + 1, row, 1, 1);
643  gtk_entry_set_max_length(GTK_ENTRY(widget), 300);
644  gtk_widget_set_hexpand(widget, TRUE);
645  if (setting_name)
646  gtk_widget_set_name(widget, setting_name);
647 
648  if (value)
649  gtk_entry_set_text(GTK_ENTRY(widget), value);
650 
651  return widget;
652 }
653 
654 static GtkWidget *remmina_file_editor_create_text(RemminaFileEditor *gfe, GtkWidget *grid,
655  gint row, gint col, const gchar *label, const gchar *value,
656  gchar *setting_name)
657 {
658  TRACE_CALL(__func__);
659  return remmina_file_editor_create_text2(gfe, grid, row, col, label, value, 0, 40,
660  setting_name);
661 }
662 
663 static GtkWidget *remmina_file_editor_create_textarea(RemminaFileEditor *gfe, GtkWidget *grid,
664  gint row, gint col, const gchar *label, const gchar *value,
665  gchar *setting_name)
666 {
667  TRACE_CALL(__func__);
668  GtkWidget *widget;
669  GtkTextView *view;
670  GtkTextBuffer *buffer;
671  GtkTextIter start;
672 
673  widget = gtk_text_view_new();
674  view = GTK_TEXT_VIEW(widget);
675  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
676  gtk_text_view_set_top_margin(GTK_TEXT_VIEW(view), 20);
677  gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(view), 20);
678  gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 20);
679  gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 20);
680  gtk_text_view_set_monospace(view, TRUE);
681  if (setting_name)
682  gtk_widget_set_name(widget, setting_name);
683  if (value) {
684  buffer = gtk_text_view_get_buffer(view);
685  gtk_text_buffer_set_text(buffer, value, -1);
686  gtk_text_buffer_get_start_iter(buffer, &start);
687  gtk_text_buffer_place_cursor(buffer, &start);
688  }
689  gtk_widget_show(widget);
690  gtk_widget_set_hexpand(widget, TRUE);
691  gtk_widget_set_size_request(GTK_WIDGET(view), 320, 300);
692  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
693  return widget;
694 }
695 
696 static GtkWidget *remmina_file_editor_create_select(RemminaFileEditor *gfe, GtkWidget *grid,
697  gint row, gint col, const gchar *label, const gpointer *list,
698  const gchar *value, gchar *setting_name)
699 {
700  TRACE_CALL(__func__);
701  GtkWidget *widget;
702 
703  widget = gtk_label_new(label);
704  gtk_widget_show(widget);
705  gtk_widget_set_valign(widget, GTK_ALIGN_START);
706  gtk_widget_set_halign(widget, GTK_ALIGN_START);
707  if (setting_name)
708  gtk_widget_set_name(widget, setting_name);
709  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
710 
711  widget = remmina_public_create_combo_map(list, value, FALSE, gfe->priv->plugin->domain);
712  gtk_widget_show(widget);
713  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1);
714 
715  return widget;
716 }
717 
718 static GtkWidget *remmina_file_editor_create_combo(RemminaFileEditor *gfe, GtkWidget *grid,
719  gint row, gint col, const gchar *label, const gchar *list,
720  const gchar *value, gchar *setting_name)
721 {
722  TRACE_CALL(__func__);
723  GtkWidget *widget;
724 
725  widget = gtk_label_new(label);
726  gtk_widget_show(widget);
727  gtk_widget_set_valign(widget, GTK_ALIGN_START);
728  gtk_widget_set_halign(widget, GTK_ALIGN_START);
729  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
730 
731  widget = remmina_public_create_combo_entry(list, value, FALSE);
732  gtk_widget_show(widget);
733  gtk_widget_set_hexpand(widget, TRUE);
734  if (setting_name)
735  gtk_widget_set_name(widget, setting_name);
736  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1);
737 
738  return widget;
739 }
740 
741 static GtkWidget *remmina_file_editor_create_check(RemminaFileEditor *gfe, GtkWidget *grid,
742  gint row, gint top, const gchar *label, gboolean value,
743  gchar *setting_name)
744 {
745  TRACE_CALL(__func__);
746  GtkWidget *widget;
747  widget = gtk_check_button_new_with_label(label);
748  gtk_widget_show(widget);
749  if (setting_name)
750  gtk_widget_set_name(widget, setting_name);
751  gtk_grid_attach(GTK_GRID(grid), widget, top, row, 1, 1);
752 
753  if (value)
754  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
755 
756  return widget;
757 }
758 
768 static GtkWidget *
769 remmina_file_editor_create_chooser(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label,
770  const gchar *value, gint type, gchar *setting_name)
771 {
772  TRACE_CALL(__func__);
773  GtkWidget *check;
774  GtkWidget *widget;
775  GtkWidget *hbox;
776 
777  widget = gtk_label_new(label);
778  gtk_widget_show(widget);
779  gtk_widget_set_valign(widget, GTK_ALIGN_START);
780  gtk_widget_set_halign(widget, GTK_ALIGN_START);
781  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
782 
783  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
784  gtk_widget_show(hbox);
785  gtk_grid_attach(GTK_GRID(grid), hbox, 1, row, 1, 1);
786 
787  check = gtk_check_button_new();
788  gtk_widget_show(check);
789  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), (value && value[0] == '/'));
790  gtk_box_pack_start(GTK_BOX(hbox), check, FALSE, FALSE, 0);
791 
792  widget = gtk_file_chooser_button_new(label, type);
793  if (setting_name)
794  gtk_widget_set_name(widget, setting_name);
795  gtk_widget_show(widget);
796  if (value)
797  gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(widget), value);
798  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
799 
800  g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(remmina_file_editor_button_on_toggled), widget);
801  remmina_file_editor_button_on_toggled(GTK_TOGGLE_BUTTON(check), widget);
802 
803  return widget;
804 }
805 
806 // used to filter out invalid characters for REMMINA_PROTOCOL_SETTING_TYPE_INT
807 void remmina_file_editor_int_setting_filter(GtkEditable *editable, const gchar *text,
808  gint length, gint *position, gpointer data)
809 {
810  for (int i = 0; i < length; i++) {
811  if (!isdigit(text[i]) && text[i] != '-') {
812  g_signal_stop_emission_by_name(G_OBJECT(editable), "insert-text");
813  return;
814  }
815  }
816 }
817 
818 // used to filter out invalid characters for REMMINA_PROTOCOL_SETTING_TYPE_DOUBLE
819 // '.' and ',' can't be used interchangeably! It depends on the language setting
820 // of the user.
821 void remmina_file_editor_double_setting_filter(GtkEditable *editable, const gchar *text,
822  gint length, gint *position, gpointer data)
823 {
824  for (int i = 0; i < length; i++) {
825  if (!isdigit(text[i]) && text[i] != '-' && text[i] != '.' && text[i] != ',') {
826  g_signal_stop_emission_by_name(G_OBJECT(editable), "insert-text");
827  return;
828  }
829  }
830 }
831 
832 static GtkWidget *remmina_file_editor_create_int(RemminaFileEditor *gfe, GtkWidget *grid,
833  gint row, gint col, const gchar *label, const gint value,
834  gint left, gint right, gchar *setting_name)
835 {
836  TRACE_CALL(__func__);
837  GtkWidget *widget;
838 
839  widget = gtk_label_new(label);
840  gtk_widget_show(widget);
841 #if GTK_CHECK_VERSION(3, 12, 0)
842  gtk_widget_set_margin_start(widget, left);
843  gtk_widget_set_margin_end(widget, right);
844 #else
845  gtk_widget_set_margin_left(widget, left);
846  gtk_widget_set_margin_right(widget, right);
847 #endif
848  gtk_widget_set_valign(widget, GTK_ALIGN_START);
849  gtk_widget_set_halign(widget, GTK_ALIGN_START);
850  gtk_grid_attach(GTK_GRID(grid), widget, col, row, 1, 1);
851 
852  widget = gtk_entry_new();
853  gtk_widget_show(widget);
854  gtk_grid_attach(GTK_GRID(grid), widget, col + 1, row, 1, 1);
855  gtk_entry_set_max_length(GTK_ENTRY(widget), 300);
856  gtk_widget_set_hexpand(widget, TRUE);
857  if (setting_name)
858  gtk_widget_set_name(widget, setting_name);
859 
860  // Convert int to str.
861  int length = snprintf(NULL, 0, "%d", value) + 1; // +1 '\0' byte
862  char *str = malloc(length);
863  snprintf(str, length, "%d", value);
864 
865  gtk_entry_set_text(GTK_ENTRY(widget), str);
866  free(str);
867 
868  g_signal_connect(G_OBJECT(widget), "insert-text",
869  G_CALLBACK(remmina_file_editor_int_setting_filter), NULL);
870 
871  return widget;
872 }
873 
875  GtkWidget *grid, gint row, gint col,
876  const gchar *label, gdouble value, gint left,
877  gint right, gchar *setting_name)
878 {
879  TRACE_CALL(__func__);
880  GtkWidget *widget;
881 
882  widget = gtk_label_new(label);
883  gtk_widget_show(widget);
884 #if GTK_CHECK_VERSION(3, 12, 0)
885  gtk_widget_set_margin_start(widget, left);
886  gtk_widget_set_margin_end(widget, right);
887 #else
888  gtk_widget_set_margin_left(widget, left);
889  gtk_widget_set_margin_right(widget, right);
890 #endif
891  gtk_widget_set_valign(widget, GTK_ALIGN_START);
892  gtk_widget_set_halign(widget, GTK_ALIGN_START);
893  gtk_grid_attach(GTK_GRID(grid), widget, col, row, 1, 1);
894 
895  widget = gtk_entry_new();
896  gtk_widget_show(widget);
897  gtk_grid_attach(GTK_GRID(grid), widget, col + 1, row, 1, 1);
898  gtk_entry_set_max_length(GTK_ENTRY(widget), 300);
899  gtk_widget_set_hexpand(widget, TRUE);
900  if (setting_name)
901  gtk_widget_set_name(widget, setting_name);
902 
903  // Convert double to str.
904  int length = snprintf(NULL, 0, "%.8g", value) + 1; // +1 '\0' byte
905  char *str = malloc(length);
906  snprintf(str, length, "%f", value);
907 
908  gtk_entry_set_text(GTK_ENTRY(widget), str);
909  free(str);
910 
911  g_signal_connect(G_OBJECT(widget), "insert-text",
913 
914  return widget;
915 }
916 
917 
918 
919 static void remmina_file_editor_create_settings(RemminaFileEditor *gfe, GtkWidget *grid,
920  const RemminaProtocolSetting *settings)
921 {
922  TRACE_CALL(__func__);
923  RemminaFileEditorPriv *priv = gfe->priv;
924  GtkWidget *widget;
925  gint grid_row = 0;
926  gint grid_column = 0;
927  gchar **strarr;
928  gchar *setting_name;
929  const gchar *escaped;
930 
931  while (settings->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
932  setting_name = (gchar *)(remmina_plugin_manager_get_canonical_setting_name(settings));
933  switch (settings->type) {
935  remmina_file_editor_create_server(gfe, settings, grid, grid_row);
936  break;
937 
939  widget = remmina_file_editor_create_password(gfe, grid, grid_row, 0,
940  g_dgettext(priv->plugin->domain, settings->label),
941  remmina_file_get_string(priv->remmina_file, setting_name),
942  setting_name);
943  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
944  grid_row++;
945  break;
946 
948  remmina_file_editor_create_resolution(gfe, settings, grid, grid_row);
949  grid_row += 1;
950  break;
951 
953  strarr = remmina_pref_keymap_groups();
954  priv->keymap_combo = remmina_file_editor_create_select(gfe, grid,
955  grid_row + 1, 0,
956  _("Keyboard mapping"), (const gpointer *)strarr,
957  remmina_file_get_string(priv->remmina_file, "keymap"),
958  setting_name);
959  g_strfreev(strarr);
960  grid_row++;
961  break;
962 
964  widget = remmina_file_editor_create_text(gfe, grid, grid_row, 0,
965  g_dgettext(priv->plugin->domain, settings->label),
966  remmina_file_get_string(priv->remmina_file, setting_name),
967  setting_name);
968  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
969  if (settings->opt2)
970  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
971  grid_row++;
972  break;
973 
975  escaped = remmina_file_get_string(priv->remmina_file, setting_name);
976  escaped = g_uri_unescape_string(escaped, NULL);
977  widget = remmina_file_editor_create_textarea(gfe, grid, grid_row, 0,
978  g_dgettext(priv->plugin->domain, settings->label), escaped,
979  setting_name);
980  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
981  grid_row++;
982  break;
983 
985  widget = remmina_file_editor_create_select(gfe, grid, grid_row, 0,
986  g_dgettext(priv->plugin->domain, settings->label),
987  (const gpointer *)settings->opt1,
988  remmina_file_get_string(priv->remmina_file, setting_name),
989  setting_name);
990  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
991  if (settings->opt2)
992  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
993  break;
994 
996  widget = remmina_file_editor_create_combo(gfe, grid, grid_row, 0,
997  g_dgettext(priv->plugin->domain, settings->label),
998  (const gchar *)settings->opt1,
999  remmina_file_get_string(priv->remmina_file, setting_name),
1000  setting_name);
1001  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1002  if (settings->opt2)
1003  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1004  break;
1005 
1007  widget = remmina_file_editor_create_check(gfe, grid, grid_row, grid_column,
1008  g_dgettext(priv->plugin->domain, settings->label),
1009  remmina_file_get_int(priv->remmina_file, setting_name, FALSE),
1010  setting_name);
1011  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1012  if (settings->opt2)
1013  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1014  break;
1015 
1017  widget = remmina_file_editor_create_chooser(gfe, grid, grid_row, 0,
1018  g_dgettext(priv->plugin->domain, settings->label),
1019  remmina_file_get_string(priv->remmina_file, setting_name),
1020  GTK_FILE_CHOOSER_ACTION_OPEN, setting_name);
1021  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1022  if (settings->opt2)
1023  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1024  break;
1025 
1027  widget = remmina_file_editor_create_chooser(gfe, grid, grid_row, 0,
1028  g_dgettext(priv->plugin->domain, settings->label),
1029  remmina_file_get_string(priv->remmina_file, setting_name),
1030  GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1031  setting_name);
1032  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1033  if (settings->opt2)
1034  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1035  break;
1037  widget = remmina_file_editor_create_int(gfe, grid, grid_row, 0,
1038  g_dgettext(priv->plugin->domain, settings->label),
1039  remmina_file_get_int(priv->remmina_file, setting_name, 0),
1040  0, 40, setting_name);
1041  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1042  if (settings->opt2)
1043  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1044  grid_row++;
1045  break;
1047  widget = remmina_file_editor_create_double(gfe, grid, grid_row, 0,
1048  g_dgettext(priv->plugin->domain, settings->label),
1049  remmina_file_get_double(priv->remmina_file, setting_name, 0.0f),
1050  0, 40, setting_name);
1051  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1052  if (settings->opt2)
1053  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1054  grid_row++;
1055  break;
1056 
1057  default:
1058  break;
1059  }
1060  /* If the setting wants compactness, move to the next column */
1061  if (settings->compact)
1062  grid_column++;
1063  /* Add a new settings row and move to the first column
1064  * if the setting doesn’t want the compactness
1065  * or we already have two columns */
1066  if (!settings->compact || grid_column > 1) {
1067  grid_row++;
1068  grid_column = 0;
1069  }
1070  settings++;
1071  }
1072 }
1073 
1075 {
1076  TRACE_CALL(__func__);
1077  RemminaFileEditorPriv *priv = gfe->priv;
1078  GtkWidget *grid;
1079  GtkWidget *widget;
1080  const gchar *cs;
1081 
1082  /* The Behavior tab (implementation) */
1083  grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Behavior"), 20, 2);
1084 
1085  /* Execute Command frame */
1086  remmina_public_create_group(GTK_GRID(grid), _("Execute a Command"), 0, 1, 2);
1087 
1088  /* PRE connection command */
1089  cs = remmina_file_get_string(priv->remmina_file, "precommand");
1090  widget = remmina_file_editor_create_text2(gfe, grid, 2, 0, _("Before connecting"), cs, 24, 26, "precommand");
1091  priv->behavior_precommand_entry = widget;
1092  gtk_entry_set_placeholder_text(GTK_ENTRY(widget), _("command %h %u %t %U %p %g --option"));
1093  gtk_widget_set_tooltip_markup(widget, _(cmd_tips));
1094 
1095  /* POST connection command */
1096  cs = remmina_file_get_string(priv->remmina_file, "postcommand");
1097  widget = remmina_file_editor_create_text2(gfe, grid, 3, 0, _("After connecting"), cs, 24, 16, "postcommand");
1098  priv->behavior_postcommand_entry = widget;
1099  gtk_entry_set_placeholder_text(GTK_ENTRY(widget), _("/path/to/command -opt1 arg %h %u %t -opt2 %U %p %g"));
1100  gtk_widget_set_tooltip_markup(widget, _(cmd_tips));
1101 
1102  /* Startup frame */
1103  remmina_public_create_group(GTK_GRID(grid), _("Start-up"), 4, 1, 2);
1104 
1105  /* Autostart profile option */
1106  priv->behavior_autostart_check = remmina_file_editor_create_check(gfe, grid, 6, 1, _("Auto-start this profile"),
1107  remmina_file_get_int(priv->remmina_file, "enable-autostart", FALSE), "enable-autostart");
1108 
1109  /* Startup frame */
1110  remmina_public_create_group(GTK_GRID(grid), _("Connection profile security"), 8, 1, 2);
1111 
1112  /* Autostart profile option */
1113  priv->behavior_lock_check = remmina_file_editor_create_check(gfe, grid, 10, 1, _("Require password to connect or edit the profile"),
1114  remmina_file_get_int(priv->remmina_file, "profile-lock", FALSE), "profile-lock");
1115 }
1116 
1117 #ifdef HAVE_LIBSSH
1118 static gpointer ssh_tunnel_auth_list[] =
1119 {
1120  "0", N_("Password"),
1121  "1", N_("SSH identity file"),
1122  "2", N_("SSH agent"),
1123  "3", N_("Public key (automatic)"),
1124  "4", N_("Kerberos (GSSAPI)"),
1125  NULL
1126 };
1127 #endif
1128 
1130 {
1131  TRACE_CALL(__func__);
1132 #ifdef HAVE_LIBSSH
1133  RemminaFileEditorPriv *priv = gfe->priv;
1134  GtkWidget *grid;
1135  GtkWidget *widget;
1136  const gchar *cs;
1137  gchar *s;
1138  gchar *p;
1139  gint row = 0;
1140 
1141  if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_NONE)
1142  return;
1143 
1144  /* The SSH tab (implementation) */
1145  grid = remmina_file_editor_create_notebook_tab(gfe, NULL,
1146  _("SSH Tunnel"), 9, 3);
1147  widget = gtk_toggle_button_new_with_label(_("Enable SSH tunnel"));
1148  gtk_widget_set_halign(widget, GTK_ALIGN_START);
1149  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
1150  g_signal_connect(G_OBJECT(widget), "toggled",
1152  priv->ssh_tunnel_enabled_check = widget;
1153 
1154  widget = gtk_check_button_new_with_label(_("Tunnel via loopback address"));
1155  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 2, 1);
1156  priv->ssh_tunnel_loopback_check = widget;
1157 
1158  // 1
1159  row++;
1160  /* SSH Server group */
1161 
1162  switch (ssh_setting) {
1164  s = g_strdup_printf(_("Same server at port %i"), DEFAULT_SSH_PORT);
1165  widget = gtk_radio_button_new_with_label(NULL, s);
1166  g_free(s);
1167  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 3, 1);
1168  priv->ssh_tunnel_server_default_radio = widget;
1169  // 2
1170  row++;
1171 
1172  widget = gtk_radio_button_new_with_label_from_widget(
1173  GTK_RADIO_BUTTON(priv->ssh_tunnel_server_default_radio), _("Custom"));
1174  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
1175  g_signal_connect(G_OBJECT(widget), "toggled",
1177  priv->ssh_tunnel_server_custom_radio = widget;
1178 
1179  widget = gtk_entry_new();
1180  gtk_entry_set_max_length(GTK_ENTRY(widget), 100);
1181  gtk_widget_set_tooltip_markup(widget, _(server_tips2));
1182  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 2, 1);
1183  priv->ssh_tunnel_server_entry = widget;
1184  // 3
1185  row++;
1186  break;
1187 
1189  priv->ssh_tunnel_server_default_radio = NULL;
1190  priv->ssh_tunnel_server_custom_radio = NULL;
1191 
1192  priv->ssh_tunnel_server_entry = remmina_file_editor_create_text(gfe, grid, 1, 0,
1193  _("Server"), NULL, "ssh_reverse_tunnel_server");
1194  gtk_widget_set_tooltip_markup(priv->ssh_tunnel_server_entry, _(server_tips));
1195  // 2
1196  row++;
1197  break;
1200  priv->ssh_tunnel_server_default_radio = NULL;
1201  priv->ssh_tunnel_server_custom_radio = NULL;
1202  priv->ssh_tunnel_server_entry = NULL;
1203 
1204  break;
1205 
1206  default:
1207  break;
1208  }
1209 
1210  /* This is not used? */
1211  p = remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->protocol_combo));
1212  if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_SFTP) {
1213  widget = remmina_file_editor_create_text(gfe, grid, row, 1,
1214  _("Start-up path"), NULL, "start-up-path");
1215  cs = remmina_file_get_string(priv->remmina_file, "execpath");
1216  gtk_entry_set_text(GTK_ENTRY(widget), cs ? cs : "");
1217  g_hash_table_insert(priv->setting_widgets, "execpath", widget);
1218  // 2
1219  row++;
1220  }
1221 
1222  /* SSH Authentication frame */
1223  remmina_public_create_group(GTK_GRID(grid), _("SSH Authentication"), row, 6, 1);
1224  // 5
1225  row += 2;
1226 
1227  priv->ssh_tunnel_auth_combo = remmina_file_editor_create_select(gfe, grid, row, 0,
1228  _("Authentication type"),
1229  (const gpointer *)ssh_tunnel_auth_list,
1230  remmina_file_get_string(priv->remmina_file, "ssh_tunnel_auth"), "ssh_tunnel_auth");
1231  row++;
1232 
1233  if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_TUNNEL ||
1235  priv->ssh_tunnel_username_entry =
1236  remmina_file_editor_create_text(gfe, grid, row, 0,
1237  _("Username"), NULL, "ssh_tunnel_username");
1238  // 5
1239  row++;
1240  }
1241 
1242  widget = remmina_file_editor_create_password(gfe, grid, row, 0,
1243  _("Password"),
1244  remmina_file_get_string(priv->remmina_file, "ssh_tunnel_password"),
1245  "ssh_tunnel_password");
1246  priv->ssh_tunnel_auth_password = widget;
1247  row++;
1248 
1249  priv->ssh_tunnel_privatekey_chooser = remmina_file_editor_create_chooser(gfe, grid, row, 0,
1250  _("SSH private key file"),
1251  remmina_file_get_string(priv->remmina_file, "ssh_tunnel_privatekey"),
1252  GTK_FILE_CHOOSER_ACTION_OPEN, "ssh_tunnel_privatekey");
1253  row++;
1254 
1255  priv->ssh_tunnel_certfile_chooser = remmina_file_editor_create_chooser(gfe, grid, row, 0,
1256  _("SSH certificate file"),
1257  remmina_file_get_string(priv->remmina_file, "ssh_tunnel_certfile"),
1258  GTK_FILE_CHOOSER_ACTION_OPEN, "ssh_tunnel_certfile");
1259  row++;
1260 
1261  widget = gtk_label_new(_("Password to unlock private key"));
1262  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
1263  widget = gtk_entry_new();
1264  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 2, 1);
1265  gtk_entry_set_max_length(GTK_ENTRY(widget), 300);
1266  gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
1267  gtk_widget_set_hexpand(widget, TRUE);
1268  priv->ssh_tunnel_passphrase = widget;
1269  row++;
1270 
1271  /* Set the values */
1272  cs = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_server");
1273  if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_TUNNEL) {
1274  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_enabled_check),
1275  remmina_file_get_int(priv->remmina_file, "ssh_tunnel_enabled", FALSE));
1276  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_loopback_check),
1277  remmina_file_get_int(priv->remmina_file, "ssh_tunnel_loopback", FALSE));
1278 
1279  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cs ? priv->ssh_tunnel_server_custom_radio : priv->ssh_tunnel_server_default_radio), TRUE);
1280  gtk_entry_set_text(GTK_ENTRY(priv->ssh_tunnel_server_entry),
1281  cs ? cs : "");
1282  } else if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_REVERSE_TUNNEL) {
1283  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_enabled_check),
1284  remmina_file_get_int(priv->remmina_file, "ssh_tunnel_enabled", FALSE));
1285  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_loopback_check),
1286  remmina_file_get_int(priv->remmina_file, "ssh_tunnel_loopback", FALSE));
1287  gtk_entry_set_text(GTK_ENTRY(priv->ssh_tunnel_server_entry),
1288  cs ? cs : "");
1289  }
1290 
1292  gtk_widget_show_all(grid);
1293  g_free(p);
1294 #endif
1295 }
1296 
1298 {
1299  TRACE_CALL(__func__);
1300  RemminaFileEditorPriv *priv = gfe->priv;
1301  GtkWidget *grid;
1302 
1303  static const RemminaProtocolSetting notes_settings[] =
1304  {
1305  { REMMINA_PROTOCOL_SETTING_TYPE_TEXTAREA, "notes_text", NULL, FALSE, NULL, NULL },
1306  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
1307  };
1308 
1310 
1311  /* The Basic tab */
1312  if (priv->plugin->basic_settings) {
1313  grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Basic"), 20, 2);
1314  remmina_file_editor_create_settings(gfe, grid, priv->plugin->basic_settings);
1315  }
1316 
1317  /* The Advanced tab */
1318  if (priv->plugin->advanced_settings) {
1319  grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Advanced"), 20, 2);
1320  remmina_file_editor_create_settings(gfe, grid, priv->plugin->advanced_settings);
1321  }
1322 
1323  /* The Behavior tab */
1325 
1326  /* The SSH tab */
1327  remmina_file_editor_create_ssh_tunnel_tab(gfe, priv->plugin->ssh_setting);
1328 
1329  /* Notes tab */
1330  grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Notes"), 1, 1);
1331  remmina_file_editor_create_settings(gfe, grid, notes_settings);
1332 }
1333 
1335 {
1336  TRACE_CALL(__func__);
1337  RemminaFileEditorPriv *priv = gfe->priv;
1338  gchar *protocol;
1339 
1340  if (priv->config_container) {
1341  gtk_widget_destroy(priv->config_container);
1342  priv->config_container = NULL;
1343  gtk_widget_destroy(priv->config_viewport);
1344  priv->config_viewport = NULL;
1345  gtk_widget_destroy(priv->config_scrollable);
1346  priv->config_scrollable = NULL;
1347  }
1348 
1349  priv->server_combo = NULL;
1350  priv->resolution_iws_radio = NULL;
1351  priv->resolution_auto_radio = NULL;
1352  priv->resolution_custom_radio = NULL;
1353  priv->resolution_custom_combo = NULL;
1354  priv->keymap_combo = NULL;
1355 
1356  priv->ssh_tunnel_enabled_check = NULL;
1357  priv->ssh_tunnel_loopback_check = NULL;
1358  priv->ssh_tunnel_server_default_radio = NULL;
1359  priv->ssh_tunnel_server_custom_radio = NULL;
1360  priv->ssh_tunnel_server_entry = NULL;
1361  priv->ssh_tunnel_username_entry = NULL;
1362  priv->ssh_tunnel_auth_combo = NULL;
1363  priv->ssh_tunnel_auth_password = NULL;
1364  priv->ssh_tunnel_privatekey_chooser = NULL;
1365  priv->ssh_tunnel_certfile_chooser = NULL;
1366 
1367  g_hash_table_remove_all(priv->setting_widgets);
1368 
1369  protocol = remmina_public_combo_get_active_text(combo);
1370  if (protocol) {
1372  protocol);
1373  g_free(protocol);
1375  }
1376 }
1377 
1379 {
1380  TRACE_CALL(__func__);
1381  RemminaFileEditorPriv *priv = gfe->priv;
1382 
1383  remmina_file_set_string(priv->remmina_file, "precommand", gtk_entry_get_text(GTK_ENTRY(priv->behavior_precommand_entry)));
1384  remmina_file_set_string(priv->remmina_file, "postcommand", gtk_entry_get_text(GTK_ENTRY(priv->behavior_postcommand_entry)));
1385 
1386  gboolean autostart_enabled = (priv->behavior_autostart_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->behavior_autostart_check)) : FALSE);
1387  remmina_file_set_int(priv->remmina_file, "enable-autostart", autostart_enabled);
1388  gboolean lock_enabled = (priv->behavior_lock_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->behavior_lock_check)) : FALSE);
1389  remmina_file_set_int(priv->remmina_file, "profile-lock", lock_enabled);
1390 }
1391 
1393 {
1394  TRACE_CALL(__func__);
1395  RemminaFileEditorPriv *priv = gfe->priv;
1396  gboolean ssh_tunnel_enabled;
1397  int ssh_tunnel_auth;
1398 
1399  ssh_tunnel_enabled = (priv->ssh_tunnel_enabled_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_enabled_check)) : FALSE);
1400  remmina_file_set_int(priv->remmina_file,
1401  "ssh_tunnel_loopback",
1402  (priv->ssh_tunnel_loopback_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_loopback_check)) : FALSE));
1403  remmina_file_set_int(priv->remmina_file, "ssh_tunnel_enabled", ssh_tunnel_enabled);
1404  remmina_file_set_string(priv->remmina_file, "ssh_tunnel_auth",
1405  remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->ssh_tunnel_auth_combo)));
1406  remmina_file_set_string(priv->remmina_file, "ssh_tunnel_username",
1407  (ssh_tunnel_enabled ? gtk_entry_get_text(GTK_ENTRY(priv->ssh_tunnel_username_entry)) : NULL));
1409  priv->remmina_file,
1410  "ssh_tunnel_server",
1411  (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));
1412 
1413  ssh_tunnel_auth = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->ssh_tunnel_auth_combo));
1414 
1416  priv->remmina_file,
1417  "ssh_tunnel_auth",
1418  ssh_tunnel_auth);
1419 
1420  // If box is unchecked for private key and certfile file choosers,
1421  // set the string to NULL in the remmina file
1422  if (gtk_widget_get_sensitive(priv->ssh_tunnel_privatekey_chooser)) {
1424  priv->remmina_file,
1425  "ssh_tunnel_privatekey",
1426  (priv->ssh_tunnel_privatekey_chooser ? gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->ssh_tunnel_privatekey_chooser)) : NULL));
1427  }
1428  else {
1429  remmina_file_set_string(priv->remmina_file, "ssh_tunnel_privatekey", NULL);
1430  }
1431 
1432  if (gtk_widget_get_sensitive(priv->ssh_tunnel_certfile_chooser)) {
1434  priv->remmina_file,
1435  "ssh_tunnel_certfile",
1436  (priv->ssh_tunnel_certfile_chooser ? gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->ssh_tunnel_certfile_chooser)) : NULL));
1437  }
1438  else {
1439  remmina_file_set_string(priv->remmina_file, "ssh_tunnel_certfile", NULL);
1440  }
1441 
1443  priv->remmina_file,
1444  "ssh_tunnel_password",
1445  (ssh_tunnel_enabled && (ssh_tunnel_auth == SSH_AUTH_PASSWORD)) ? gtk_entry_get_text(GTK_ENTRY(priv->ssh_tunnel_auth_password)) : NULL);
1446 
1448  priv->remmina_file,
1449  "ssh_tunnel_passphrase",
1450  (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);
1451 }
1452 
1454  gchar * setting_name_to_validate,
1455  gconstpointer value,
1456  GError ** err)
1457 {
1458  if (!setting_name_to_validate || !value || !gfe) {
1459  if (!setting_name_to_validate) {
1460  g_critical(_("(%s: %i): Can't validate setting '%s' since 'value' or 'gfe' "
1461  "are NULL!"),
1462  __func__, __LINE__, setting_name_to_validate);
1463  } else {
1464  g_critical(_("(%s: %i): Can't validate user input since "
1465  "'setting_name_to_validate', 'value' or 'gfe' are NULL!"),
1466  __func__, __LINE__);
1467  }
1468  g_set_error(err, 1, 1, _("Internal error."));
1469  return FALSE;
1470  }
1471 
1472  if (strcmp(setting_name_to_validate, "notes_text") == 0) {
1473  // Not a plugin setting. Bail out early.
1474  return TRUE;
1475  }
1476 
1477  const RemminaProtocolSetting *setting_iter;
1478  RemminaProtocolPlugin *protocol_plugin;
1479  RemminaFileEditorPriv *priv = gfe->priv;
1480  protocol_plugin = priv->plugin;
1481 
1482  setting_iter = protocol_plugin->basic_settings;
1483  if (setting_iter) {
1484  // gboolean found = FALSE;
1485  while (setting_iter->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
1486  if (setting_iter->name == NULL) {
1487  g_error("Internal error: a setting name in protocol plugin %s is "
1488  "null. Please fix RemminaProtocolSetting struct content.",
1489  protocol_plugin->name);
1490  } else if ((gchar *)setting_name_to_validate) {
1491  if (strcmp((gchar *)setting_name_to_validate, setting_iter->name) == 0) {
1492  // found = TRUE;
1493 
1494  gpointer validator_data = setting_iter->validator_data;
1495  GCallback validator = setting_iter->validator;
1496 
1497  // Default behaviour is that everything is valid,
1498  // except a validator is given and its returned GError is not NULL.
1499  GError *err_ret = NULL;
1500 
1501  g_debug("Checking setting '%s' for validation.", setting_iter->name);
1502  if (validator != NULL) {
1503  // Looks weird but it calls the setting's validator
1504  // function using setting_name_to_validate, value and
1505  // validator_data as parameters and it returns a GError*.
1506  err_ret = ((GError * (*)(gpointer, gconstpointer, gpointer)) validator)(setting_name_to_validate, value, validator_data);
1507  }
1508 
1509  if (err_ret) {
1510  g_debug("it has a validator function and it had an error!");
1511  // pass err (returned value) to function caller.
1512  *err = err_ret;
1513  return FALSE;
1514  }
1515 
1516  break;
1517  }
1518  }
1519  setting_iter++;
1520  }
1521 
1522  // if (!found) {
1523  // TOO VERBOSE:
1524  // g_warning("%s is not a plugin setting!", setting_name_to_validate);
1525  // }
1526  }
1527 
1528  return TRUE;
1529 }
1530 
1532  GtkWidget ** failed_widget)
1533 {
1534  TRACE_CALL(__func__);
1535  RemminaFileEditorPriv *priv = gfe->priv;
1536  GHashTableIter iter;
1537  gpointer key;
1538  gpointer widget;
1539  GtkTextBuffer *buffer;
1540  gchar *escaped, *unescaped;
1541  GtkTextIter start, end;
1542 
1543  GError *err = NULL;
1544  *failed_widget = NULL;
1545 
1546  g_hash_table_iter_init(&iter, priv->setting_widgets);
1547  while (g_hash_table_iter_next(&iter, &key, &widget)) {
1548 
1549  // We don't want to save or validate grayed-out settings.
1550  // If widget is a file chooser, it was made not sensitive because
1551  // the box was unchecked. In that case, don't continue. The
1552  // relevant file strings will be set to NULL in the remmina file.
1553  if (!gtk_widget_get_sensitive(GTK_WIDGET(widget)) && !GTK_IS_FILE_CHOOSER(widget)) {
1554  g_debug("Grayed-out setting-widget '%s' will not be saved.",
1555  gtk_widget_get_name(widget));
1556  continue;
1557  }
1558 
1559  if (GTK_IS_ENTRY(widget)) {
1560  const gchar *value = gtk_entry_get_text(GTK_ENTRY(widget));
1561 
1562  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, value, &err)) {
1563  // Error while validating!
1564  // err should be set now.
1565  *failed_widget = widget;
1566  break;
1567  }
1568 
1569  remmina_file_set_string(priv->remmina_file, (gchar *)key, value);
1570  } else if (GTK_IS_TEXT_VIEW(widget)) {
1571  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
1572  gtk_text_buffer_get_start_iter(buffer, &start);
1573  gtk_text_buffer_get_end_iter(buffer, &end);
1574  unescaped = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1575  escaped = g_uri_escape_string(unescaped, NULL, TRUE);
1576 
1577  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, escaped, &err)) {
1578  // Error while validating!
1579  // err should be set now.
1580  *failed_widget = widget;
1581  break;
1582  }
1583 
1584  remmina_file_set_string(priv->remmina_file, (gchar *)key, escaped);
1585  g_free(escaped);
1586  } else if (GTK_IS_COMBO_BOX(widget)) {
1587  gchar *value = remmina_public_combo_get_active_text(GTK_COMBO_BOX(widget));
1588 
1589  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, value, &err)) {
1590  // Error while validating!
1591  // err should be set now.
1592  *failed_widget = widget;
1593  break;
1594  }
1595 
1596  remmina_file_set_string(priv->remmina_file, (gchar *)key, value);
1597  } else if (GTK_IS_FILE_CHOOSER(widget)) {
1598  gchar *value = gtk_widget_get_sensitive(GTK_WIDGET(widget)) ? gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)) : NULL;
1599 
1600  if (!gtk_widget_get_sensitive(GTK_WIDGET(widget))) {
1601  remmina_file_set_string(priv->remmina_file, (gchar *)key, value);
1602  continue;
1603  }
1604 
1605  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, value, &err)) {
1606  // Error while validating!
1607  // err should be set now.
1608  g_free(value);
1609  *failed_widget = widget;
1610  break;
1611  }
1612 
1613  remmina_file_set_string(priv->remmina_file, (gchar *)key, value);
1614  g_free(value);
1615  } else if (GTK_IS_TOGGLE_BUTTON(widget)) {
1616  gboolean value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
1617 
1618  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, &value, &err)) {
1619  // Error while validating!
1620  // err should be set now.
1621  *failed_widget = widget;
1622  break;
1623  }
1624 
1625  remmina_file_set_int(priv->remmina_file, (gchar *)key, value);
1626  }
1627  }
1628 
1629  if (err) {
1630  return err;
1631  }
1632 
1633  return NULL;
1634 }
1635 
1637  GtkWidget ** failed_widget)
1638 {
1639  TRACE_CALL(__func__);
1640  int res_w, res_h;
1641  gchar *custom_resolution;
1643 
1644  RemminaFileEditorPriv *priv = gfe->priv;
1645 
1646  remmina_file_set_string(priv->remmina_file, "name", gtk_entry_get_text(GTK_ENTRY(priv->name_entry)));
1647 
1648  remmina_file_set_string(priv->remmina_file, "labels", gtk_entry_get_text(GTK_ENTRY(priv->labels_entry)));
1649 
1650  remmina_file_set_string(priv->remmina_file, "group",
1651  (priv->group_combo ? remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->group_combo)) : NULL));
1652 
1653  remmina_file_set_string(priv->remmina_file, "protocol",
1654  remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->protocol_combo)));
1655 
1656  remmina_file_set_string(priv->remmina_file, "server",
1657  (priv->server_combo ? remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->server_combo)) : NULL));
1658 
1659  if (priv->resolution_auto_radio) {
1660  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->resolution_auto_radio))) {
1661  /* Resolution is set to auto (which means: Use client fullscreen resolution, aka use client resolution) */
1662  res_w = res_h = 0;
1663  res_mode = RES_USE_CLIENT;
1664  } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->resolution_iws_radio))) {
1665  /* Resolution is set to initial window size */
1666  res_w = res_h = 0;
1667  res_mode = RES_USE_INITIAL_WINDOW_SIZE;
1668  } else {
1669  /* Resolution is set to a value from the list */
1670  custom_resolution = remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->resolution_custom_combo));
1671  if (remmina_public_split_resolution_string(custom_resolution, &res_w, &res_h))
1672  res_mode = RES_USE_CUSTOM;
1673  else
1674  res_mode = RES_USE_INITIAL_WINDOW_SIZE;
1675  g_free(custom_resolution);
1676  }
1677  remmina_file_set_int(priv->remmina_file, "resolution_mode", res_mode);
1678  remmina_file_set_int(priv->remmina_file, "resolution_width", res_w);
1679  remmina_file_set_int(priv->remmina_file, "resolution_height", res_h);
1680  }
1681 
1682  if (priv->keymap_combo)
1683  remmina_file_set_string(priv->remmina_file, "keymap",
1684  remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->keymap_combo)));
1685 
1688  return remmina_file_editor_update_settings(gfe, failed_widget);
1689 }
1690 
1691 static void remmina_file_editor_on_default(GtkWidget *button, RemminaFileEditor *gfe)
1692 {
1693  TRACE_CALL(__func__);
1694  RemminaFile *gf;
1695  GtkWidget *dialog;
1696 
1697  GtkWidget *failed_widget = NULL;
1698  GError *err = remmina_file_editor_update(gfe, &failed_widget);
1699  if (err) {
1700  g_warning(_("Couldn't validate user input. %s"), err->message);
1701  remmina_file_editor_show_validation_error_popup(gfe, failed_widget, err);
1702  return;
1703  }
1704 
1705  gf = remmina_file_dup(gfe->priv->remmina_file);
1706 
1708 
1709  /* Clear properties that should never be default */
1710  remmina_file_set_string(gf, "name", NULL);
1711  remmina_file_set_string(gf, "server", NULL);
1712  remmina_file_set_string(gf, "password", NULL);
1713  remmina_file_set_string(gf, "precommand", NULL);
1714  remmina_file_set_string(gf, "postcommand", NULL);
1715 
1716  remmina_file_set_string(gf, "ssh_tunnel_server", NULL);
1717  remmina_file_set_string(gf, "ssh_tunnel_password", NULL);
1718  remmina_file_set_string(gf, "ssh_tunnel_passphrase", NULL);
1719 
1720  remmina_file_save(gf);
1721  remmina_file_free(gf);
1722 
1723  dialog = gtk_message_dialog_new(GTK_WINDOW(gfe), GTK_DIALOG_MODAL, GTK_MESSAGE_INFO,
1724  GTK_BUTTONS_OK, _("Default settings saved."));
1725  gtk_dialog_run(GTK_DIALOG(dialog));
1726  gtk_widget_destroy(dialog);
1727 }
1728 
1729 static void remmina_file_editor_on_save(GtkWidget *button, RemminaFileEditor *gfe)
1730 {
1731  TRACE_CALL(__func__);
1732 
1733  GtkWidget *failed_widget = NULL;
1734  GError *err = remmina_file_editor_update(gfe, &failed_widget);
1735  if (err) {
1736  g_warning(_("Couldn't validate user input. %s"), err->message);
1737  remmina_file_editor_show_validation_error_popup(gfe, failed_widget, err);
1738  return;
1739  }
1740 
1742 
1743  remmina_file_save(gfe->priv->remmina_file);
1745 
1746  gtk_widget_destroy(GTK_WIDGET(gfe));
1747 }
1748 
1749 static void remmina_file_editor_on_connect(GtkWidget *button, RemminaFileEditor *gfe)
1750 {
1751  TRACE_CALL(__func__);
1752  RemminaFile *gf;
1753 
1754  GtkWidget *failed_widget = NULL;
1755  GError *err = remmina_file_editor_update(gfe, &failed_widget);
1756  if (err) {
1757  g_warning(_("Couldn't validate user input. %s"), err->message);
1758  remmina_file_editor_show_validation_error_popup(gfe, failed_widget, err);
1759  return;
1760  }
1761 
1762  gf = remmina_file_dup(gfe->priv->remmina_file);
1763  /* Put server into name for "Quick Connect" */
1764  if (remmina_file_get_filename(gf) == NULL)
1765  remmina_file_set_string(gf, "name", remmina_file_get_string(gf, "server"));
1766  gtk_widget_destroy(GTK_WIDGET(gfe));
1767  gf->prevent_saving = TRUE;
1768  rcw_open_from_file(gf);
1769 }
1770 
1771 static void remmina_file_editor_on_save_connect(GtkWidget *button, RemminaFileEditor *gfe)
1772 {
1773  TRACE_CALL(__func__);
1775  RemminaFile *gf;
1776 
1777  GtkWidget *failed_widget = NULL;
1778  GError *err = remmina_file_editor_update(gfe, &failed_widget);
1779  if (err) {
1780  g_warning(_("Couldn't validate user input. %s"), err->message);
1781  remmina_file_editor_show_validation_error_popup(gfe, failed_widget, err);
1782  return;
1783  }
1784 
1786 
1787  remmina_file_save(gfe->priv->remmina_file);
1789 
1790  gf = remmina_file_dup(gfe->priv->remmina_file);
1791  /* Put server into name for Quick Connect */
1792  if (remmina_file_get_filename(gf) == NULL)
1793  remmina_file_set_string(gf, "name", remmina_file_get_string(gf, "server"));
1794  gtk_widget_destroy(GTK_WIDGET(gfe));
1795  rcw_open_from_file(gf);
1796 }
1797 
1798 static void remmina_file_editor_on_cancel(GtkWidget *button, RemminaFileEditor *gfe)
1799 {
1800  TRACE_CALL(__func__);
1801  gtk_widget_destroy(GTK_WIDGET(gfe));
1802 }
1803 
1805 {
1806  TRACE_CALL(__func__);
1807  RemminaFileEditorPriv *priv;
1808  GtkWidget *widget;
1809 
1810  priv = g_new0(RemminaFileEditorPriv, 1);
1811  gfe->priv = priv;
1812 
1813  /* Create the editor dialog */
1814  gtk_window_set_title(GTK_WINDOW(gfe), _("Remote Connection Profile"));
1815 
1816  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("_Cancel")), GTK_RESPONSE_CANCEL);
1817  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_cancel), gfe);
1818 
1819  /* Default button */
1820  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("Save as Default")), GTK_RESPONSE_OK);
1821  gtk_widget_set_tooltip_text(GTK_WIDGET(widget), _("Use the current settings as the default for all new connection profiles"));
1822  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_default), gfe);
1823 
1824  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("_Save")), GTK_RESPONSE_APPLY);
1825  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_save), gfe);
1826  gtk_widget_set_sensitive(widget, FALSE);
1827  priv->save_button = widget;
1828 
1829  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("Connect")), GTK_RESPONSE_ACCEPT);
1830  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_connect), gfe);
1831 
1832  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("_Save and Connect")), GTK_RESPONSE_OK);
1833  gtk_widget_set_can_default(widget, TRUE);
1834  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_save_connect), gfe);
1835 
1836  gtk_dialog_set_default_response(GTK_DIALOG(gfe), GTK_RESPONSE_OK);
1837  gtk_window_set_default_size(GTK_WINDOW(gfe), 800, 600);
1838 
1839  g_signal_connect(G_OBJECT(gfe), "destroy", G_CALLBACK(remmina_file_editor_destroy), NULL);
1840  g_signal_connect(G_OBJECT(gfe), "realize", G_CALLBACK(remmina_file_editor_on_realize), NULL);
1841 
1842  priv->setting_widgets = g_hash_table_new(g_str_hash, g_str_equal);
1843 
1844  remmina_widget_pool_register(GTK_WIDGET(gfe));
1845 }
1846 
1847 static gboolean remmina_file_editor_iterate_protocol(gchar *protocol, RemminaPlugin *plugin, gpointer data)
1848 {
1849  TRACE_CALL(__func__);
1850  RemminaFileEditor *gfe = REMMINA_FILE_EDITOR(data);
1851  GtkListStore *store;
1852  GtkTreeIter iter;
1853  gboolean first;
1854 
1855  store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(gfe->priv->protocol_combo)));
1856 
1857  first = !gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1858 
1859  gtk_list_store_append(store, &iter);
1860  gtk_list_store_set(store, &iter, 0, protocol, 1, g_dgettext(plugin->domain, plugin->description), 2,
1861  ((RemminaProtocolPlugin *)plugin)->icon_name, -1);
1862 
1863  if (first || g_strcmp0(protocol, remmina_file_get_string(gfe->priv->remmina_file, "protocol")) == 0)
1864  gtk_combo_box_set_active_iter(GTK_COMBO_BOX(gfe->priv->protocol_combo), &iter);
1865 
1866  return FALSE;
1867 }
1868 
1870 {
1871  TRACE_CALL(__func__);
1872  RemminaFileEditorPriv *priv;
1873 
1874  priv = gfe->priv;
1875  gtk_widget_set_sensitive(priv->group_combo, TRUE);
1876  gtk_widget_set_sensitive(priv->save_button, TRUE);
1877 }
1878 
1879 static void remmina_file_editor_entry_on_changed(GtkEditable *editable, RemminaFileEditor *gfe)
1880 {
1881  TRACE_CALL(__func__);
1882  RemminaFileEditorPriv *priv;
1883 
1884  priv = gfe->priv;
1885  if (remmina_file_get_filename(priv->remmina_file) == NULL) {
1886  remmina_file_generate_filename(priv->remmina_file);
1887  /* TODO: Probably to be removed */
1889  } else {
1890  remmina_file_delete(remmina_file_get_filename(priv->remmina_file));
1891  remmina_file_generate_filename(priv->remmina_file);
1893  }
1894 }
1895 
1897 {
1898  TRACE_CALL(__func__);
1899  RemminaFileEditorPriv *priv;
1900 
1901  priv = gfe->priv;
1902  if (remmina_file_get_filename(priv->remmina_file) == NULL) {
1903  remmina_file_generate_filename(priv->remmina_file);
1904  } else {
1905  remmina_file_delete(remmina_file_get_filename(priv->remmina_file));
1906  remmina_file_generate_filename(priv->remmina_file);
1907  }
1908 }
1909 
1911 {
1912  TRACE_CALL(__func__);
1913  RemminaFileEditor *gfe;
1914  RemminaFileEditorPriv *priv;
1915  GtkWidget *grid;
1916  GtkWidget *widget;
1917  gchar *groups;
1918  gchar *s;
1919  const gchar *cs;
1920 
1921  gfe = REMMINA_FILE_EDITOR(g_object_new(REMMINA_TYPE_FILE_EDITOR, NULL));
1922  priv = gfe->priv;
1923  priv->remmina_file = remminafile;
1924 
1925  if (remmina_file_get_filename(remminafile) == NULL)
1926  gtk_dialog_set_response_sensitive(GTK_DIALOG(gfe), GTK_RESPONSE_APPLY, FALSE);
1927 
1928  /* Create the "Profile" group on the top (for name and protocol) */
1929  grid = gtk_grid_new();
1930  gtk_widget_show(grid);
1931  gtk_grid_set_row_spacing(GTK_GRID(grid), 4);
1932  gtk_grid_set_column_spacing(GTK_GRID(grid), 8);
1933  gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE);
1934  gtk_container_set_border_width(GTK_CONTAINER(grid), 8);
1935  gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(gfe))), grid, FALSE, FALSE, 2);
1936 
1937  // remmina_public_create_group(GTK_GRID(grid), _("Profile"), 0, 4, 3);
1938 
1939  gboolean profile_file_exists = (remmina_file_get_filename(remminafile) != NULL);
1940 
1941  /* Profile: Name */
1942  widget = gtk_label_new(_("Name"));
1943  gtk_widget_show(widget);
1944  gtk_widget_set_valign(widget, GTK_ALIGN_START);
1945  gtk_widget_set_halign(widget, GTK_ALIGN_START);
1946  gtk_grid_attach(GTK_GRID(grid), widget, 0, 3, 2, 1);
1947  gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
1948 
1949  widget = gtk_entry_new();
1950  gtk_widget_show(widget);
1951  gtk_grid_attach(GTK_GRID(grid), widget, 1, 3, 3, 1);
1952  gtk_entry_set_max_length(GTK_ENTRY(widget), 100);
1953  priv->name_entry = widget;
1954 
1955  if (!profile_file_exists) {
1956  gtk_entry_set_text(GTK_ENTRY(widget), _("Quick Connect"));
1957 #if GTK_CHECK_VERSION(3, 16, 0)
1958  gtk_entry_grab_focus_without_selecting(GTK_ENTRY(widget));
1959 #endif
1960  g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(remmina_file_editor_entry_on_changed), gfe);
1961  } else {
1962  cs = remmina_file_get_string(remminafile, "name");
1963  gtk_entry_set_text(GTK_ENTRY(widget), cs ? cs : "");
1964  }
1965 
1966  /* Profile: Group */
1967  widget = gtk_label_new(_("Group"));
1968  gtk_widget_show(widget);
1969  gtk_widget_set_valign(widget, GTK_ALIGN_START);
1970  gtk_widget_set_halign(widget, GTK_ALIGN_START);
1971  gtk_grid_attach(GTK_GRID(grid), widget, 0, 6, 2, 1);
1972 
1974  priv->group_combo = remmina_public_create_combo_entry(groups, remmina_file_get_string(remminafile, "group"), FALSE);
1975  g_free(groups);
1976  gtk_widget_show(priv->group_combo);
1977  gtk_grid_attach(GTK_GRID(grid), priv->group_combo, 1, 6, 3, 1);
1978  gtk_widget_set_sensitive(priv->group_combo, FALSE);
1979 
1980  s = g_strdup_printf(_("Use '%s' as subgroup delimiter"), "/");
1981  gtk_widget_set_tooltip_text(priv->group_combo, s);
1982  g_free(s);
1983 
1984  /* Profile: Labels */
1985  widget = gtk_label_new(_("Labels"));
1986  gtk_widget_show(widget);
1987  gtk_widget_set_valign(widget, GTK_ALIGN_START);
1988  gtk_widget_set_halign(widget, GTK_ALIGN_START);
1989  gtk_grid_attach(GTK_GRID(grid), widget, 0, 9, 2, 1);
1990  gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
1991 
1992  widget = gtk_entry_new();
1993  gtk_widget_show(widget);
1994  gtk_grid_attach(GTK_GRID(grid), widget, 1, 9, 3, 1);
1995  gtk_entry_set_max_length(GTK_ENTRY(widget), 255);
1996  priv->labels_entry = widget;
1997 
1998  if (!profile_file_exists) {
1999  gtk_widget_set_tooltip_text(widget, _("Label1,Label2"));
2000 #if GTK_CHECK_VERSION(3, 16, 0)
2001  gtk_entry_grab_focus_without_selecting(GTK_ENTRY(widget));
2002 #endif
2003  g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(remmina_file_editor_entry_on_changed), gfe);
2004  } else {
2005  cs = remmina_file_get_string(remminafile, "labels");
2006  gtk_entry_set_text(GTK_ENTRY(widget), cs ? cs : "");
2007  }
2008 
2009  /* Profile: Protocol */
2010  widget = gtk_label_new(_("Protocol"));
2011  gtk_widget_show(widget);
2012  gtk_widget_set_valign(widget, GTK_ALIGN_START);
2013  gtk_widget_set_halign(widget, GTK_ALIGN_START);
2014  gtk_grid_attach(GTK_GRID(grid), widget, 0, 12, 2, 1);
2015 
2016  widget = remmina_public_create_combo(TRUE);
2017  gtk_widget_show(widget);
2018  gtk_grid_attach(GTK_GRID(grid), widget, 1, 12, 3, 1);
2019  priv->protocol_combo = widget;
2021  g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(remmina_file_editor_protocol_combo_on_changed), gfe);
2022 
2023  /* Create the "Preference" frame */
2024  widget = gtk_event_box_new();
2025  gtk_widget_show(widget);
2026  gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(gfe))), widget, TRUE, TRUE, 2);
2027  priv->config_box = widget;
2028 
2029  priv->config_container = NULL;
2030  priv->config_scrollable = NULL;
2031 
2032  remmina_file_editor_protocol_combo_on_changed(GTK_COMBO_BOX(priv->protocol_combo), gfe);
2033 
2035 
2036  return GTK_WIDGET(gfe);
2037 }
2038 
2039 GtkWidget *remmina_file_editor_new(void)
2040 {
2041  TRACE_CALL(__func__);
2042  return remmina_file_editor_new_full(NULL, NULL);
2043 }
2044 
2045 GtkWidget *remmina_file_editor_new_full(const gchar *server, const gchar *protocol)
2046 {
2047  TRACE_CALL(__func__);
2048  RemminaFile *remminafile;
2049 
2050  remminafile = remmina_file_new();
2051  if (server)
2052  remmina_file_set_string(remminafile, "server", server);
2053  if (protocol)
2054  remmina_file_set_string(remminafile, "protocol", protocol);
2055 
2056  return remmina_file_editor_new_from_file(remminafile);
2057 }
2058 
2059 GtkWidget *remmina_file_editor_new_copy(const gchar *filename)
2060 {
2061  TRACE_CALL(__func__);
2062  RemminaFile *remminafile;
2063  GtkWidget *dialog;
2064 
2065  remminafile = remmina_file_copy(filename);
2066 
2067  if (remminafile) {
2068  return remmina_file_editor_new_from_file(remminafile);
2069  } else {
2070  dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
2071  _("Could not find the file “%s”."), filename);
2072  gtk_dialog_run(GTK_DIALOG(dialog));
2073  gtk_widget_destroy(dialog);
2074  return NULL;
2075  }
2076 }
2077 
2078 GtkWidget *remmina_file_editor_new_from_filename(const gchar *filename)
2079 {
2080  TRACE_CALL(__func__);
2081  RemminaFile *remminafile;
2082 
2083  remminafile = remmina_file_manager_load_file(filename);
2084  if (remminafile) {
2085  if (remmina_file_get_int(remminafile, "profile-lock", FALSE) && remmina_unlock_new(remmina_main_get_window()) == 0)
2086  return NULL;
2087  return remmina_file_editor_new_from_file(remminafile);
2088  } else {
2089  GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
2090  _("Could not find the file “%s”."), filename);
2091  gtk_dialog_run(GTK_DIALOG(dialog));
2092  gtk_widget_destroy(dialog);
2093  return NULL;
2094  }
2095 }
void remmina_public_create_group(GtkGrid *grid, const gchar *group, gint row, gint rows, gint cols)
-
const gchar * remmina_plugin_manager_get_canonical_setting_name(const RemminaProtocolSetting *setting)
+
const gchar * remmina_plugin_manager_get_canonical_setting_name(const RemminaProtocolSetting *setting)
GtkDialog * remmina_string_list_new(gboolean two_columns, const gchar *fields_separator)
void remmina_file_free(RemminaFile *remminafile)
Definition: remmina_file.c:709
@@ -119,7 +119,7 @@ $(document).ready(function(){initNavTree('remmina__file__editor_8c_source.html',
static GtkWidget * remmina_file_editor_create_select(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gpointer *list, const gchar *value, gchar *setting_name)
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:44
static void remmina_file_editor_protocol_combo_on_changed(GtkComboBox *combo, RemminaFileEditor *gfe)
-
void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data)
+
void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data)
void remmina_widget_pool_register(GtkWidget *widget)
@@ -207,7 +207,7 @@ $(document).ready(function(){initNavTree('remmina__file__editor_8c_source.html',
static void remmina_file_editor_on_realize(GtkWidget *widget, gpointer user_data)
static void remmina_file_editor_button_on_toggled(GtkToggleButton *togglebutton, GtkWidget *widget)
static void remmina_file_editor_on_cancel(GtkWidget *button, RemminaFileEditor *gfe)
-
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
+
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
static void remmina_file_editor_on_save(GtkWidget *button, RemminaFileEditor *gfe)
diff --git a/public/remmina__file__manager_8c_source.html b/public/remmina__file__manager_8c_source.html index da862c13c..b87f1770e 100644 --- a/public/remmina__file__manager_8c_source.html +++ b/public/remmina__file__manager_8c_source.html @@ -99,7 +99,7 @@ $(document).ready(function(){initNavTree('remmina__file__manager_8c_source.html'
RemminaFile *(* import_func)(struct _RemminaFilePlugin *instance, const gchar *from_file)
Definition: plugin.h:108
void remmina_file_manager_free_group_tree(GNode *node)
-
RemminaFilePlugin * remmina_plugin_manager_get_import_file_handler(const gchar *file)
+
RemminaFilePlugin * remmina_plugin_manager_get_import_file_handler(const gchar *file)
gint remmina_string_array_find(RemminaStringArray *array, const gchar *str)
gchar * remmina_string_array_to_string(RemminaStringArray *array)
node_t * node
Definition: remmina_ssh.c:142
diff --git a/public/remmina__main_8c_source.html b/public/remmina__main_8c_source.html index 338dc7119..80a42ae39 100644 --- a/public/remmina__main_8c_source.html +++ b/public/remmina__main_8c_source.html @@ -121,7 +121,7 @@ $(document).ready(function(){initNavTree('remmina__main_8c_source.html','');});
GtkTreeViewColumn * column_files_list_name
Definition: remmina_main.h:75
const gchar * export_hints
Definition: plugin.h:111
-
void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data)
+
void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data)
GtkMenu * menu_popup_delete_rc
Definition: remmina_main.h:55
void remmina_widget_pool_register(GtkWidget *widget)
@@ -132,16 +132,16 @@ $(document).ready(function(){initNavTree('remmina__main_8c_source.html','');});
static gboolean remmina_main_filter_visible_func(GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
Definition: remmina_main.c:527
gboolean remmina_network_monitor_status(RemminaMonitor *rm_monitor)
-
RemminaFilePlugin * remmina_plugin_manager_get_export_file_handler(RemminaFile *remminafile)
+
RemminaFilePlugin * remmina_plugin_manager_get_export_file_handler(RemminaFile *remminafile)
void remmina_file_manager_free_group_tree(GNode *node)
-
RemminaFilePlugin * remmina_plugin_manager_get_import_file_handler(const gchar *file)
+
RemminaFilePlugin * remmina_plugin_manager_get_import_file_handler(const gchar *file)
static gboolean remmina_main_search_key_event(GtkWidget *search_entry, GdkEventKey *event, gpointer user_data)
Definition: remmina_main.c:870
void remmina_main_on_action_tools_import(GSimpleAction *action, GVariant *param, gpointer data)
RemminaMonitor * remmina_network_monitor_new()
-
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
+
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
static void remmina_main_save_size(void)
Save the Remmina Main Window size to assure the main geometry at each restart.
Definition: remmina_main.c:139
@@ -187,7 +187,7 @@ $(document).ready(function(){initNavTree('remmina__main_8c_source.html','');});
GtkMenuButton * menu_header_button
Definition: remmina_main.h:53
static void remmina_main_load_file_tree_group(GtkTreeStore *store)
Definition: remmina_main.c:407
void remmina_main_on_action_connection_copy(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:910
-
void remmina_plugin_manager_show(GtkWindow *parent)
+
void remmina_plugin_manager_show(GtkWindow *parent)
static void remmina_main_import_file_list(GSList *files)
@@ -264,7 +264,7 @@ $(document).ready(function(){initNavTree('remmina__main_8c_source.html','');});
GtkWidget * remmina_file_editor_new(void)
-
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
+
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
static gboolean remmina_main_add_tool_plugin(gchar *name, RemminaPlugin *plugin, gpointer user_data)
diff --git a/public/remmina__mpchange_8c_source.html b/public/remmina__mpchange_8c_source.html index e39f1e255..17bed45ca 100644 --- a/public/remmina__mpchange_8c_source.html +++ b/public/remmina__mpchange_8c_source.html @@ -101,7 +101,7 @@ $(document).ready(function(){initNavTree('remmina__mpchange_8c_source.html','');
GtkDialog * dialog
-
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
+
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
GtkEntry * ePassword1
gboolean(* is_service_available)(struct _RemminaSecretPlugin *instance)
Definition: plugin.h:144
diff --git a/public/remmina__plugin__manager_8c.html b/public/remmina__plugin__manager_8c.html index d406de62f..834a437d4 100644 --- a/public/remmina__plugin__manager_8c.html +++ b/public/remmina__plugin__manager_8c.html @@ -354,7 +354,7 @@ Variables
-

Definition at line 468 of file remmina_plugin_manager.c.

+

Definition at line 470 of file remmina_plugin_manager.c.

@@ -374,7 +374,7 @@ Variables
-

Definition at line 454 of file remmina_plugin_manager.c.

+

Definition at line 456 of file remmina_plugin_manager.c.

@@ -394,7 +394,7 @@ Variables
-

Definition at line 590 of file remmina_plugin_manager.c.

+

Definition at line 592 of file remmina_plugin_manager.c.

@@ -414,7 +414,7 @@ Variables
-

Definition at line 571 of file remmina_plugin_manager.c.

+

Definition at line 573 of file remmina_plugin_manager.c.

@@ -444,7 +444,7 @@ Variables
-

Definition at line 439 of file remmina_plugin_manager.c.

+

Definition at line 441 of file remmina_plugin_manager.c.

@@ -464,7 +464,7 @@ Variables
-

Definition at line 607 of file remmina_plugin_manager.c.

+

Definition at line 609 of file remmina_plugin_manager.c.

@@ -514,7 +514,7 @@ Variables
-

Definition at line 632 of file remmina_plugin_manager.c.

+

Definition at line 634 of file remmina_plugin_manager.c.

@@ -534,7 +534,7 @@ Variables
-

Definition at line 434 of file remmina_plugin_manager.c.

+

Definition at line 436 of file remmina_plugin_manager.c.

@@ -570,7 +570,7 @@ Variables
-

Definition at line 613 of file remmina_plugin_manager.c.

+

Definition at line 615 of file remmina_plugin_manager.c.

@@ -618,7 +618,7 @@ Variables
-

Definition at line 517 of file remmina_plugin_manager.c.

+

Definition at line 519 of file remmina_plugin_manager.c.

@@ -656,7 +656,7 @@ Variables
-

Definition at line 506 of file remmina_plugin_manager.c.

+

Definition at line 508 of file remmina_plugin_manager.c.

@@ -684,7 +684,7 @@ Variables
-

Definition at line 488 of file remmina_plugin_manager.c.

+

Definition at line 490 of file remmina_plugin_manager.c.

@@ -703,7 +703,7 @@ Variables
-

Definition at line 499 of file remmina_plugin_manager.c.

+

Definition at line 501 of file remmina_plugin_manager.c.

diff --git a/public/remmina__plugin__manager_8c_source.html b/public/remmina__plugin__manager_8c_source.html index 041216040..a459523c2 100644 --- a/public/remmina__plugin__manager_8c_source.html +++ b/public/remmina__plugin__manager_8c_source.html @@ -86,14 +86,14 @@ $(document).ready(function(){initNavTree('remmina__plugin__manager_8c_source.htm
remmina_plugin_manager.c
-Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2010-2011 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2023 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 "config.h"
38 
39 
40 #include <gtk/gtk.h>
41 #include <glib/gi18n.h>
42 #include <gmodule.h>
43 #include <gio/gio.h>
44 #include <string.h>
45 #ifdef GDK_WINDOWING_X11
46 #include <gdk/gdkx.h>
47 #elif defined(GDK_WINDOWING_WAYLAND)
48 #include <gdk/gdkwayland.h>
49 #endif
50 #include "remmina_public.h"
51 #include "remmina_main.h"
52 #include "remmina_file_manager.h"
53 #include "remmina_pref.h"
55 #include "remmina_log.h"
56 #include "remmina_widget_pool.h"
57 #include "rcw.h"
58 #include "remmina_plugin_manager.h"
59 #include "remmina_plugin_native.h"
60 #include "remmina_public.h"
63 
64 static GPtrArray* remmina_plugin_table = NULL;
65 
66 /* A GHashTable of GHashTables where to store the names of the encrypted settings */
67 static GHashTable *encrypted_settings_cache = NULL;
68 
69 /* There can be only one secret plugin loaded */
71 
72 // TRANSLATORS: "Language Wrapper" is a wrapper for plugins written in other programmin languages (Python in this context)
73 static const gchar *remmina_plugin_type_name[] =
74 { N_("Protocol"), N_("Entry"), N_("File"), N_("Tool"), N_("Preference"), N_("Secret"), N_("Language Wrapper"), NULL };
75 
77 {
78  TRACE_CALL(__func__);
79  return g_strcmp0((*a)->name, (*b)->name);
80 }
81 
82 static void htdestroy(gpointer ht)
83 {
84  g_hash_table_unref(ht);
85 }
86 
87 static void init_settings_cache(RemminaPlugin *plugin)
88 {
89  TRACE_CALL(__func__);
90  RemminaProtocolPlugin *protocol_plugin;
91  const RemminaProtocolSetting* setting_iter;
92  GHashTable *pht;
93 
94  /* This code make a encrypted setting cache only for protocol plugins */
95 
96  if (plugin->type != REMMINA_PLUGIN_TYPE_PROTOCOL) {
97  return;
98  }
99 
100  if (encrypted_settings_cache == NULL)
101  encrypted_settings_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, htdestroy);
102 
103  if (!(pht = g_hash_table_lookup(encrypted_settings_cache, plugin->name))) {
104  pht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
105  g_hash_table_insert(encrypted_settings_cache, g_strdup(plugin->name), pht);
106  }
107 
108  /* Some settings are encrypted "by name" */
109  /* g_hash_table_insert(pht, g_strdup("proxy_password"), (gpointer)TRUE); */
110 
111  g_hash_table_insert(pht, g_strdup("ssh_tunnel_password"), (gpointer)TRUE);
112  g_hash_table_insert(pht, g_strdup("ssh_tunnel_passphrase"), (gpointer)TRUE);
113 
114  /* ssh_password is no longer used starting from remmina 1.4.
115  * But we MUST mark it as encrypted setting, or the migration procedure will fail */
116  g_hash_table_insert(pht, g_strdup("ssh_password"), (gpointer)TRUE);
117 
118  /* Other settings are encrypted because of their type */
119  protocol_plugin = (RemminaProtocolPlugin *)plugin;
120  setting_iter = protocol_plugin->basic_settings;
121  if (setting_iter) {
122  while (setting_iter->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
123  if (setting_iter->type == REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD)
124  g_hash_table_insert(pht, g_strdup(remmina_plugin_manager_get_canonical_setting_name(setting_iter)), (gpointer)TRUE);
125  setting_iter++;
126  }
127  }
128  setting_iter = protocol_plugin->advanced_settings;
129  if (setting_iter) {
130  while (setting_iter->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
131  if (setting_iter->type == REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD)
132  g_hash_table_insert(pht, g_strdup(remmina_plugin_manager_get_canonical_setting_name(setting_iter)), (gpointer)TRUE);
133  setting_iter++;
134  }
135  }
136 
137 }
138 
140 {
141  TRACE_CALL(__func__);
142  if (plugin->type == REMMINA_PLUGIN_TYPE_SECRET) {
143  g_print("Remmina plugin %s (type=%s) has been registered, but is not yet initialized/activated. "
144  "The initialization order is %d.\n", plugin->name,
145  _(remmina_plugin_type_name[plugin->type]),
146  ((RemminaSecretPlugin*)plugin)->init_order);
147  }
148  init_settings_cache(plugin);
149 
150  g_ptr_array_add(remmina_plugin_table, plugin);
151  g_ptr_array_sort(remmina_plugin_table, (GCompareFunc)remmina_plugin_manager_compare_func);
152  return TRUE;
153 }
154 
156 {
157  GdkDisplayManager* dm;
158  GdkDisplay* d;
159  gboolean available;
160 
161  dm = gdk_display_manager_get();
162  d = gdk_display_manager_get_default_display(dm);
163  available = FALSE;
164 
165 #ifdef GDK_WINDOWING_X11
166  if (GDK_IS_X11_DISPLAY(d)) {
167  /* GtkSocket support is available only under X.Org */
168  available = TRUE;
169  }
170 #endif
171  return available;
172 }
173 
175 {
222 
224 
234 
243 
253 
255 
311 };
312 
313 const char *get_filename_ext(const char *filename) {
314  const char* last = strrchr(filename, '/');
315  const char *dot = strrchr(last, '.');
316  if(!dot || dot == filename) return "";
317  return dot + 1;
318 }
319 
320 static gint compare_secret_plugin_init_order(gconstpointer a, gconstpointer b)
321 {
322  RemminaSecretPlugin *sa, *sb;
323 
324  sa = (RemminaSecretPlugin*)a;
325  sb = (RemminaSecretPlugin*)b;
326 
327  if (sa->init_order > sb->init_order)
328  return 1;
329  else if (sa->init_order < sb->init_order)
330  return -1;
331  return 0;
332 }
333 
335 {
336  TRACE_CALL(__func__);
337  GDir *dir;
338  const gchar *name, *ptr;
339  gchar *fullpath;
340  RemminaPlugin *plugin;
342  int i, j;
343  GSList *secret_plugins;
344  GSList *sple;
345  GPtrArray *alternative_language_plugins;
346 
347  remmina_plugin_table = g_ptr_array_new();
348  alternative_language_plugins = g_ptr_array_new();
349 
350  if (!g_module_supported()) {
351  g_print("Dynamic loading of plugins is not supported on this platform!\n");
352  return;
353  }
354 
355  g_print("Load modules from %s\n", REMMINA_RUNTIME_PLUGINDIR);
356  dir = g_dir_open(REMMINA_RUNTIME_PLUGINDIR, 0, NULL);
357 
358  if (dir == NULL)
359  return;
360  while ((name = g_dir_read_name(dir)) != NULL) {
361  if ((ptr = strrchr(name, '.')) == NULL)
362  continue;
363  ptr++;
364  fullpath = g_strdup_printf(REMMINA_RUNTIME_PLUGINDIR "/%s", name);
366  g_ptr_array_add(alternative_language_plugins, g_strdup_printf(REMMINA_RUNTIME_PLUGINDIR "/%s", name));
367  continue;
368  }
369  remmina_plugin_native_load(&remmina_plugin_manager_service, fullpath);
370  g_free(fullpath);
371  }
372  g_dir_close(dir);
373 
374  while (alternative_language_plugins->len > 0) {
375  gboolean has_loaded = FALSE;
376  gchar* name = (gchar*)g_ptr_array_remove_index(alternative_language_plugins, 0);
377  const gchar* ext = get_filename_ext(name);
378 
379  for (j = 0; j < remmina_plugin_table->len && !has_loaded; j++) {
380  plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, j);
383  const gchar** supported_extention = wrapper_plugin->supported_extentions;
384  while (*supported_extention) {
385  if (g_str_equal(*supported_extention, ext)) {
386  has_loaded = wrapper_plugin->load(wrapper_plugin, name);
387  if (has_loaded) {
388  break;
389  }
390  }
391  supported_extention++;
392  }
393  if (has_loaded) break;
394  }
395  }
396 
397  if (!has_loaded) {
398  g_print("%s: Skip unsupported file type '%s'\n", name, ext);
399  }
400 
401  g_free(name);
402  }
403 
404  /* Now all secret plugins needs to initialize, following their init_order.
405  * The 1st plugin which will initialize correctly will be
406  * the default remmina_secret_plugin */
407 
408  if (remmina_secret_plugin)
409  g_print("Internal ERROR: remmina_secret_plugin must be null here\n");
410 
411  secret_plugins = NULL;
412  for (i = 0; i < remmina_plugin_table->len; i++) {
413  plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, i);
414  if (plugin->type == REMMINA_PLUGIN_TYPE_SECRET) {
415  secret_plugins = g_slist_insert_sorted(secret_plugins, (gpointer)plugin, compare_secret_plugin_init_order);
416  }
417  }
418 
419  sple = secret_plugins;
420  while(sple != NULL) {
421  sp = (RemminaSecretPlugin*)sple->data;
422  if (sp->init(sp)) {
423  g_print("The %s secret plugin has been initialized and it will be your default secret plugin\n",
424  sp->name);
425  remmina_secret_plugin = sp;
426  break;
427  }
428  sple = sple->next;
429  }
430 
431  g_slist_free(secret_plugins);
432 }
433 
434 gboolean remmina_plugin_manager_loader_supported(const char *filetype) {
435  TRACE_CALL(__func__);
436  return g_str_equal(G_MODULE_SUFFIX, filetype);
437 }
438 
440 {
441  TRACE_CALL(__func__);
442  RemminaPlugin *plugin;
443  gint i;
444 
445  for (i = 0; i < remmina_plugin_table->len; i++) {
446  plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, i);
447  if (plugin->type == type && g_strcmp0(plugin->name, name) == 0) {
448  return plugin;
449  }
450  }
451  return NULL;
452 }
453 
455 {
456  if (setting->name == NULL) {
458  return "server";
460  return "password";
462  return "resolution";
463  return "missing_setting_name_into_plugin_RemminaProtocolSetting";
464  }
465  return setting->name;
466 }
467 
469 {
470  TRACE_CALL(__func__);
471  RemminaPlugin *plugin;
472  gint i;
473 
474  for (i = 0; i < remmina_plugin_table->len; i++) {
475  plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, i);
476  if (plugin->type == type) {
477  func((gchar*)plugin->name, plugin, data);
478  }
479  }
480 }
481 
482 /* A copy of remmina_plugin_manager_show and remmina_plugin_manager_show_for_each
483  * This is because we want to print the list of plugins, and their versions, to the standard output
484  * with the remmina command line option --full-version instead of using the plugins widget
485  ** @todo Investigate to use only GListStore and than pass the elements to be shown to 2 separate
486  * functions
487  * WARNING: GListStore is supported only from GLib 2.44 */
489 {
490  TRACE_CALL(__func__);
491 
492  g_print("%-20s%-16s%-64s%-10s\n", plugin->name,
493  _(remmina_plugin_type_name[plugin->type]),
494  g_dgettext(plugin->domain, plugin->description),
495  plugin->version);
496  return FALSE;
497 }
498 
500 {
501  TRACE_CALL(__func__);
502  g_print("%-20s%-16s%-64s%-10s\n", "NAME", "TYPE", "DESCRIPTION", "PLUGIN AND LIBRARY VERSION");
503  g_ptr_array_foreach(remmina_plugin_table, (GFunc)remmina_plugin_manager_show_for_each_stdout, NULL);
504 }
505 
506 static gboolean remmina_plugin_manager_show_for_each(RemminaPlugin *plugin, GtkListStore *store)
507 {
508  TRACE_CALL(__func__);
509  GtkTreeIter iter;
510 
511  gtk_list_store_append(store, &iter);
512  gtk_list_store_set(store, &iter, 0, plugin->name, 1, _(remmina_plugin_type_name[plugin->type]), 2,
513  g_dgettext(plugin->domain, plugin->description), 3, plugin->version, -1);
514  return FALSE;
515 }
516 
517 void remmina_plugin_manager_show(GtkWindow *parent)
518 {
519  TRACE_CALL(__func__);
520  GtkWidget *dialog;
521  GtkWidget *scrolledwindow;
522  GtkWidget *tree;
523  GtkCellRenderer *renderer;
524  GtkTreeViewColumn *column;
525  GtkListStore *store;
526 
527  dialog = gtk_dialog_new_with_buttons(_("Plugins"), parent, GTK_DIALOG_MODAL, _("_OK"), GTK_RESPONSE_ACCEPT, NULL);
528  g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), dialog);
529  gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 350);
530 
531  scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
532  gtk_widget_show(scrolledwindow);
533  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
534  gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), scrolledwindow, TRUE, TRUE, 0);
535 
536  tree = gtk_tree_view_new();
537  gtk_container_add(GTK_CONTAINER(scrolledwindow), tree);
538  gtk_widget_show(tree);
539 
540  store = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
541  g_ptr_array_foreach(remmina_plugin_table, (GFunc)remmina_plugin_manager_show_for_each, store);
542  gtk_tree_view_set_model(GTK_TREE_VIEW(tree), GTK_TREE_MODEL(store));
543 
544  renderer = gtk_cell_renderer_text_new();
545  column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "text", 0, NULL);
546  gtk_tree_view_column_set_resizable(column, TRUE);
547  gtk_tree_view_column_set_sort_column_id(column, 0);
548  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
549 
550  renderer = gtk_cell_renderer_text_new();
551  column = gtk_tree_view_column_new_with_attributes(_("Type"), renderer, "text", 1, NULL);
552  gtk_tree_view_column_set_resizable(column, TRUE);
553  gtk_tree_view_column_set_sort_column_id(column, 1);
554  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
555 
556  renderer = gtk_cell_renderer_text_new();
557  column = gtk_tree_view_column_new_with_attributes(_("Description"), renderer, "text", 2, NULL);
558  gtk_tree_view_column_set_resizable(column, TRUE);
559  gtk_tree_view_column_set_sort_column_id(column, 2);
560  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
561 
562  renderer = gtk_cell_renderer_text_new();
563  column = gtk_tree_view_column_new_with_attributes(_("Version"), renderer, "text", 3, NULL);
564  gtk_tree_view_column_set_resizable(column, TRUE);
565  gtk_tree_view_column_set_sort_column_id(column, 3);
566  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
567 
568  gtk_widget_show(dialog);
569 }
570 
572 {
573  TRACE_CALL(__func__);
574  RemminaFilePlugin *plugin;
575  gint i;
576 
577  for (i = 0; i < remmina_plugin_table->len; i++) {
578  plugin = (RemminaFilePlugin*)g_ptr_array_index(remmina_plugin_table, i);
579 
580  if (plugin->type != REMMINA_PLUGIN_TYPE_FILE)
581  continue;
582 
583  if (plugin->import_test_func(plugin, file)) {
584  return plugin;
585  }
586  }
587  return NULL;
588 }
589 
591 {
592  TRACE_CALL(__func__);
593  RemminaFilePlugin *plugin;
594  gint i;
595 
596  for (i = 0; i < remmina_plugin_table->len; i++) {
597  plugin = (RemminaFilePlugin*)g_ptr_array_index(remmina_plugin_table, i);
598  if (plugin->type != REMMINA_PLUGIN_TYPE_FILE)
599  continue;
600  if (plugin->export_test_func(plugin, remminafile)) {
601  return plugin;
602  }
603  }
604  return NULL;
605 }
606 
608 {
609  TRACE_CALL(__func__);
610  return remmina_secret_plugin;
611 }
612 
614 {
615  TRACE_CALL(__func__);
616  const RemminaProtocolFeature *feature;
617  RemminaProtocolPlugin* plugin;
618 
620 
621  if (plugin == NULL) {
622  return FALSE;
623  }
624 
625  for (feature = plugin->features; feature && feature->type; feature++) {
626  if (feature->type == ftype)
627  return TRUE;
628  }
629  return FALSE;
630 }
631 
633 {
634  TRACE_CALL(__func__);
635  GHashTable *pht;
636 
637  if (encrypted_settings_cache == NULL)
638  return FALSE;
639 
640  if (!(pht = g_hash_table_lookup(encrypted_settings_cache, pp->name)))
641  return FALSE;
642 
643  if (!g_hash_table_lookup(pht, setting))
644  return FALSE;
645 
646  return TRUE;
647 }
gboolean remmina_protocol_widget_get_expand(RemminaProtocolWidget *gp)
+Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2010-2011 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2023 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 "config.h"
38 
39 
40 #include <gtk/gtk.h>
41 #include <glib/gi18n.h>
42 #include <gmodule.h>
43 #include <gio/gio.h>
44 #include <string.h>
45 #ifdef GDK_WINDOWING_X11
46 #include <gdk/gdkx.h>
47 #elif defined(GDK_WINDOWING_WAYLAND)
48 #include <gdk/gdkwayland.h>
49 #endif
50 #include "remmina_public.h"
51 #include "remmina_main.h"
52 #include "remmina_file_manager.h"
53 #include "remmina_pref.h"
55 #include "remmina_log.h"
56 #include "remmina_widget_pool.h"
57 #include "rcw.h"
58 #include "remmina_plugin_manager.h"
59 #include "remmina_plugin_native.h"
60 #include "remmina_public.h"
63 
64 static GPtrArray* remmina_plugin_table = NULL;
65 
66 /* A GHashTable of GHashTables where to store the names of the encrypted settings */
67 static GHashTable *encrypted_settings_cache = NULL;
68 
69 /* There can be only one secret plugin loaded */
71 
72 // TRANSLATORS: "Language Wrapper" is a wrapper for plugins written in other programmin languages (Python in this context)
73 static const gchar *remmina_plugin_type_name[] =
74 { N_("Protocol"), N_("Entry"), N_("File"), N_("Tool"), N_("Preference"), N_("Secret"), N_("Language Wrapper"), NULL };
75 
77 {
78  TRACE_CALL(__func__);
79  return g_strcmp0((*a)->name, (*b)->name);
80 }
81 
82 static void htdestroy(gpointer ht)
83 {
84  g_hash_table_unref(ht);
85 }
86 
87 static void init_settings_cache(RemminaPlugin *plugin)
88 {
89  TRACE_CALL(__func__);
90  RemminaProtocolPlugin *protocol_plugin;
91  const RemminaProtocolSetting* setting_iter;
92  GHashTable *pht;
93 
94  /* This code make a encrypted setting cache only for protocol plugins */
95 
96  if (plugin->type != REMMINA_PLUGIN_TYPE_PROTOCOL) {
97  return;
98  }
99 
100  if (encrypted_settings_cache == NULL)
101  encrypted_settings_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, htdestroy);
102 
103  if (!(pht = g_hash_table_lookup(encrypted_settings_cache, plugin->name))) {
104  pht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
105  g_hash_table_insert(encrypted_settings_cache, g_strdup(plugin->name), pht);
106  }
107 
108  /* Some settings are encrypted "by name" */
109  /* g_hash_table_insert(pht, g_strdup("proxy_password"), (gpointer)TRUE); */
110 
111  g_hash_table_insert(pht, g_strdup("ssh_tunnel_password"), (gpointer)TRUE);
112  g_hash_table_insert(pht, g_strdup("ssh_tunnel_passphrase"), (gpointer)TRUE);
113 
114  /* ssh_password is no longer used starting from remmina 1.4.
115  * But we MUST mark it as encrypted setting, or the migration procedure will fail */
116  g_hash_table_insert(pht, g_strdup("ssh_password"), (gpointer)TRUE);
117 
118  /* Other settings are encrypted because of their type */
119  protocol_plugin = (RemminaProtocolPlugin *)plugin;
120  setting_iter = protocol_plugin->basic_settings;
121  if (setting_iter) {
122  while (setting_iter->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
123  if (setting_iter->type == REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD)
124  g_hash_table_insert(pht, g_strdup(remmina_plugin_manager_get_canonical_setting_name(setting_iter)), (gpointer)TRUE);
125  setting_iter++;
126  }
127  }
128  setting_iter = protocol_plugin->advanced_settings;
129  if (setting_iter) {
130  while (setting_iter->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
131  if (setting_iter->type == REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD)
132  g_hash_table_insert(pht, g_strdup(remmina_plugin_manager_get_canonical_setting_name(setting_iter)), (gpointer)TRUE);
133  setting_iter++;
134  }
135  }
136 
137 }
138 
140 {
141  TRACE_CALL(__func__);
142  if (plugin->type == REMMINA_PLUGIN_TYPE_SECRET) {
143  g_print("Remmina plugin %s (type=%s) has been registered, but is not yet initialized/activated. "
144  "The initialization order is %d.\n", plugin->name,
145  _(remmina_plugin_type_name[plugin->type]),
146  ((RemminaSecretPlugin*)plugin)->init_order);
147  }
148  init_settings_cache(plugin);
149 
150  g_ptr_array_add(remmina_plugin_table, plugin);
151  g_ptr_array_sort(remmina_plugin_table, (GCompareFunc)remmina_plugin_manager_compare_func);
152  return TRUE;
153 }
154 
156 {
157  GdkDisplayManager* dm;
158  GdkDisplay* d;
159  gboolean available;
160 
161  dm = gdk_display_manager_get();
162  d = gdk_display_manager_get_default_display(dm);
163  available = FALSE;
164 
165 #ifdef GDK_WINDOWING_X11
166  if (GDK_IS_X11_DISPLAY(d)) {
167  /* GtkSocket support is available only under X.Org */
168  available = TRUE;
169  }
170 #endif
171  return available;
172 }
173 
175 {
222 
224 
234 
243 
253 
255 
311 };
312 
313 const char *get_filename_ext(const char *filename) {
314  const char* last = strrchr(filename, '/');
315  const char *dot = strrchr(last, '.');
316  if(!dot || dot == filename) return "";
317  return dot + 1;
318 }
319 
320 static gint compare_secret_plugin_init_order(gconstpointer a, gconstpointer b)
321 {
322  RemminaSecretPlugin *sa, *sb;
323 
324  sa = (RemminaSecretPlugin*)a;
325  sb = (RemminaSecretPlugin*)b;
326 
327  if (sa->init_order > sb->init_order)
328  return 1;
329  else if (sa->init_order < sb->init_order)
330  return -1;
331  return 0;
332 }
333 
335 {
336  TRACE_CALL(__func__);
337  GDir *dir;
338  const gchar *name, *ptr;
339  gchar *fullpath;
340  RemminaPlugin *plugin;
342  int i, j;
343  GSList *secret_plugins;
344  GSList *sple;
345  GPtrArray *alternative_language_plugins;
346 
347  remmina_plugin_table = g_ptr_array_new();
348  alternative_language_plugins = g_ptr_array_new();
349 
350  if (!g_module_supported()) {
351  g_print("Dynamic loading of plugins is not supported on this platform!\n");
352  return;
353  }
354 
355  g_print("Load modules from %s\n", REMMINA_RUNTIME_PLUGINDIR);
356  dir = g_dir_open(REMMINA_RUNTIME_PLUGINDIR, 0, NULL);
357 
358  if (dir == NULL)
359  return;
360  while ((name = g_dir_read_name(dir)) != NULL) {
361  if ((ptr = strrchr(name, '.')) == NULL)
362  continue;
363  ptr++;
364  fullpath = g_strdup_printf(REMMINA_RUNTIME_PLUGINDIR "/%s", name);
366  g_ptr_array_add(alternative_language_plugins, g_strdup_printf(REMMINA_RUNTIME_PLUGINDIR "/%s", name));
367  g_free(fullpath);
368  continue;
369  }
370  remmina_plugin_native_load(&remmina_plugin_manager_service, fullpath);
371  g_free(fullpath);
372  }
373  g_dir_close(dir);
374 
375  while (alternative_language_plugins->len > 0) {
376  gboolean has_loaded = FALSE;
377  gchar* name = (gchar*)g_ptr_array_remove_index(alternative_language_plugins, 0);
378  const gchar* ext = get_filename_ext(name);
379 
380  for (j = 0; j < remmina_plugin_table->len && !has_loaded; j++) {
381  plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, j);
384  const gchar** supported_extention = wrapper_plugin->supported_extentions;
385  while (*supported_extention) {
386  if (g_str_equal(*supported_extention, ext)) {
387  has_loaded = wrapper_plugin->load(wrapper_plugin, name);
388  if (has_loaded) {
389  break;
390  }
391  }
392  supported_extention++;
393  }
394  if (has_loaded) break;
395  }
396  }
397 
398  if (!has_loaded) {
399  g_print("%s: Skip unsupported file type '%s'\n", name, ext);
400  }
401 
402  g_free(name);
403  }
404 
405  /* Now all secret plugins needs to initialize, following their init_order.
406  * The 1st plugin which will initialize correctly will be
407  * the default remmina_secret_plugin */
408 
409  if (remmina_secret_plugin)
410  g_print("Internal ERROR: remmina_secret_plugin must be null here\n");
411 
412  secret_plugins = NULL;
413  for (i = 0; i < remmina_plugin_table->len; i++) {
414  plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, i);
415  if (plugin->type == REMMINA_PLUGIN_TYPE_SECRET) {
416  secret_plugins = g_slist_insert_sorted(secret_plugins, (gpointer)plugin, compare_secret_plugin_init_order);
417  }
418  }
419 
420  sple = secret_plugins;
421  while(sple != NULL) {
422  sp = (RemminaSecretPlugin*)sple->data;
423  if (sp->init(sp)) {
424  g_print("The %s secret plugin has been initialized and it will be your default secret plugin\n",
425  sp->name);
426  remmina_secret_plugin = sp;
427  break;
428  }
429  sple = sple->next;
430  }
431 
432  g_slist_free(secret_plugins);
433  g_ptr_array_free(alternative_language_plugins, TRUE);
434 }
435 
436 gboolean remmina_plugin_manager_loader_supported(const char *filetype) {
437  TRACE_CALL(__func__);
438  return g_str_equal(G_MODULE_SUFFIX, filetype);
439 }
440 
442 {
443  TRACE_CALL(__func__);
444  RemminaPlugin *plugin;
445  gint i;
446 
447  for (i = 0; i < remmina_plugin_table->len; i++) {
448  plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, i);
449  if (plugin->type == type && g_strcmp0(plugin->name, name) == 0) {
450  return plugin;
451  }
452  }
453  return NULL;
454 }
455 
457 {
458  if (setting->name == NULL) {
460  return "server";
462  return "password";
464  return "resolution";
465  return "missing_setting_name_into_plugin_RemminaProtocolSetting";
466  }
467  return setting->name;
468 }
469 
471 {
472  TRACE_CALL(__func__);
473  RemminaPlugin *plugin;
474  gint i;
475 
476  for (i = 0; i < remmina_plugin_table->len; i++) {
477  plugin = (RemminaPlugin*)g_ptr_array_index(remmina_plugin_table, i);
478  if (plugin->type == type) {
479  func((gchar*)plugin->name, plugin, data);
480  }
481  }
482 }
483 
484 /* A copy of remmina_plugin_manager_show and remmina_plugin_manager_show_for_each
485  * This is because we want to print the list of plugins, and their versions, to the standard output
486  * with the remmina command line option --full-version instead of using the plugins widget
487  ** @todo Investigate to use only GListStore and than pass the elements to be shown to 2 separate
488  * functions
489  * WARNING: GListStore is supported only from GLib 2.44 */
491 {
492  TRACE_CALL(__func__);
493 
494  g_print("%-20s%-16s%-64s%-10s\n", plugin->name,
495  _(remmina_plugin_type_name[plugin->type]),
496  g_dgettext(plugin->domain, plugin->description),
497  plugin->version);
498  return FALSE;
499 }
500 
502 {
503  TRACE_CALL(__func__);
504  g_print("%-20s%-16s%-64s%-10s\n", "NAME", "TYPE", "DESCRIPTION", "PLUGIN AND LIBRARY VERSION");
505  g_ptr_array_foreach(remmina_plugin_table, (GFunc)remmina_plugin_manager_show_for_each_stdout, NULL);
506 }
507 
508 static gboolean remmina_plugin_manager_show_for_each(RemminaPlugin *plugin, GtkListStore *store)
509 {
510  TRACE_CALL(__func__);
511  GtkTreeIter iter;
512 
513  gtk_list_store_append(store, &iter);
514  gtk_list_store_set(store, &iter, 0, plugin->name, 1, _(remmina_plugin_type_name[plugin->type]), 2,
515  g_dgettext(plugin->domain, plugin->description), 3, plugin->version, -1);
516  return FALSE;
517 }
518 
519 void remmina_plugin_manager_show(GtkWindow *parent)
520 {
521  TRACE_CALL(__func__);
522  GtkWidget *dialog;
523  GtkWidget *scrolledwindow;
524  GtkWidget *tree;
525  GtkCellRenderer *renderer;
526  GtkTreeViewColumn *column;
527  GtkListStore *store;
528 
529  dialog = gtk_dialog_new_with_buttons(_("Plugins"), parent, GTK_DIALOG_MODAL, _("_OK"), GTK_RESPONSE_ACCEPT, NULL);
530  g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), dialog);
531  gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 350);
532 
533  scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
534  gtk_widget_show(scrolledwindow);
535  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
536  gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), scrolledwindow, TRUE, TRUE, 0);
537 
538  tree = gtk_tree_view_new();
539  gtk_container_add(GTK_CONTAINER(scrolledwindow), tree);
540  gtk_widget_show(tree);
541 
542  store = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
543  g_ptr_array_foreach(remmina_plugin_table, (GFunc)remmina_plugin_manager_show_for_each, store);
544  gtk_tree_view_set_model(GTK_TREE_VIEW(tree), GTK_TREE_MODEL(store));
545 
546  renderer = gtk_cell_renderer_text_new();
547  column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "text", 0, NULL);
548  gtk_tree_view_column_set_resizable(column, TRUE);
549  gtk_tree_view_column_set_sort_column_id(column, 0);
550  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
551 
552  renderer = gtk_cell_renderer_text_new();
553  column = gtk_tree_view_column_new_with_attributes(_("Type"), renderer, "text", 1, NULL);
554  gtk_tree_view_column_set_resizable(column, TRUE);
555  gtk_tree_view_column_set_sort_column_id(column, 1);
556  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
557 
558  renderer = gtk_cell_renderer_text_new();
559  column = gtk_tree_view_column_new_with_attributes(_("Description"), renderer, "text", 2, NULL);
560  gtk_tree_view_column_set_resizable(column, TRUE);
561  gtk_tree_view_column_set_sort_column_id(column, 2);
562  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
563 
564  renderer = gtk_cell_renderer_text_new();
565  column = gtk_tree_view_column_new_with_attributes(_("Version"), renderer, "text", 3, NULL);
566  gtk_tree_view_column_set_resizable(column, TRUE);
567  gtk_tree_view_column_set_sort_column_id(column, 3);
568  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
569 
570  gtk_widget_show(dialog);
571 }
572 
574 {
575  TRACE_CALL(__func__);
576  RemminaFilePlugin *plugin;
577  gint i;
578 
579  for (i = 0; i < remmina_plugin_table->len; i++) {
580  plugin = (RemminaFilePlugin*)g_ptr_array_index(remmina_plugin_table, i);
581 
582  if (plugin->type != REMMINA_PLUGIN_TYPE_FILE)
583  continue;
584 
585  if (plugin->import_test_func(plugin, file)) {
586  return plugin;
587  }
588  }
589  return NULL;
590 }
591 
593 {
594  TRACE_CALL(__func__);
595  RemminaFilePlugin *plugin;
596  gint i;
597 
598  for (i = 0; i < remmina_plugin_table->len; i++) {
599  plugin = (RemminaFilePlugin*)g_ptr_array_index(remmina_plugin_table, i);
600  if (plugin->type != REMMINA_PLUGIN_TYPE_FILE)
601  continue;
602  if (plugin->export_test_func(plugin, remminafile)) {
603  return plugin;
604  }
605  }
606  return NULL;
607 }
608 
610 {
611  TRACE_CALL(__func__);
612  return remmina_secret_plugin;
613 }
614 
616 {
617  TRACE_CALL(__func__);
618  const RemminaProtocolFeature *feature;
619  RemminaProtocolPlugin* plugin;
620 
622 
623  if (plugin == NULL) {
624  return FALSE;
625  }
626 
627  for (feature = plugin->features; feature && feature->type; feature++) {
628  if (feature->type == ftype)
629  return TRUE;
630  }
631  return FALSE;
632 }
633 
635 {
636  TRACE_CALL(__func__);
637  GHashTable *pht;
638 
639  if (encrypted_settings_cache == NULL)
640  return FALSE;
641 
642  if (!(pht = g_hash_table_lookup(encrypted_settings_cache, pp->name)))
643  return FALSE;
644 
645  if (!g_hash_table_lookup(pht, setting))
646  return FALSE;
647 
648  return TRUE;
649 }
gboolean remmina_protocol_widget_get_expand(RemminaProtocolWidget *gp)
void remmina_protocol_widget_panel_show(RemminaProtocolWidget *gp)
static RemminaSecretPlugin * remmina_secret_plugin
-
const gchar * remmina_plugin_manager_get_canonical_setting_name(const RemminaProtocolSetting *setting)
+
const gchar * remmina_plugin_manager_get_canonical_setting_name(const RemminaProtocolSetting *setting)
void remmina_protocol_widget_set_error(RemminaProtocolWidget *gp, const gchar *fmt,...)
-
gboolean remmina_plugin_manager_is_encrypted_setting(RemminaProtocolPlugin *pp, const char *setting)
+
gboolean remmina_plugin_manager_is_encrypted_setting(RemminaProtocolPlugin *pp, const char *setting)
gint remmina_pref_get_scale_quality(void)
const gchar * remmina_file_get_string(RemminaFile *remminafile, const gchar *setting)
Definition: remmina_file.c:516
@@ -105,7 +105,7 @@ $(document).ready(function(){initNavTree('remmina__plugin__manager_8c_source.htm
void remmina_protocol_widget_chat_receive(RemminaProtocolWidget *gp, const gchar *text)
gint remmina_protocol_widget_get_width(RemminaProtocolWidget *gp)
gboolean remmina_plugin_native_load(RemminaPluginService *service, const char *name)
-
gboolean remmina_plugin_manager_loader_supported(const char *filetype)
+
gboolean remmina_plugin_manager_loader_supported(const char *filetype)
gint remmina_protocol_widget_get_profile_remote_width(RemminaProtocolWidget *gp)
gint remmina_pref_get_sshtunnel_port(void)
gint remmina_protocol_widget_panel_auth(RemminaProtocolWidget *gp, RemminaMessagePanelFlags pflags, const gchar *title, const gchar *default_username, const gchar *default_password, const gchar *default_domain, const gchar *password_prompt)
@@ -125,7 +125,7 @@ $(document).ready(function(){initNavTree('remmina__plugin__manager_8c_source.htm
void remmina_protocol_widget_register_hostkey(RemminaProtocolWidget *gp, GtkWidget *widget)
gboolean remmina_protocol_widget_start_reverse_tunnel(RemminaProtocolWidget *gp, gint local_port)
G_BEGIN_DECLS typedef gboolean(* RemminaPluginFunc)(gchar *name, RemminaPlugin *plugin, gpointer data)
-
void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data)
+
void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data)
gchar * remmina_protocol_widget_get_domain(RemminaProtocolWidget *gp)
GtkWidget * rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: rcw.c:4499
void remmina_widget_pool_register(GtkWidget *widget)
@@ -137,12 +137,12 @@ $(document).ready(function(){initNavTree('remmina__plugin__manager_8c_source.htm
gint remmina_pref_get_ssh_loglevel(void)
const RemminaProtocolFeature * features
Definition: plugin.h:77
-
RemminaFilePlugin * remmina_plugin_manager_get_export_file_handler(RemminaFile *remminafile)
+
RemminaFilePlugin * remmina_plugin_manager_get_export_file_handler(RemminaFile *remminafile)
const gchar * domain
Definition: plugin.h:60
void _remmina_warning(const gchar *fun, const gchar *fmt,...)
Definition: remmina_log.c:286
-
RemminaFilePlugin * remmina_plugin_manager_get_import_file_handler(const gchar *file)
-
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
+
RemminaFilePlugin * remmina_plugin_manager_get_import_file_handler(const gchar *file)
+
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
gchar * remmina_protocol_widget_get_cacert(RemminaProtocolWidget *gp)
void remmina_protocol_widget_lock_dynres(RemminaProtocolWidget *gp)
gint remmina_protocol_widget_get_height(RemminaProtocolWidget *gp)
@@ -160,14 +160,14 @@ $(document).ready(function(){initNavTree('remmina__plugin__manager_8c_source.htm
void _remmina_debug(const gchar *fun, const gchar *fmt,...)
Print a string in the Remmina Debug Windows and in the terminal.
Definition: remmina_log.c:259
void remmina_protocol_widget_set_display(RemminaProtocolWidget *gp, gint display)
void remmina_protocol_widget_set_width(RemminaProtocolWidget *gp, gint width)
-
static gboolean remmina_plugin_manager_show_for_each(RemminaPlugin *plugin, GtkListStore *store)
+
static gboolean remmina_plugin_manager_show_for_each(RemminaPlugin *plugin, GtkListStore *store)
gchar * remmina_pref_get_value(const gchar *key)
gboolean remmina_pref_get_ssh_parseconfig(void)
const gchar * remmina_protocol_widget_get_name(RemminaProtocolWidget *gp)
const char * get_filename_ext(const char *filename)
void remmina_protocol_widget_panel_show_listen(RemminaProtocolWidget *gp, gint port)
-
void remmina_plugin_manager_show(GtkWindow *parent)
+
void remmina_plugin_manager_show(GtkWindow *parent)
gboolean remmina_masterthread_exec_is_main_thread()
guint * remmina_pref_keymap_get_table(const gchar *keymap)
@@ -181,7 +181,7 @@ $(document).ready(function(){initNavTree('remmina__plugin__manager_8c_source.htm
gboolean(* export_test_func)(struct _RemminaFilePlugin *instance, RemminaFile *file)
Definition: plugin.h:109
const gchar * name
Definition: plugin.h:137
gint remmina_protocol_widget_panel_changed_certificate(RemminaProtocolWidget *gp, const gchar *subject, const gchar *issuer, const gchar *new_fingerprint, const gchar *old_fingerprint)
-
gboolean remmina_plugin_manager_query_feature_by_type(RemminaPluginType ptype, const gchar *name, RemminaProtocolFeatureType ftype)
+
gboolean remmina_plugin_manager_query_feature_by_type(RemminaPluginType ptype, const gchar *name, RemminaProtocolFeatureType ftype)
gchar * remmina_protocol_widget_get_clientcert(RemminaProtocolWidget *gp)
void remmina_file_set_int(RemminaFile *remminafile, const gchar *setting, gint value)
Definition: remmina_file.c:585
void remmina_protocol_widget_unlock_dynres(RemminaProtocolWidget *gp)
@@ -211,7 +211,7 @@ $(document).ready(function(){initNavTree('remmina__plugin__manager_8c_source.htm
gint remmina_file_get_int(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: remmina_file.c:603
gboolean remmina_protocol_widget_start_xport_tunnel(RemminaProtocolWidget *gp, RemminaXPortTunnelInitFunc init_func)
gboolean(* import_test_func)(struct _RemminaFilePlugin *instance, const gchar *from_file)
Definition: plugin.h:107
-
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
+
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
void remmina_protocol_widget_signal_connection_opened(RemminaProtocolWidget *gp)
static GPtrArray * remmina_plugin_table
@@ -226,7 +226,7 @@ $(document).ready(function(){initNavTree('remmina__plugin__manager_8c_source.htm
void remmina_protocol_widget_set_expand(RemminaProtocolWidget *gp, gboolean expand)
void remmina_log_printf(const gchar *fmt,...)
Definition: remmina_log.c:411
-
static gboolean remmina_plugin_manager_show_for_each_stdout(RemminaPlugin *plugin)
+
static gboolean remmina_plugin_manager_show_for_each_stdout(RemminaPlugin *plugin)
void remmina_file_set_string(RemminaFile *remminafile, const gchar *setting, const gchar *value)
Definition: remmina_file.c:469
void remmina_protocol_widget_send_keys_signals(GtkWidget *widget, const guint *keyvals, int keyvals_length, GdkEventType action)
void remmina_log_print(const gchar *text)
Definition: remmina_log.c:197
@@ -238,7 +238,7 @@ $(document).ready(function(){initNavTree('remmina__plugin__manager_8c_source.htm
void remmina_public_get_server_port(const gchar *server, gint defaultport, gchar **host, gint *port)
static gboolean remmina_plugin_manager_register_plugin(RemminaPlugin *plugin)
-
void remmina_plugin_manager_show_stdout()
+
void remmina_plugin_manager_show_stdout()
RemminaFile * remmina_protocol_widget_get_file(RemminaProtocolWidget *gp)
static void init_settings_cache(RemminaPlugin *plugin)
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
diff --git a/public/remmina__plugin__manager_8h.html b/public/remmina__plugin__manager_8h.html index ec4a7e39a..dd3487962 100644 --- a/public/remmina__plugin__manager_8h.html +++ b/public/remmina__plugin__manager_8h.html @@ -241,7 +241,7 @@ Variables
-

Definition at line 468 of file remmina_plugin_manager.c.

+

Definition at line 470 of file remmina_plugin_manager.c.

@@ -295,7 +295,7 @@ Variables
-

Definition at line 454 of file remmina_plugin_manager.c.

+

Definition at line 456 of file remmina_plugin_manager.c.

@@ -315,7 +315,7 @@ Variables
-

Definition at line 590 of file remmina_plugin_manager.c.

+

Definition at line 592 of file remmina_plugin_manager.c.

@@ -335,7 +335,7 @@ Variables
-

Definition at line 571 of file remmina_plugin_manager.c.

+

Definition at line 573 of file remmina_plugin_manager.c.

@@ -365,7 +365,7 @@ Variables
-

Definition at line 439 of file remmina_plugin_manager.c.

+

Definition at line 441 of file remmina_plugin_manager.c.

@@ -385,7 +385,7 @@ Variables
-

Definition at line 607 of file remmina_plugin_manager.c.

+

Definition at line 609 of file remmina_plugin_manager.c.

@@ -435,7 +435,7 @@ Variables
-

Definition at line 632 of file remmina_plugin_manager.c.

+

Definition at line 634 of file remmina_plugin_manager.c.

@@ -455,7 +455,7 @@ Variables
-

Definition at line 434 of file remmina_plugin_manager.c.

+

Definition at line 436 of file remmina_plugin_manager.c.

@@ -491,7 +491,7 @@ Variables
-

Definition at line 613 of file remmina_plugin_manager.c.

+

Definition at line 615 of file remmina_plugin_manager.c.

@@ -511,7 +511,7 @@ Variables
-

Definition at line 517 of file remmina_plugin_manager.c.

+

Definition at line 519 of file remmina_plugin_manager.c.

@@ -530,7 +530,7 @@ Variables
-

Definition at line 499 of file remmina_plugin_manager.c.

+

Definition at line 501 of file remmina_plugin_manager.c.

diff --git a/public/remmina__plugin__manager_8h_source.html b/public/remmina__plugin__manager_8h_source.html index 85bd06101..4c3375a74 100644 --- a/public/remmina__plugin__manager_8h_source.html +++ b/public/remmina__plugin__manager_8h_source.html @@ -87,28 +87,28 @@ $(document).ready(function(){initNavTree('remmina__plugin__manager_8h_source.htm
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2010 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2023 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 #pragma once
38 
39 #include "remmina/plugin.h"
40 
41 G_BEGIN_DECLS
42 
43 typedef gboolean (*RemminaPluginFunc)(gchar *name, RemminaPlugin *plugin, gpointer data);
44 
49 void remmina_plugin_manager_show(GtkWindow *parent);
58 
60 
61 typedef gboolean (*RemminaPluginLoaderFunc)(RemminaPluginService*, const gchar* name);
62 
63 typedef struct {
64  char* filetype;
67 
68 gboolean remmina_plugin_manager_loader_supported(const char *filetype);
70 
71 G_END_DECLS
-
void remmina_plugin_manager_show_stdout()
+
void remmina_plugin_manager_show_stdout()
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:44
G_BEGIN_DECLS typedef gboolean(* RemminaPluginFunc)(gchar *name, RemminaPlugin *plugin, gpointer data)
-
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
-
void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data)
+
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
+
void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data)
RemminaPluginService remmina_plugin_manager_service
-
RemminaFilePlugin * remmina_plugin_manager_get_export_file_handler(RemminaFile *remminafile)
-
RemminaFilePlugin * remmina_plugin_manager_get_import_file_handler(const gchar *file)
+
RemminaFilePlugin * remmina_plugin_manager_get_export_file_handler(RemminaFile *remminafile)
+
RemminaFilePlugin * remmina_plugin_manager_get_import_file_handler(const gchar *file)
RemminaPluginLoaderFunc func
void remmina_plugin_manager_for_each_plugin_stdout(RemminaPluginType type, RemminaPluginFunc func, gpointer data)
-
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
+
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
gboolean(* RemminaPluginLoaderFunc)(RemminaPluginService *, const gchar *name)
gboolean remmina_gtksocket_available()
void remmina_plugin_manager_add_loader(char *filetype, RemminaPluginLoaderFunc func)
-
const gchar * remmina_plugin_manager_get_canonical_setting_name(const RemminaProtocolSetting *setting)
-
gboolean remmina_plugin_manager_is_encrypted_setting(RemminaProtocolPlugin *pp, const char *setting)
-
gboolean remmina_plugin_manager_query_feature_by_type(RemminaPluginType ptype, const gchar *name, RemminaProtocolFeatureType ftype)
-
void remmina_plugin_manager_show(GtkWindow *parent)
+
const gchar * remmina_plugin_manager_get_canonical_setting_name(const RemminaProtocolSetting *setting)
+
gboolean remmina_plugin_manager_is_encrypted_setting(RemminaProtocolPlugin *pp, const char *setting)
+
gboolean remmina_plugin_manager_query_feature_by_type(RemminaPluginType ptype, const gchar *name, RemminaProtocolFeatureType ftype)
+
void remmina_plugin_manager_show(GtkWindow *parent)
RemminaProtocolFeatureType
Definition: types.h:46
@@ -116,7 +116,7 @@ $(document).ready(function(){initNavTree('remmina__plugin__manager_8h_source.htm
RemminaPluginType
Definition: plugin.h:46
-
gboolean remmina_plugin_manager_loader_supported(const char *filetype)
+
gboolean remmina_plugin_manager_loader_supported(const char *filetype)
diff --git a/public/remmina__pref__dialog_8c_source.html b/public/remmina__pref__dialog_8c_source.html index 9401aa3c0..6177c80eb 100644 --- a/public/remmina__pref__dialog_8c_source.html +++ b/public/remmina__pref__dialog_8c_source.html @@ -122,7 +122,7 @@ $(document).ready(function(){initNavTree('remmina__pref__dialog_8c_source.html',
GtkSwitch * switch_security_audit
gboolean ssh_parseconfig
Definition: remmina_pref.h:167
static void remmina_pref_dialog_init(void)
-
void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data)
+
void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data)
GtkEntry * entry_options_file_name
GtkCheckButton * checkbutton_appearance_fullscreen_on_auto
GtkLabel * label_terminal_foreground
diff --git a/public/remmina__protocol__widget_8c_source.html b/public/remmina__protocol__widget_8c_source.html index 895d05df4..81ca5fbae 100644 --- a/public/remmina__protocol__widget_8c_source.html +++ b/public/remmina__protocol__widget_8c_source.html @@ -328,7 +328,7 @@ $(document).ready(function(){initNavTree('remmina__protocol__widget_8c_source.ht
panel_type
gboolean(* query_feature)(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: plugin.h:82
static gboolean remmina_protocol_widget_tunnel_destroy(RemminaSSHTunnel *tunnel, gpointer data)
-
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
+
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
gchar * default_password
void remmina_protocol_widget_signal_connection_opened(RemminaProtocolWidget *gp)
diff --git a/public/remmina__stats_8c_source.html b/public/remmina__stats_8c_source.html index 907415406..e497ae4ab 100644 --- a/public/remmina__stats_8c_source.html +++ b/public/remmina__stats_8c_source.html @@ -96,7 +96,7 @@ $(document).ready(function(){initNavTree('remmina__stats_8c_source.html','');});
gchar datestr
-
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
+
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
gint pcount
Date in string format in the proto_date hash table.
gboolean(* is_service_available)(struct _RemminaSecretPlugin *instance)
Definition: plugin.h:144
GHashTable * proto_count
-- cgit v1.2.3