summaryrefslogtreecommitdiffstats
path: root/src/remmina_exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/remmina_exec.c')
-rw-r--r--src/remmina_exec.c544
1 files changed, 544 insertions, 0 deletions
diff --git a/src/remmina_exec.c b/src/remmina_exec.c
new file mode 100644
index 0000000..cf76b58
--- /dev/null
+++ b/src/remmina_exec.c
@@ -0,0 +1,544 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2010 Vic Lee
+ * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+ * Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ *
+ */
+
+#include "config.h"
+#include "buildflags.h"
+#include <glib/gi18n.h>
+#include <stdlib.h>
+#include "remmina.h"
+#include "remmina_main.h"
+#include "remmina_log.h"
+#include "remmina_pref.h"
+#include "remmina_widget_pool.h"
+#include "remmina_unlock.h"
+#include "remmina_pref_dialog.h"
+#include "remmina_file.h"
+#include "remmina_file_manager.h"
+#include "remmina_file_editor.h"
+#include "rcw.h"
+#include "remmina_about.h"
+#include "remmina_plugin_manager.h"
+#include "remmina_exec.h"
+#include "remmina/remmina_trace_calls.h"
+#include "remmina_file_manager.h"
+#include "remmina_crypt.h"
+
+#include "remmina_icon.h"
+
+#ifdef SNAP_BUILD
+# define ISSNAP "- SNAP Build -"
+#else
+# define ISSNAP "-"
+#endif
+
+static gboolean cb_closewidget(GtkWidget *widget, gpointer data)
+{
+ TRACE_CALL(__func__);
+ /* The correct way to close a rcw is to send
+ * it a "delete-event" signal. Simply destroying it will not close
+ * all network connections */
+ if (REMMINA_IS_CONNECTION_WINDOW(widget))
+ return rcw_delete(RCW(widget));
+ return TRUE;
+}
+
+const gchar* remmina_exec_get_build_config(void)
+{
+ static const gchar build_config[] =
+ "Build configuration: " BUILD_CONFIG "\n"
+ "Build type: " BUILD_TYPE "\n"
+ "CFLAGS: " CFLAGS "\n"
+ "Compiler: " COMPILER_ID ", " COMPILER_VERSION "\n"
+ "Target architecture: " TARGET_ARCH "\n";
+ return build_config;
+}
+
+void remmina_exec_exitremmina()
+{
+ TRACE_CALL(__func__);
+
+ /* Save main window state/position */
+ remmina_main_save_before_destroy();
+
+ /* Delete all widgets, main window not included */
+ remmina_widget_pool_foreach(cb_closewidget, NULL);
+
+#ifdef HAVE_LIBAPPINDICATOR
+ /* Remove systray menu */
+ remmina_icon_destroy();
+#endif
+
+ /* close/destroy main window struct and window */
+ remmina_main_destroy();
+
+ /* Exit from Remmina */
+ g_application_quit(g_application_get_default());
+}
+
+static gboolean disable_rcw_delete_confirm_cb(GtkWidget *widget, gpointer data)
+{
+ TRACE_CALL(__func__);
+ RemminaConnectionWindow *rcw;
+
+ if (REMMINA_IS_CONNECTION_WINDOW(widget)) {
+ rcw = (RemminaConnectionWindow*)widget;
+ rcw_set_delete_confirm_mode(rcw, RCW_ONDELETE_NOCONFIRM);
+ }
+ return TRUE;
+}
+
+void remmina_exec_exitremmina_one_confirm()
+{
+ TRACE_CALL(__func__);
+ GtkWidget* dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_YES_NO,
+ _("Are you sure you want to fully quit Remmina?\n This will close any active connections."));
+ int response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ if (response != GTK_RESPONSE_YES)
+ return;
+ remmina_widget_pool_foreach(disable_rcw_delete_confirm_cb, NULL);
+ remmina_exec_exitremmina();
+}
+
+void remmina_application_condexit(RemminaCondExitType why)
+{
+ TRACE_CALL(__func__);
+
+ /* Exit remmina only if there are no interesting windows left:
+ * no main window, no systray menu, no connection window.
+ * This function is usually called after a disconnection */
+
+ switch (why) {
+ case REMMINA_CONDEXIT_ONDISCONNECT:
+ // A connection has disconnected, should we exit remmina ?
+ if (remmina_widget_pool_count() < 1 && !remmina_main_get_window() && !remmina_icon_is_available())
+ remmina_exec_exitremmina();
+ break;
+ case REMMINA_CONDEXIT_ONMAINWINDELETE:
+ /* If we are in Kiosk mode, we just exit */
+ if (kioskmode && kioskmode == TRUE)
+ remmina_exec_exitremmina();
+ // Main window has been deleted
+ if (remmina_widget_pool_count() < 1 && !remmina_icon_is_available())
+ remmina_exec_exitremmina();
+ break;
+ case REMMINA_CONDEXIT_ONQUIT:
+ // Quit command has been sent from main window or appindicator/systray menu
+ // quit means QUIT.
+ remmina_widget_pool_foreach(disable_rcw_delete_confirm_cb, NULL);
+ remmina_exec_exitremmina();
+ break;
+ }
+}
+
+
+static void newline_remove(char *s)
+{
+ char c;
+ while((c = *s) != 0 && c != '\r' && c != '\n')
+ s++;
+ *s = 0;
+}
+
+/* used for commandline parameter --update-profile X --set-option Y --set-option Z
+ * return a status code for exit()
+ */
+int remmina_exec_set_setting(gchar *profilefilename, gchar **settings)
+{
+ RemminaFile *remminafile;
+ int i;
+ gchar **tk, *value = NULL;
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t read;
+ gboolean abort = FALSE;
+
+ remminafile = remmina_file_manager_load_file(profilefilename);
+
+ if (!remminafile) {
+ g_print("Unable to open profile file %s\n", profilefilename);
+ return 2;
+ }
+
+ for(i = 0; settings[i] != NULL && !abort; i++) {
+ if (strlen(settings[i]) > 0) {
+ tk = g_strsplit(settings[i], "=", 2);
+ if (tk[1] == NULL) {
+ read = getline(&line, &len, stdin);
+ if (read > 0) {
+ newline_remove(line);
+ value = line;
+ } else {
+ g_print("Error: an extra line of standard input is needed\n");
+ abort = TRUE;
+ }
+ } else
+ value = tk[1];
+ remmina_file_set_string(remminafile, tk[0], value);
+ g_strfreev(tk);
+ }
+ }
+
+ if (line) free(line);
+
+ if (!abort) remmina_file_save(remminafile);
+
+ return 0;
+
+}
+
+static void remmina_exec_autostart_cb(RemminaFile *remminafile, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+
+ if (remmina_file_get_int(remminafile, "enable-autostart", FALSE)) {
+ REMMINA_DEBUG ("Profile %s is set to autostart", remminafile->filename);
+ rcw_open_from_filename(remminafile->filename);
+ }
+
+}
+
+static void remmina_exec_connect(const gchar *data)
+{
+ TRACE_CALL(__func__);
+
+ gchar *protocol;
+ gchar **protocolserver;
+ gchar *server;
+ RemminaFile *remminafile;
+ gchar **userat;
+ gchar **userpass;
+ gchar *user;
+ gchar *password;
+ gchar **domainuser;
+ gchar **serverquery;
+ gchar **querystring;
+ gchar **querystringpart;
+ gchar **querystringpartkv;
+ gchar *value;
+ gchar *temp;
+ GError *error = NULL;
+
+ protocol = NULL;
+ if (strncmp("rdp://", data, 6) == 0 || strncmp("RDP://", data, 6) == 0)
+ protocol = "RDP";
+ else if (strncmp("vnc://", data, 6) == 0 || strncmp("VNC://", data, 6) == 0)
+ protocol = "VNC";
+ else if (strncmp("ssh://", data, 6) == 0 || strncmp("SSH://", data, 6) == 0)
+ protocol = "SSH";
+ else if (strncmp("spice://", data, 8) == 0 || strncmp("SPICE://", data, 8) == 0)
+ protocol = "SPICE";
+
+ if (strncmp("file://", data, 6) == 0) {
+ gchar *filename = g_filename_from_uri (data, NULL, &error);
+ if (filename != NULL) {
+ rcw_open_from_filename(filename);
+ } else
+ REMMINA_DEBUG ("Opening URI %s failed with error %s", data, error->message);
+ g_error_free(error);
+ return;
+ }
+
+ if (protocol == NULL) {
+ rcw_open_from_filename(data);
+ return;
+ }
+
+ protocolserver = g_strsplit(data, "://", 2);
+ server = g_strdup(protocolserver[1]);
+
+ // Support loading .remmina files using handler
+ if ((temp = strrchr(server, '.')) != NULL && g_strcmp0(temp + 1, "remmina") == 0) {
+ g_strfreev(protocolserver);
+ temp = g_uri_unescape_string(server, NULL);
+ g_free(server);
+ server = temp;
+ rcw_open_from_filename(server);
+ return;
+ }
+
+ remminafile = remmina_file_new();
+
+ // Check for username@server
+ if ((strcmp(protocol, "RDP") == 0 || strcmp(protocol, "VNC") == 0 || strcmp(protocol, "SSH") == 0) && strstr(server, "@") != NULL) {
+ userat = g_strsplit(server, "@", 2);
+
+ // Check for username:password
+ if (strstr(userat[0], ":") != NULL) {
+ userpass = g_strsplit(userat[0], ":", 2);
+ user = g_uri_unescape_string(userpass[0], NULL);
+ password = g_uri_unescape_string(userpass[1], NULL);
+
+ // Try to decrypt the password field if it contains =
+ temp = password != NULL && strrchr(password, '=') != NULL ? remmina_crypt_decrypt(password) : NULL;
+ if (temp != NULL) {
+ g_free(password);
+ password = temp;
+ }
+ remmina_file_set_string(remminafile, "password", password);
+ g_free(password);
+ g_strfreev(userpass);
+ } else {
+ user = g_uri_unescape_string(userat[0], NULL);
+ }
+
+ // Check for domain\user for RDP connections
+ if (strcmp(protocol, "RDP") == 0 && strstr(user, "\\") != NULL) {
+ domainuser = g_strsplit(user, "\\", 2);
+ remmina_file_set_string(remminafile, "domain", domainuser[0]);
+ g_free(user);
+ user = g_strdup(domainuser[1]);
+ }
+
+ remmina_file_set_string(remminafile, "username", user);
+ g_free(user);
+ g_free(server);
+ server = g_strdup(userat[1]);
+ g_strfreev(userat);
+ }
+
+ if (strcmp(protocol, "VNC") == 0 && strstr(server, "?") != NULL) {
+ // https://tools.ietf.org/html/rfc7869
+ // VncUsername, VncPassword and ColorLevel supported for vnc-params
+
+ // Check for query string parameters
+ serverquery = g_strsplit(server, "?", 2);
+ querystring = g_strsplit(serverquery[1], "&", -1);
+ for (querystringpart = querystring; *querystringpart; querystringpart++) {
+ if (strstr(*querystringpart, "=") == NULL)
+ continue;
+ querystringpartkv = g_strsplit(*querystringpart, "=", 2);
+ value = g_uri_unescape_string(querystringpartkv[1], NULL);
+ if (strcmp(querystringpartkv[0], "VncPassword") == 0) {
+ // Try to decrypt password field if it contains =
+ temp = value != NULL && strrchr(value, '=') != NULL ? remmina_crypt_decrypt(value) : NULL;
+ if (temp != NULL) {
+ g_free(value);
+ value = temp;
+ }
+ remmina_file_set_string(remminafile, "password", value);
+ } else if (strcmp(querystringpartkv[0], "VncUsername") == 0) {
+ remmina_file_set_string(remminafile, "username", value);
+ } else if (strcmp(querystringpartkv[0], "ColorLevel") == 0) {
+ remmina_file_set_string(remminafile, "colordepth", value);
+ }
+ g_free(value);
+ g_strfreev(querystringpartkv);
+ }
+ g_strfreev(querystring);
+ g_free(server);
+ server = g_strdup(serverquery[0]);
+ g_strfreev(serverquery);
+ }
+
+ // Unescape server
+ temp = g_uri_unescape_string(server, NULL);
+ g_free(server);
+ server = temp;
+
+ remmina_file_set_string(remminafile, "server", server);
+ remmina_file_set_string(remminafile, "name", server);
+ remmina_file_set_string(remminafile, "sound", "off");
+ remmina_file_set_string(remminafile, "protocol", protocol);
+ g_free(server);
+ g_strfreev(protocolserver);
+ rcw_open_from_file(remminafile);
+}
+
+void remmina_exec_command(RemminaCommandType command, const gchar* data)
+{
+ TRACE_CALL(__func__);
+ gchar *s1;
+ gchar *s2;
+ gchar *temp;
+ GtkWidget *widget;
+ GtkWindow *mainwindow;
+ GtkWidget *prefdialog;
+ RemminaEntryPlugin *plugin;
+ int i;
+ int ch;
+ mainwindow = remmina_main_get_window();
+
+ switch (command) {
+ case REMMINA_COMMAND_AUTOSTART:
+ remmina_file_manager_iterate((GFunc)remmina_exec_autostart_cb, NULL);
+ break;
+
+ case REMMINA_COMMAND_MAIN:
+ if (mainwindow) {
+ gtk_window_present(mainwindow);
+ gtk_window_deiconify(GTK_WINDOW(mainwindow));
+ }else {
+ widget = remmina_main_new();
+ gtk_widget_show(widget);
+ }
+ break;
+
+ case REMMINA_COMMAND_PREF:
+ if (remmina_pref_get_boolean("use_primary_password")
+ && remmina_unlock_new(mainwindow) == 0)
+ break;
+ prefdialog = remmina_pref_dialog_get_dialog();
+ if (prefdialog) {
+ gtk_window_present(GTK_WINDOW(prefdialog));
+ gtk_window_deiconify(GTK_WINDOW(prefdialog));
+ }else {
+ /* Create a new preference dialog */
+ widget = remmina_pref_dialog_new(atoi(data), NULL);
+ gtk_widget_show(widget);
+ }
+ break;
+
+ case REMMINA_COMMAND_NEW:
+ if (remmina_pref_get_boolean("lock_edit")
+ && remmina_pref_get_boolean("use_primary_password"))
+ if (remmina_unlock_new(mainwindow) == 0)
+ break;
+ s1 = (data ? strchr(data, ',') : NULL);
+ if (s1) {
+ s1 = g_strdup(data);
+ s2 = strchr(s1, ',');
+ *s2++ = '\0';
+ widget = remmina_file_editor_new_full(s2, s1);
+ g_free(s1);
+ }else {
+ widget = remmina_file_editor_new_full(NULL, data);
+ }
+ gtk_widget_show(widget);
+ break;
+
+ case REMMINA_COMMAND_CONNECT:
+ REMMINA_DEBUG ("Initiating connection");
+ /** @todo This should be a G_OPTION_ARG_FILENAME_ARRAY (^aay) so that
+ * we can implement multi profile connection:
+ * https://gitlab.com/Remmina/Remmina/issues/915
+ */
+ if (remmina_pref_get_boolean("lock_connect")
+ && remmina_pref_get_boolean("use_primary_password"))
+ if (remmina_unlock_new(mainwindow) == 0)
+ break;
+ remmina_exec_connect(data);
+ break;
+
+ case REMMINA_COMMAND_EDIT:
+ if (remmina_pref_get_boolean("lock_edit")
+ && remmina_pref_get_boolean("use_primary_password"))
+ if (remmina_unlock_new(mainwindow) == 0)
+ break;
+ widget = remmina_file_editor_new_from_filename(data);
+ if (widget)
+ gtk_widget_show(widget);
+ break;
+
+ case REMMINA_COMMAND_ABOUT:
+ remmina_about_open(NULL);
+ break;
+
+ case REMMINA_COMMAND_VERSION:
+ mainwindow = remmina_main_get_window();
+ if (mainwindow) {
+ remmina_about_open(NULL);
+ }else {
+ g_print("%s %s %s (git %s)\n", g_get_application_name(), ISSNAP, VERSION, REMMINA_GIT_REVISION);
+ /* As we do not use the "handle-local-options" signal, we have to exit Remmina */
+ remmina_exec_command(REMMINA_COMMAND_EXIT, NULL);
+ }
+
+ break;
+
+ case REMMINA_COMMAND_FULL_VERSION:
+ mainwindow = remmina_main_get_window();
+ if (mainwindow) {
+ /* Show th widget with the list of plugins and versions */
+ remmina_plugin_manager_show(mainwindow);
+ }else {
+ g_print("\n%s %s %s (git %s)\n\n", g_get_application_name(), ISSNAP, VERSION, REMMINA_GIT_REVISION);
+
+ remmina_plugin_manager_show_stdout();
+ g_print("\n%s\n", remmina_exec_get_build_config());
+ remmina_exec_command(REMMINA_COMMAND_EXIT, NULL);
+ }
+
+ break;
+
+
+ case REMMINA_COMMAND_PLUGIN:
+ plugin = (RemminaEntryPlugin*)remmina_plugin_manager_get_plugin(REMMINA_PLUGIN_TYPE_ENTRY, data);
+ if (plugin) {
+ plugin->entry_func(plugin);
+ }else {
+ widget = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ _("Plugin %s is not registered."), data);
+ g_signal_connect(G_OBJECT(widget), "response", G_CALLBACK(gtk_widget_destroy), NULL);
+ gtk_widget_show(widget);
+ remmina_widget_pool_register(widget);
+ }
+ break;
+
+ case REMMINA_COMMAND_ENCRYPT_PASSWORD:
+ i = 0;
+ g_print("Enter the password you want to encrypt: ");
+ temp = (char *)g_malloc(255 * sizeof(char));
+ while ((ch = getchar()) != EOF && ch != '\n') {
+ if (i < 254) {
+ temp[i] = ch;
+ i++;
+ }
+ }
+ temp[i] = '\0';
+ s1 = remmina_crypt_encrypt(temp);
+ s2 = g_uri_escape_string(s1, NULL, TRUE);
+ g_print("\nEncrypted password: %s\n\n", s1);
+ g_print("Usage:\n");
+ g_print("rdp://username:%s@server\n", s1);
+ g_print("vnc://username:%s@server\n", s1);
+ g_print("vnc://server?VncUsername=user\\&VncPassword=%s\n", s2);
+ g_free(s1);
+ g_free(s2);
+ g_free(temp);
+ remmina_exec_exitremmina();
+ break;
+
+ case REMMINA_COMMAND_EXIT:
+ remmina_widget_pool_foreach(disable_rcw_delete_confirm_cb, NULL);
+ remmina_exec_exitremmina();
+ break;
+
+ default:
+ break;
+ }
+}