Remmina - The GTK+ Remote Desktop Client  v1.4.33
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
remmina.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK Remote Desktop Client
3  * Copyright (C) 2014-2023 Antenore Gatta, Giovanni Panozzo
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  * In addition, as a special exception, the copyright holders give
21  * permission to link the code of portions of this program with the
22  * OpenSSL library under certain conditions as described in each
23  * individual source file, and distribute linked combinations
24  * including the two.
25  * You must obey the GNU General Public License in all respects
26  * for all of the code used other than OpenSSL. * If you modify
27  * file(s) with this exception, you may extend this exception to your
28  * version of the file(s), but you are not obligated to do so. * If you
29  * do not wish to do so, delete this exception statement from your
30  * version. * If you delete this exception statement from all source
31  * files in the program, then also delete it here.
32  *
33  */
34 
35 #include <gdk/gdk.h>
36 
37 #define G_LOG_USE_STRUCTURED
38 #ifndef G_LOG_DOMAIN
39 #define G_LOG_DOMAIN ((gchar*)"remmina")
40 #endif /* G_LOG_DOMAIN */
41 #ifdef GDK_WINDOWING_X11
42 #include <gdk/gdkx.h>
43 #elif defined(GDK_WINDOWING_WAYLAND)
44 #include <gdk/gdkwayland.h>
45 #endif
46 #include <gio/gio.h>
47 #include <glib/gi18n.h>
48 #include <stdlib.h>
49 
50 #include "config.h"
51 #include "remmina_sodium.h"
52 #include "remmina.h"
53 #include "remmina_exec.h"
54 #include "remmina_file_manager.h"
55 #include "remmina_icon.h"
56 #include "remmina_main.h"
58 #include "remmina_plugin_manager.h"
59 #include "remmina_plugin_native.h"
60 #ifdef WITH_PYTHONLIBS
61 #include "remmina_plugin_python.h"
62 #endif
63 #include "remmina_pref.h"
64 #include "remmina_public.h"
65 #include "remmina_sftp_plugin.h"
66 #include "remmina_ssh_plugin.h"
67 #include "remmina_widget_pool.h"
69 
70 #ifdef HAVE_ERRNO_H
71 #include <errno.h>
72 #endif
73 #include <pthread.h>
74 #ifdef HAVE_LIBGCRYPT
75 #include <gcrypt.h>
76 # if GCRYPT_VERSION_NUMBER < 0x010600
78 #endif /* !GCRYPT_VERSION_NUMBER */
79 #endif /* HAVE_LIBGCRYPT */
80 
81 #ifdef HAVE_LIBGCRYPT
82 # if GCRYPT_VERSION_NUMBER < 0x010600
84 #endif /* !GCRYPT_VERSION_NUMBER */
85 #endif /* HAVE_LIBGCRYPT */
86 
87 gboolean kioskmode;
88 gboolean disabletoolbar;
89 gboolean fullscreen;
90 gboolean extrahardening;
91 gboolean disabletrayicon;
92 
93 static GOptionEntry remmina_options[] =
94 {
95  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
96  { "about", 'a', 0, G_OPTION_ARG_NONE, NULL, N_("Show \'About\'"), NULL },
97  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
98  { "connect", 'c', 0, G_OPTION_ARG_FILENAME_ARRAY, NULL, N_("Connect either to a desktop described in a file (.remmina or a filetype supported by a plugin) or a supported URI (RDP, VNC, SSH or SPICE)"), N_("FILE") },
99  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
100  { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, NULL, N_("Connect to a desktop described in a file (.remmina or a filetype supported by a plugin)"), N_("FILE") },
101  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
102  { "edit", 'e', 0, G_OPTION_ARG_FILENAME_ARRAY, NULL, N_("Edit desktop connection described in file (.remmina or a filetype supported by plugin)"), N_("FILE") },
103  { "help", '?', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, NULL, NULL, NULL },
104  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
105  { "kiosk", 'k', 0, G_OPTION_ARG_NONE, NULL, N_("Start in kiosk mode"), NULL },
106  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
107  { "new", 'n', 0, G_OPTION_ARG_NONE, NULL, N_("Create new connection profile"), NULL },
108  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
109  { "pref", 'p', 0, G_OPTION_ARG_STRING, NULL, N_("Show preferences"), N_("TABINDEX") },
110 #if 0
111  /* This option was used mainly for telepathy, let's keep it if we will need it in the future */
112  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
113  //{ "plugin", 'x', 0, G_OPTION_ARG_STRING, NULL, N_("Run a plugin"), N_("PLUGIN") },
114 #endif
115  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
116  { "quit", 'q', 0, G_OPTION_ARG_NONE, NULL, N_("Quit"), NULL },
117  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
118  { "server", 's', 0, G_OPTION_ARG_STRING, NULL, N_("Use default server name (for --new)"), N_("SERVER") },
119  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
120  { "protocol", 't', 0, G_OPTION_ARG_STRING, NULL, N_("Use default protocol (for --new)"), N_("PROTOCOL") },
121  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
122  { "icon", 'i', 0, G_OPTION_ARG_NONE, NULL, N_("Start in tray"), NULL },
123  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
124  { "version", 'v', 0, G_OPTION_ARG_NONE, NULL, N_("Show the application version"), NULL },
125  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
126  { "full-version", 'V', 0, G_OPTION_ARG_NONE, NULL, N_("Show version of the application and its plugins"), NULL },
127  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
128  { "update-profile", 0, 0, G_OPTION_ARG_FILENAME, NULL, N_("Modify connection profile (requires --set-option)"), NULL },
129  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
130  { "set-option", 0, 0, G_OPTION_ARG_STRING_ARRAY, NULL, N_("Set one or more profile settings, to be used with --update-profile"), NULL },
131  { "encrypt-password", 0, 0, G_OPTION_ARG_NONE, NULL, N_("Encrypt a password"), NULL },
132  { "disable-toolbar", 0, 0, G_OPTION_ARG_NONE, NULL, N_("Disable toolbar"), NULL },
133  { "enable-fullscreen", 0, 0, G_OPTION_ARG_NONE, NULL, N_("Enable fullscreen"), NULL },
134  { "enable-extra-hardening", 0, 0, G_OPTION_ARG_NONE, NULL, N_("Enable extra hardening (disable closing confirmation, disable unsafe shortcut keys, hide tabs, hide search bar)"), NULL },
135  { "no-tray-icon", 0, 0, G_OPTION_ARG_NONE, NULL, N_("Disable tray icon"), NULL },
136  { NULL }
137 };
138 
139 #ifdef WITH_LIBGCRYPT
140 static int
141 _gpg_error_to_errno(gcry_error_t e)
142 {
143  /* be lazy right now */
144  if (e == GPG_ERR_NO_ERROR)
145  return 0;
146  else
147  return EINVAL;
148 }
149 #endif /* !WITH_LIBGCRYPT */
150 
151 static gint remmina_on_command_line(GApplication *app, GApplicationCommandLine *cmdline)
152 {
153  TRACE_CALL(__func__);
154 
155  gint status = 0;
156  gboolean executed = FALSE;
157  GVariantDict *opts;
158  gchar *str;
159  const gchar **files;
160  const gchar **remaining_args;
161  gchar *protocol;
162  gchar *server;
163 
164 #if SODIUM_VERSION_INT >= 90200
166 #endif
167  opts = g_application_command_line_get_options_dict(cmdline);
168 
169  if (g_variant_dict_lookup_value(opts, "disable-toolbar", NULL)) {
170  disabletoolbar = TRUE;
171  }
172 
173  if (g_variant_dict_lookup_value(opts, "enable-fullscreen", NULL)) {
174  fullscreen = TRUE;
175  }
176 
177  if (g_variant_dict_lookup_value(opts, "enable-extra-hardening", NULL)) {
178  extrahardening = TRUE;
179  }
180 
181  if (g_variant_dict_lookup_value(opts, "no-tray-icon", NULL)) {
182  disabletrayicon = TRUE;
183  }
184 
186 
187  if (g_variant_dict_lookup_value(opts, "quit", NULL)) {
189  executed = TRUE;
190  status = 1;
191  }
192 
193  if (g_variant_dict_lookup_value(opts, "about", NULL)) {
195  executed = TRUE;
196  }
197 
201  if (g_variant_dict_lookup(opts, "connect", "^aay", &files)) {
202  if (files)
203  for (gint i = 0; files[i]; i++) {
204  g_debug ("Connecting to: %s", files[i]);
206  }
207  executed = TRUE;
208  }
209 
210  if (g_variant_dict_lookup(opts, G_OPTION_REMAINING, "^a&ay", &remaining_args)) {
211  remmina_exec_command(REMMINA_COMMAND_CONNECT, remaining_args[0]);
212  g_free(remaining_args);
213  executed = TRUE;
214  }
215 
216  if (g_variant_dict_lookup(opts, "edit", "^aay", &files)) {
217  if (files)
218  for (gint i = 0; files[i]; i++) {
219  g_debug ("Editing file: %s", files[i]);
221  }
222  //remmina_exec_command(REMMINA_COMMAND_EDIT, str);
223  //g_free(str);
224  executed = TRUE;
225  }
226 
227  if (g_variant_dict_lookup_value(opts, "kiosk", NULL)) {
228  kioskmode = TRUE;
230  executed = TRUE;
231  }
232 
233  if (g_variant_dict_lookup_value(opts, "new", NULL)) {
234  if (!g_variant_dict_lookup(opts, "protocol", "&s", &protocol))
235  protocol = NULL;
236 
237  if (g_variant_dict_lookup(opts, "server", "&s", &server))
238  str = g_strdup_printf("%s,%s", protocol, server);
239  else
240  str = g_strdup(protocol);
241 
243  g_free(str);
244  executed = TRUE;
245  }
246 
247  if (g_variant_dict_lookup(opts, "pref", "&s", &str)) {
249  executed = TRUE;
250  }
251 
252  if (g_variant_dict_lookup(opts, "plugin", "&s", &str)) {
254  executed = TRUE;
255  }
256 
257  if (g_variant_dict_lookup_value(opts, "icon", NULL)) {
259  executed = TRUE;
260  }
261 
262  if (g_variant_dict_lookup_value(opts, "encrypt-password", NULL)) {
264  executed = TRUE;
265  status = 1;
266  }
267 
268  if (!executed)
270 
271  return status;
272 }
273 
274 static void remmina_on_startup(GApplication *app)
275 {
276  TRACE_CALL(__func__);
277 
278  RemminaSecretPlugin *secret_plugin;
279 
284 
285  g_set_application_name("Remmina");
286  gtk_window_set_default_icon_name(REMMINA_APP_ID);
287 
288  /* Setting the X11 program class (WM_CLASS) is necessary to group
289  * windows with .desktop file which has the same StartupWMClass */
290  gdk_set_program_class(REMMINA_APP_ID);
291 
292  gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(),
293  REMMINA_RUNTIME_DATADIR G_DIR_SEPARATOR_S "icons");
294  g_application_hold(app);
295 
296  /* Check for secret plugin and service initialization and show console warnings if
297  * something is missing */
299  if (!secret_plugin)
300  g_print("Warning: Remmina is running without a secret plugin. Passwords will be saved in a less secure way.\n");
301  else
302  if (!secret_plugin->is_service_available(secret_plugin))
303  g_print("Warning: Remmina is running with a secrecy plugin, but it cannot connect to a secrecy service.\n");
304 
306 }
307 
308 static gint remmina_on_local_cmdline(GApplication *app, GVariantDict *opts, gpointer user_data)
309 {
310  TRACE_CALL(__func__);
311 
312  int status = -1;
313  gchar *str;
314  gchar **settings;
315 
316  /* Here you handle any command line options that you want to be executed
317  * in the local instance (the non-unique instance) */
318 
319  if (g_variant_dict_lookup_value(opts, "version", NULL)) {
321  status = 0;
322  }
323 
324  if (g_variant_dict_lookup_value(opts, "full-version", NULL)) {
326  status = 0;
327  }
328 
329  if (g_variant_dict_lookup(opts, "update-profile", "^&ay", &str)) { /* ^&ay no need to free */
330  if (g_variant_dict_lookup(opts, "set-option", "^a&s", &settings)) {
331  if (settings != NULL) {
332  status = remmina_exec_set_setting(str, settings);
333  g_free(settings);
334  } else {
335  status = 1;
336  }
337  } else {
338  status = 1;
339  g_print("Error: --update-profile requires --set-option\n");
340  }
341  }
342 
343  /* Returning a non negative value here makes the application exit */
344  return status;
345 }
346 
347 int main(int argc, char *argv[])
348 {
349  TRACE_CALL(__func__);
350  GtkApplication *app;
351  const gchar *app_id;
352  int status;
353 
354  g_unsetenv("GDK_CORE_DEVICE_EVENTS");
355 
356  // Checking for environment variable "G_MESSAGES_DEBUG"
357  // Give the less familiar with GLib a tip on where to get
358  // more debugging info.
359  if(!getenv("G_MESSAGES_DEBUG")) {
360  /* TRANSLATORS:
361  * This link should point to a resource explaining how to get Remmina
362  * to log more verbose statements.
363  */
364  g_message(_("Remmina does not log all output statements. "
365  "Turn on more verbose output by using "
366  "\"G_MESSAGES_DEBUG=all\" as an environment variable.\n"
367  "More info available on the Remmina wiki at:\n"
368  "https://gitlab.com/Remmina/Remmina/-/wikis/Usage/Remmina-debugging"
369  ));
370  }
371 
372  /* Enable wayland backend only after GTK 3.22.27 or the clipboard
373  * will not work. See GTK bug 790031 */
374  if (remmina_gtk_check_version(3, 22, 27))
375  gdk_set_allowed_backends("wayland,x11,broadway,quartz,mir");
376  else
377  gdk_set_allowed_backends("x11,broadway,quartz,mir");
378 
380 
381  bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
382  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
383  textdomain(GETTEXT_PACKAGE);
384 
385 #ifdef HAVE_LIBGCRYPT
386 # if GCRYPT_VERSION_NUMBER < 0x010600
387  gcry_error_t e;
389  if ((e = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread)) != GPG_ERR_NO_ERROR)
390  return -1;
392  }
393 #endif /* !GCRYPT_VERSION_NUMBER */
394  gcry_check_version(NULL);
395  gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
396  gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
397 #endif /* !HAVE_LIBGCRYPT */
398 
399  /* Initialize some Remmina parts needed also on a local instance for correct handle-local-options */
402 
404 
405 
406 
407  app_id = g_application_id_is_valid(REMMINA_APP_ID) ? REMMINA_APP_ID : NULL;
408  app = gtk_application_new(app_id, G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_CAN_OVERRIDE_APP_ID);
409 #if !GTK_CHECK_VERSION(4, 0, 0) /* This is not needed anymore starting from GTK 4 */
410  g_set_prgname(app_id);
411 #endif
412  g_application_add_main_option_entries(G_APPLICATION(app), remmina_options);
413 #if GLIB_CHECK_VERSION(2,56,0)
414  gchar *summary = g_strdup_printf ("%s %s", app_id, VERSION);
415  g_application_set_option_context_summary (G_APPLICATION(app), summary);
416  g_free(summary);
417  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
418  g_application_set_option_context_parameter_string (G_APPLICATION(app), _("- or protocol://username:encryptedpassword@host:port"));
419  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
420  g_application_set_option_context_description (G_APPLICATION(app),
421  _("Examples:\n"
422  "To connect using an existing connection profile, use:\n"
423  "\n"
424  "\tremmina -c FILE.remmina\n"
425  "\n"
426  "To quick connect using a URI:\n"
427  "\n"
428  "\tremmina -c rdp://username@server\n"
429  "\tremmina -c rdp://domain\\\\username@server\n"
430  "\tremmina -c vnc://username@server\n"
431  "\tremmina -c vnc://server?VncUsername=username\n"
432  "\tremmina -c ssh://user@server\n"
433  "\tremmina -c spice://server\n"
434  "\n"
435  "To quick connect using a URI along with an encrypted password:\n"
436  "\n"
437  "\tremmina -c rdp://username:encrypted-password@server\n"
438  "\tremmina -c vnc://username:encrypted-password@server\n"
439  "\tremmina -c vnc://server?VncUsername=username\\&VncPassword=encrypted-password\n"
440  "\n"
441  "To encrypt a password for use with a URI:\n"
442  "\n"
443  "\tremmina --encrypt-password\n"
444  "\n"
445  "To update username and password and set a different resolution mode of a Remmina connection profile, use:\n"
446  "\n"
447  "\techo \"username\\napassword\" | remmina --update-profile /PATH/TO/FOO.remmina --set-option username --set-option resolution_mode=2 --set-option password\n"));
448 #endif
449 
450  g_signal_connect(app, "startup", G_CALLBACK(remmina_on_startup), NULL);
451  g_signal_connect(app, "command-line", G_CALLBACK(remmina_on_command_line), NULL);
452  g_signal_connect(app, "handle-local-options", G_CALLBACK(remmina_on_local_cmdline), NULL);
453 
454 
455  g_application_set_inactivity_timeout(G_APPLICATION(app), 10000);
456  status = g_application_run(G_APPLICATION(app), argc, argv);
457  g_object_unref(app);
458 
459  return status;
460 }
int remmina_exec_set_setting(gchar *profilefilename, gchar **settings)
Definition: remmina_exec.c:180
static GOptionEntry remmina_options[]
Definition: remmina.c:93
static void remmina_on_startup(GApplication *app)
Definition: remmina.c:274
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)
int main(int argc, char *argv[])
Definition: remmina.c:347
static int _gpg_error_to_errno(gcry_error_t e)
Definition: remmina.c:141
static gint remmina_on_command_line(GApplication *app, GApplicationCommandLine *cmdline)
Definition: remmina.c:151
void remmina_file_manager_init(void)
It creates the Remmina data and cache folders.
void remmina_ssh_plugin_register(void)
void remmina_icon_init(void)
Definition: remmina_icon.c:362
static gint remmina_on_local_cmdline(GApplication *app, GVariantDict *opts, gpointer user_data)
Definition: remmina.c:308
gboolean disabletoolbar
Definition: remmina.c:88
static int gcrypt_thread_initialized
Definition: remmina.c:83
void remmina_exec_command(RemminaCommandType command, const gchar *data)
Definition: remmina_exec.c:382
gboolean fullscreen
Definition: remmina.c:89
void remmina_plugin_manager_init()
void remmina_widget_pool_init(void)
gboolean remmina_gtk_check_version(guint major, guint minor, guint micro)
gboolean disabletrayicon
Definition: remmina.c:91
void remmina_pref_init(void)
Definition: remmina_pref.c:220
GCRY_THREAD_OPTION_PTHREAD_IMPL
Definition: remmina.c:77
gboolean kioskmode
Definition: remmina.c:87
gboolean extrahardening
Definition: remmina.c:90
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
void remmina_sodium_init(void)