Remmina - The GTK+ Remote Desktop Client  v1.4.2
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
remmina.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK Remote Desktop Client
3  * Copyright (C) 2009-2010 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2020 Antenore Gatta, Giovanni Panozzo
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * In addition, as a special exception, the copyright holders give
23  * permission to link the code of portions of this program with the
24  * OpenSSL library under certain conditions as described in each
25  * individual source file, and distribute linked combinations
26  * including the two.
27  * You must obey the GNU General Public License in all respects
28  * for all of the code used other than OpenSSL. * If you modify
29  * file(s) with this exception, you may extend this exception to your
30  * version of the file(s), but you are not obligated to do so. * If you
31  * do not wish to do so, delete this exception statement from your
32  * version. * If you delete this exception statement from all source
33  * files in the program, then also delete it here.
34  *
35  */
36 
37 #include <gdk/gdkx.h>
38 #include <gio/gio.h>
39 #include <glib/gi18n.h>
40 #include <gtk/gtk.h>
41 #include <stdlib.h>
42 
43 #include "config.h"
44 #include "remmina_sodium.h"
45 #include "remmina.h"
46 #include "remmina_exec.h"
47 #include "remmina_file_manager.h"
48 #include "remmina_icon.h"
49 #include "remmina_main.h"
51 #include "remmina_plugin_manager.h"
52 #include "remmina_pref.h"
53 #include "remmina_public.h"
54 #include "remmina_sftp_plugin.h"
55 #include "remmina_ssh_plugin.h"
56 #include "remmina_widget_pool.h"
58 #include "rmnews.h"
59 #include "remmina_stats_sender.h"
60 
61 
62 #ifdef HAVE_ERRNO_H
63 #include <errno.h>
64 #endif
65 #include <pthread.h>
66 #ifdef HAVE_LIBGCRYPT
67 #include <gcrypt.h>
68 # if GCRYPT_VERSION_NUMBER < 0x010600
70 #endif /* !GCRYPT_VERSION_NUMBER */
71 #endif /* HAVE_LIBGCRYPT */
72 
73 #ifdef HAVE_LIBGCRYPT
74 # if GCRYPT_VERSION_NUMBER < 0x010600
76 #endif /* !GCRYPT_VERSION_NUMBER */
77 #endif /* HAVE_LIBGCRYPT */
78 
79 gboolean kioskmode;
80 
81 static GOptionEntry remmina_options[] =
82 {
83  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
84  { "about", 'a', 0, G_OPTION_ARG_NONE, NULL, N_("Show \'About\'"), NULL },
85  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
86  { "connect", 'c', 0, G_OPTION_ARG_FILENAME, NULL, N_("Connect to desktop described in file (.remmina or type supported by plugin)"), "FILE" },
87  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
88  { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, NULL, N_("Connect to desktop described in file (.remmina or type supported by plugin)"), "FILE" },
89  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
90  { "edit", 'e', 0, G_OPTION_ARG_FILENAME, NULL, N_("Edit desktop connection described in file (.remmina or type supported by plugin)"), "FILE" },
91  { "help", '?', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, NULL, NULL, NULL },
92  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
93  { "kiosk", 'k', 0, G_OPTION_ARG_NONE, NULL, N_("Start in kiosk mode"), NULL },
94  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
95  { "new", 'n', 0, G_OPTION_ARG_NONE, NULL, N_("Create new connection profile"), NULL },
96  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
97  { "pref", 'p', 0, G_OPTION_ARG_STRING, NULL, N_("Show preferences"), "PAGENR" },
98  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
99  { "plugin", 'x', 0, G_OPTION_ARG_STRING, NULL, N_("Run a plugin"), "PLUGIN" },
100  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
101  { "quit", 'q', 0, G_OPTION_ARG_NONE, NULL, N_("Quit"), NULL },
102  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
103  { "server", 's', 0, G_OPTION_ARG_STRING, NULL, N_("Use default server name (for --new)"), "SERVER" },
104  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
105  { "protocol", 't', 0, G_OPTION_ARG_STRING, NULL, N_("Use default protocol (for --new)"), "PROTOCOL" },
106  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
107  { "icon", 'i', 0, G_OPTION_ARG_NONE, NULL, N_("Start in tray"), NULL },
108  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
109  { "version", 'v', 0, G_OPTION_ARG_NONE, NULL, N_("Show the application version"), NULL },
110  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
111  { "full-version", 'V', 0, G_OPTION_ARG_NONE, NULL, N_("Show version of the application and its plugins"), NULL },
112  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
113  { "update-profile", 0, 0, G_OPTION_ARG_FILENAME, NULL, N_("Modify connection profile (requires --set-option)"), NULL },
114  // TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
115  { "set-option", 0, 0, G_OPTION_ARG_STRING_ARRAY, NULL, N_("Set one or more profile settings, to be used with --update-profile"), NULL },
116  { NULL }
117 };
118 
119 #ifdef WITH_LIBGCRYPT
120 static int
121 _gpg_error_to_errno(gcry_error_t e)
122 {
123  /* be lazy right now */
124  if (e == GPG_ERR_NO_ERROR)
125  return 0;
126  else
127  return EINVAL;
128 }
129 #endif /* !WITH_LIBGCRYPT */
130 
131 static gint remmina_on_command_line(GApplication *app, GApplicationCommandLine *cmdline)
132 {
133  TRACE_CALL(__func__);
134 
135  gint status = 0;
136  gboolean executed = FALSE;
137  GVariantDict *opts;
138  gchar *str;
139  const gchar **remaining_args;
140  gchar *protocol;
141  gchar *server;
142 
143 #if SODIUM_VERSION_INT >= 90200
145 #endif
147 
148  opts = g_application_command_line_get_options_dict(cmdline);
149 
150  if (g_variant_dict_lookup_value(opts, "quit", NULL)) {
152  executed = TRUE;
153  status = 1;
154  }
155 
156  if (g_variant_dict_lookup_value(opts, "about", NULL)) {
158  executed = TRUE;
159  }
160 
165  if (g_variant_dict_lookup(opts, "connect", "^ay", &str)) {
167  g_free(str);
168  executed = TRUE;
169  }
170 
171  if (g_variant_dict_lookup(opts, G_OPTION_REMAINING, "^a&ay", &remaining_args)) {
172  remmina_exec_command(REMMINA_COMMAND_CONNECT, remaining_args[0]);
173  g_free(remaining_args);
174  executed = TRUE;
175  }
176 
177  if (g_variant_dict_lookup(opts, "edit", "^ay", &str)) {
179  g_free(str);
180  executed = TRUE;
181  }
182 
183  if (g_variant_dict_lookup_value(opts, "kiosk", NULL)) {
184  kioskmode = TRUE;
186  executed = TRUE;
187  }
188 
189  if (g_variant_dict_lookup_value(opts, "new", NULL)) {
190  if (!g_variant_dict_lookup(opts, "protocol", "&s", &protocol))
191  protocol = NULL;
192 
193  if (g_variant_dict_lookup(opts, "server", "&s", &server))
194  str = g_strdup_printf("%s,%s", protocol, server);
195  else
196  str = g_strdup(protocol);
197 
199  g_free(str);
200  executed = TRUE;
201  }
202 
203  if (g_variant_dict_lookup(opts, "pref", "&s", &str)) {
205  executed = TRUE;
206  }
207 
208  if (g_variant_dict_lookup(opts, "plugin", "&s", &str)) {
210  executed = TRUE;
211  }
212 
213  if (g_variant_dict_lookup_value(opts, "icon", NULL)) {
215  executed = TRUE;
216  }
217 
218  if (!executed)
220 
221  return status;
222 }
223 
224 static void remmina_on_startup(GApplication *app)
225 {
226  TRACE_CALL(__func__);
227 
228  RemminaSecretPlugin *secret_plugin;
229 
234 
235  g_set_application_name("Remmina");
236  gtk_window_set_default_icon_name(REMMINA_APP_ID);
237 
238  /* Setting the X11 program class (WM_CLASS) is necessary to group
239  * windows with .desktop file which has the same StartupWMClass */
240  gdk_set_program_class(REMMINA_APP_ID);
241 
242  gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(),
243  REMMINA_RUNTIME_DATADIR G_DIR_SEPARATOR_S "icons");
244  g_application_hold(app);
245 
247  rmnews_schedule();
248 
249  /* Check for secret plugin and service initialization and show console warnings if
250  * something is missing */
252  if (!secret_plugin) {
253  g_print("Warning: Remmina is running without a secret plugin. Passwords will be saved in a less secure way.\n");
254  } else {
255  if (!secret_plugin->is_service_available())
256  g_print("Warning: Remmina is running with a secrecy plugin, but it cannot connect to a secrecy service.\n");
257  }
258 
260 }
261 
262 static gint remmina_on_local_cmdline(GApplication *app, GVariantDict *opts, gpointer user_data)
263 {
264  TRACE_CALL(__func__);
265 
266  int status = -1;
267  gchar *str;
268  gchar **settings;
269 
270  /* Here you handle any command line options that you want to be executed
271  * in the local instance (the non-unique instance) */
272 
273  if (g_variant_dict_lookup_value(opts, "version", NULL)) {
275  status = 0;
276  }
277 
278  if (g_variant_dict_lookup_value(opts, "full-version", NULL)) {
280  status = 0;
281  }
282 
283  if (g_variant_dict_lookup(opts, "update-profile", "^&ay", &str)) { /* ^&ay no need to free */
284  if (g_variant_dict_lookup(opts, "set-option", "^a&s", &settings)) {
285  if (settings != NULL) {
286  status = remmina_exec_set_setting(str, settings);
287  g_free(settings);
288  } else {
289  status = 1;
290  }
291  } else {
292  status = 1;
293  g_print("Error: --update-profile requires --set-option\n");
294  }
295  }
296 
297  /* Returning a non negative value here makes the application exit */
298  return status;
299 }
300 
301 int main(int argc, char *argv[])
302 {
303  TRACE_CALL(__func__);
304  GtkApplication *app;
305  const gchar *app_id;
306  int status;
307 
308  g_unsetenv ("GDK_CORE_DEVICE_EVENTS");
309 
310  /* Enable wayland backend only after GTK 3.22.27 or the clipboard
311  * will not work. See GTK bug 790031 */
312  if (remmina_gtk_check_version(3, 22, 27))
313  gdk_set_allowed_backends("wayland,x11,broadway,quartz,mir");
314  else
315  gdk_set_allowed_backends("x11,broadway,quartz,mir");
316 
318 
319  bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
320  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
321  textdomain(GETTEXT_PACKAGE);
322 
323 #ifdef HAVE_LIBGCRYPT
324 # if GCRYPT_VERSION_NUMBER < 0x010600
325  gcry_error_t e;
327  if ((e = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread)) != GPG_ERR_NO_ERROR)
328  return -1;
330  }
331 #endif /* !GCRYPT_VERSION_NUMBER */
332  gcry_check_version(NULL);
333  gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
334  gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
335 #endif /* !HAVE_LIBGCRYPT */
336 
337  /* Initialize some Remmina parts needed also on a local instance for correct handle-local-options */
341 
342 
343  app_id = g_application_id_is_valid(REMMINA_APP_ID) ? REMMINA_APP_ID : NULL;
344  app = gtk_application_new(app_id, G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_CAN_OVERRIDE_APP_ID);
345 #if !GTK_CHECK_VERSION(4, 0, 0) /* This is not needed anymore starting from GTK 4 */
346  g_set_prgname(app_id);
347 #endif
348  g_signal_connect(app, "startup", G_CALLBACK(remmina_on_startup), NULL);
349  g_signal_connect(app, "command-line", G_CALLBACK(remmina_on_command_line), NULL);
350  g_signal_connect(app, "handle-local-options", G_CALLBACK(remmina_on_local_cmdline), NULL);
351 
352  g_application_add_main_option_entries(G_APPLICATION(app), remmina_options);
353 
354  g_application_set_inactivity_timeout(G_APPLICATION(app), 10000);
355  status = g_application_run(G_APPLICATION(app), argc, argv);
356  g_object_unref(app);
357 
358  return status;
359 }
int remmina_exec_set_setting(gchar *profilefilename, gchar **settings)
Definition: remmina_exec.c:160
static GOptionEntry remmina_options[]
Definition: remmina.c:81
static void remmina_on_startup(GApplication *app)
Definition: remmina.c:224
void remmina_plugin_manager_init(void)
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
void remmina_masterthread_exec_save_main_thread_id()
void remmina_sftp_plugin_register(void)
int main(int argc, char *argv[])
Definition: remmina.c:301
gboolean(* is_service_available)(void)
Definition: plugin.h:138
static int _gpg_error_to_errno(gcry_error_t e)
Definition: remmina.c:121
static gint remmina_on_command_line(GApplication *app, GApplicationCommandLine *cmdline)
Definition: remmina.c:131
void remmina_file_manager_init(void)
void remmina_ssh_plugin_register(void)
void remmina_icon_init(void)
Definition: remmina_icon.c:429
static gint remmina_on_local_cmdline(GApplication *app, GVariantDict *opts, gpointer user_data)
Definition: remmina.c:262
static int gcrypt_thread_initialized
Definition: remmina.c:75
void remmina_exec_command(RemminaCommandType command, const gchar *data)
Definition: remmina_exec.c:214
void remmina_widget_pool_init(void)
gboolean remmina_gtk_check_version(guint major, guint minor, guint micro)
void rmnews_schedule()
Definition: rmnews.c:528
void remmina_stats_sender_schedule()
void remmina_pref_init(void)
Definition: remmina_pref.c:221
GCRY_THREAD_OPTION_PTHREAD_IMPL
Definition: remmina.c:69
gboolean kioskmode
Definition: remmina.c:79
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:907
void remmina_sodium_init(void)