From 6a9cfd1ad023b90c48deaea3e75d8ab27c66da5d Mon Sep 17 00:00:00 2001 From: Antenore Gatta Date: Mon, 17 Jul 2023 15:40:05 +0000 Subject: Automatic doc build by remmina-ci --- public/rdp__plugin_8c.html | 108 ++++++++++++++-------------- public/rdp__plugin_8c_source.html | 74 +++++++++---------- public/remmina__exec_8c_source.html | 4 +- public/remmina__file__editor_8c.html | 88 +++++++++++------------ public/remmina__file__editor_8c_source.html | 90 +++++++++++------------ public/remmina__file__editor_8h.html | 14 ++-- public/remmina__file__editor_8h_source.html | 14 ++-- public/remmina__main_8c_source.html | 6 +- public/vnc__plugin_8c.html | 66 ++++++++--------- public/vnc__plugin_8c_source.html | 32 ++++----- 10 files changed, 248 insertions(+), 248 deletions(-) diff --git a/public/rdp__plugin_8c.html b/public/rdp__plugin_8c.html index 64afc5e8c..4efc41b7d 100644 --- a/public/rdp__plugin_8c.html +++ b/public/rdp__plugin_8c.html @@ -264,7 +264,7 @@ Variables
-

Definition at line 3004 of file rdp_plugin.c.

+

Definition at line 3005 of file rdp_plugin.c.

@@ -292,7 +292,7 @@ Variables
-

Definition at line 2366 of file rdp_plugin.c.

+

Definition at line 2367 of file rdp_plugin.c.

@@ -340,7 +340,7 @@ Variables
-

Definition at line 3024 of file rdp_plugin.c.

+

Definition at line 3025 of file rdp_plugin.c.

@@ -428,7 +428,7 @@ Variables
-

Definition at line 2555 of file rdp_plugin.c.

+

Definition at line 2556 of file rdp_plugin.c.

@@ -456,7 +456,7 @@ Variables
-

Definition at line 2507 of file rdp_plugin.c.

+

Definition at line 2508 of file rdp_plugin.c.

@@ -615,7 +615,7 @@ Variables
Todo:
we should lock FreeRDP subthread to update rfi->primary_buffer, rfi->gdi and w/h, from here to memcpy, but… how ?
-

Definition at line 2621 of file rdp_plugin.c.

+

Definition at line 2622 of file rdp_plugin.c.

@@ -693,7 +693,7 @@ Variables
-

Definition at line 2423 of file rdp_plugin.c.

+

Definition at line 2424 of file rdp_plugin.c.

@@ -737,7 +737,7 @@ Variables
-

Definition at line 2611 of file rdp_plugin.c.

+

Definition at line 2612 of file rdp_plugin.c.

@@ -873,7 +873,7 @@ Variables
-

Definition at line 2396 of file rdp_plugin.c.

+

Definition at line 2397 of file rdp_plugin.c.

@@ -901,7 +901,7 @@ Variables
-

Definition at line 2459 of file rdp_plugin.c.

+

Definition at line 2460 of file rdp_plugin.c.

@@ -1024,7 +1024,7 @@ Variables
-

Definition at line 2549 of file rdp_plugin.c.

+

Definition at line 2550 of file rdp_plugin.c.

@@ -1693,7 +1693,7 @@ Variables
-

Definition at line 2336 of file rdp_plugin.c.

+

Definition at line 2337 of file rdp_plugin.c.

@@ -1719,7 +1719,7 @@ Variables
Initial value:
=
N_("Options for redirection of audio output:\n"
" • [sys:<sys>,][dev:<dev>,][format:<format>,][rate:<rate>,]\n"
" [channel:<channel>] Audio output\n"
" • sys:pulse\n"
" • format:1\n"
" • sys:oss,dev:1,format:1\n"
" • sys:alsa")
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2776 of file rdp_plugin.c.

+

Definition at line 2777 of file rdp_plugin.c.

@@ -1744,7 +1744,7 @@ Variables
Initial value:
=
N_("2600 (Windows XP), 7601 (Windows Vista/7), 9600 (Windows 8 and newer)")
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2756 of file rdp_plugin.c.

+

Definition at line 2757 of file rdp_plugin.c.

@@ -1769,7 +1769,7 @@ Variables
Initial value:
=
N_("Used i.a. by terminal services in a smart card channel to distinguish client capabilities:\n"
" • < 4034: Windows XP base smart card functions\n"
" • 4034-7064: Windows Vista/7: SCardReadCache(),\n"
" SCardWriteCache(), SCardGetTransmitCount()\n"
" • >= 7065: Windows 8 and newer: SCardGetReaderIcon(),\n"
" SCardGetDeviceTypeId()")
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2759 of file rdp_plugin.c.

+

Definition at line 2760 of file rdp_plugin.c.

@@ -1794,7 +1794,7 @@ Variables
Initial value:
=
{
"99", N_("Automatic (32 bpp) (Server chooses its best format)"),
"66", N_("GFX AVC444 (32 bpp)"),
"65", N_("GFX AVC420 (32 bpp)"),
"64", N_("GFX RFX (32 bpp)"),
"63", N_("GFX RFX Progressive (32 bpp)"),
"0", N_("RemoteFX (32 bpp)"),
"32", N_("True colour (32 bpp)"),
"24", N_("True colour (24 bpp)"),
"16", N_("High colour (16 bpp)"),
"15", N_("High colour (15 bpp)"),
"8", N_("256 colours (8 bpp)"),
NULL
}
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2661 of file rdp_plugin.c.

+

Definition at line 2662 of file rdp_plugin.c.

@@ -1819,7 +1819,7 @@ Variables
Initial value:
=
N_("Redirect directory <path> as named share <name>.\n"
" • <name>,<fullpath>[;<name>,<fullpath>[;…]]\n"
" • MyHome,/home/remminer\n"
" • /home/remminer\n"
" • MyHome,/home/remminer;SomePath,/path/to/somepath\n"
"Hotplug support is enabled with:\n"
" • hotplug,*\n"
"\n")
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2814 of file rdp_plugin.c.

+

Definition at line 2815 of file rdp_plugin.c.

@@ -1867,7 +1867,7 @@ Variables
Initial value:
=
{
"http", "HTTP",
"rpc", "RPC",
"auto", "Auto",
NULL
}
-

Definition at line 2736 of file rdp_plugin.c.

+

Definition at line 2737 of file rdp_plugin.c.

@@ -1891,7 +1891,7 @@ Variables
Initial value:
=
{
"INFO", "INFO",
"FATAL", "FATAL",
"ERROR", "ERROR",
"WARN", "WARN",
"DEBUG", "DEBUG",
"TRACE", "TRACE",
"OFF", "OFF",
NULL
}
-

Definition at line 2679 of file rdp_plugin.c.

+

Definition at line 2680 of file rdp_plugin.c.

@@ -1916,7 +1916,7 @@ Variables
Initial value:
=
N_("Options for redirection of audio input:\n"
" • [sys:<sys>,][dev:<dev>,][format:<format>,][rate:<rate>,]\n"
" [channel:<channel>] Audio input (microphone)\n"
" • sys:pulse\n"
" • format:1\n"
" • sys:oss,dev:1,format:1\n"
" • sys:alsa")
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2767 of file rdp_plugin.c.

+

Definition at line 2768 of file rdp_plugin.c.

@@ -1941,7 +1941,7 @@ Variables
Initial value:
=
N_("Comma-separated list of monitor IDs and desktop orientations:\n"
" • [<id>:<orientation-in-degrees>,]\n"
" • 0,1,2,3\n"
" • 0:270,1:90\n"
"Orientations are specified in degrees, valid values are:\n"
" • 0 (landscape)\n"
" • 90 (portrait)\n"
" • 180 (landscape flipped)\n"
" • 270 (portrait flipped)\n"
"\n")
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2802 of file rdp_plugin.c.

+

Definition at line 2803 of file rdp_plugin.c.

@@ -1966,7 +1966,7 @@ Variables
Initial value:
=
{
"none", N_("None"),
"autodetect", N_("Auto-detect"),
"modem", N_("Modem"),
"broadband-low", N_("Low performance broadband"),
"satellite", N_("Satellite"),
"broadband-high", N_("High performance broadband"),
"wan", N_("WAN"),
"lan", N_("LAN"),
NULL
}
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2703 of file rdp_plugin.c.

+

Definition at line 2704 of file rdp_plugin.c.

@@ -1991,7 +1991,7 @@ Variables
Initial value:
=
N_("Performance optimisations based on the network connection type:\n"
"Using auto-detection is advised.\n"
"If “Auto-detect” fails, choose the most appropriate option in the list.\n")
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2797 of file rdp_plugin.c.

+

Definition at line 2798 of file rdp_plugin.c.

@@ -2016,7 +2016,7 @@ Variables
Initial value:
=
{
"0", N_("Poor (fastest)"),
"1", N_("Medium"),
"2", N_("Good"),
"9", N_("Best (slowest)"),
NULL
}
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2693 of file rdp_plugin.c.

+

Definition at line 2694 of file rdp_plugin.c.

@@ -2040,7 +2040,7 @@ Variables
-

Definition at line 2950 of file rdp_plugin.c.

+

Definition at line 2951 of file rdp_plugin.c.

@@ -2079,24 +2079,24 @@ Variables
-Initial value:
=
{
"RDP",
N_("RDP - Remote Desktop Protocol"),
GETTEXT_PACKAGE,
"org.remmina.Remmina-rdp-symbolic",
"org.remmina.Remmina-rdp-ssh-symbolic",
}
static gboolean remmina_rdp_open_connection(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:2459
+Initial value:
=
{
"RDP",
N_("RDP - Remote Desktop Protocol"),
GETTEXT_PACKAGE,
"org.remmina.Remmina-rdp-symbolic",
"org.remmina.Remmina-rdp-ssh-symbolic",
}
static gboolean remmina_rdp_open_connection(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:2460
gboolean remmina_rdp_event_on_unmap(RemminaProtocolWidget *gp)
Definition: rdp_event.c:74
-
static gboolean remmina_rdp_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: rdp_plugin.c:2549
+
static gboolean remmina_rdp_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: rdp_plugin.c:2550
-
static const RemminaProtocolSetting remmina_rdp_advanced_settings[]
Definition: rdp_plugin.c:2871
-
static void remmina_rdp_init(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:2423
+
static const RemminaProtocolSetting remmina_rdp_advanced_settings[]
Definition: rdp_plugin.c:2872
+
static void remmina_rdp_init(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:2424
-
static const RemminaProtocolFeature remmina_rdp_features[]
Definition: rdp_plugin.c:2936
-
static void remmina_rdp_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
Definition: rdp_plugin.c:2611
-
static gboolean remmina_rdp_close_connection(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:2507
-
static char remmina_plugin_rdp_version[256]
Definition: rdp_plugin.c:2950
-
static const RemminaProtocolSetting remmina_rdp_basic_settings[]
Definition: rdp_plugin.c:2841
-
static gboolean remmina_rdp_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
Definition: rdp_plugin.c:2621
+
static const RemminaProtocolFeature remmina_rdp_features[]
Definition: rdp_plugin.c:2937
+
static void remmina_rdp_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
Definition: rdp_plugin.c:2612
+
static gboolean remmina_rdp_close_connection(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:2508
+
static char remmina_plugin_rdp_version[256]
Definition: rdp_plugin.c:2951
+
static const RemminaProtocolSetting remmina_rdp_basic_settings[]
Definition: rdp_plugin.c:2842
+
static gboolean remmina_rdp_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
Definition: rdp_plugin.c:2622
gboolean remmina_rdp_event_on_map(RemminaProtocolWidget *gp)
Definition: rdp_event.c:52
-
static void remmina_rdp_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: rdp_plugin.c:2555
+
static void remmina_rdp_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: rdp_plugin.c:2556
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2953 of file rdp_plugin.c.

+

Definition at line 2954 of file rdp_plugin.c.

@@ -2120,7 +2120,7 @@ Variables
-

Definition at line 2871 of file rdp_plugin.c.

+

Definition at line 2872 of file rdp_plugin.c.

@@ -2144,21 +2144,21 @@ Variables
Initial value:
=
{
{ REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, NULL, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("Password"), FALSE, NULL, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "domain", N_("Domain"), FALSE, NULL, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "drive", N_("Share folder"), FALSE, NULL, drive_tooltip, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "restricted-admin", N_("Restricted admin mode"), FALSE, NULL, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "pth", N_("Password hash"), FALSE, NULL, N_("Restricted admin mode password hash"), NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "left-handed", N_("Left-handed mouse support"), TRUE, NULL, N_("Swap left and right mouse buttons for left-handed mouse support"), NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disable-smooth-scrolling", N_("Disable smooth scrolling"), TRUE, NULL, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "multimon", N_("Enable multi monitor"), TRUE, NULL, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "span", N_("Span screen over multiple monitors"), TRUE, NULL, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "monitorids", N_("List monitor IDs"), FALSE, NULL, monitorids_tooltip, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_RESOLUTION, "resolution", NULL, FALSE, NULL, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "colordepth", N_("Colour depth"), FALSE, colordepth_list, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "network", N_("Network connection type"), FALSE, network_list, network_tooltip, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_KEYMAP, "keymap", NULL, FALSE, NULL, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL }
}
-
static gchar network_tooltip[]
Definition: rdp_plugin.c:2797
+
static gchar network_tooltip[]
Definition: rdp_plugin.c:2798
-
static gpointer network_list[]
Definition: rdp_plugin.c:2703
+
static gpointer network_list[]
Definition: rdp_plugin.c:2704
-
static gpointer colordepth_list[]
Definition: rdp_plugin.c:2661
-
static gchar monitorids_tooltip[]
Definition: rdp_plugin.c:2802
+
static gpointer colordepth_list[]
Definition: rdp_plugin.c:2662
+
static gchar monitorids_tooltip[]
Definition: rdp_plugin.c:2803
-
static gchar drive_tooltip[]
Definition: rdp_plugin.c:2814
+
static gchar drive_tooltip[]
Definition: rdp_plugin.c:2815
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2841 of file rdp_plugin.c.

+

Definition at line 2842 of file rdp_plugin.c.

@@ -2190,7 +2190,7 @@ Variables
Definition: types.h:50
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2936 of file rdp_plugin.c.

+

Definition at line 2937 of file rdp_plugin.c.

@@ -2217,11 +2217,11 @@ Variables
Definition: plugin.h:49
gboolean remmina_rdp_file_import_test(RemminaFilePlugin *plugin, const gchar *from_file)
Definition: rdp_file.c:41
gboolean remmina_rdp_file_export(RemminaFilePlugin *plugin, RemminaFile *remminafile, const gchar *to_file)
Definition: rdp_file.c:282
-
static char remmina_plugin_rdp_version[256]
Definition: rdp_plugin.c:2950
+
static char remmina_plugin_rdp_version[256]
Definition: rdp_plugin.c:2951
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
RemminaFile * remmina_rdp_file_import(RemminaFilePlugin *plugin, const gchar *from_file)
Definition: rdp_file.c:185
-

Definition at line 2978 of file rdp_plugin.c.

+

Definition at line 2979 of file rdp_plugin.c.

@@ -2246,10 +2246,10 @@ Variables
Initial value:
=
{
"RDPS",
N_("RDP - Preferences"),
GETTEXT_PACKAGE,
"RDP",
}
GtkWidget * remmina_rdp_settings_new(RemminaPrefPlugin *plugin)
Definition: rdp_settings.c:696
-
static char remmina_plugin_rdp_version[256]
Definition: rdp_plugin.c:2950
+
static char remmina_plugin_rdp_version[256]
Definition: rdp_plugin.c:2951
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2993 of file rdp_plugin.c.

+

Definition at line 2994 of file rdp_plugin.c.

@@ -2274,7 +2274,7 @@ Variables
Initial value:
=
{
"", N_("Automatic negotiation"),
"nla", N_("NLA protocol security"),
"tls", N_("TLS protocol security"),
"rdp", N_("RDP protocol security"),
"ext", N_("NLA extended protocol security"),
NULL
}
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2726 of file rdp_plugin.c.

+

Definition at line 2727 of file rdp_plugin.c.

@@ -2299,7 +2299,7 @@ Variables
Initial value:
=
{
"off", N_("Off"),
"local", N_("Local"),
"remote", N_("Remote"),
NULL
}
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2717 of file rdp_plugin.c.

+

Definition at line 2718 of file rdp_plugin.c.

@@ -2324,7 +2324,7 @@ Variables
Initial value:
=
N_("Advanced setting for high latency links:\n"
"Adjusts the connection timeout. Use if your connection times out.\n"
"The highest possible value is 600000 ms (10 minutes).\n")
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2792 of file rdp_plugin.c.

+

Definition at line 2793 of file rdp_plugin.c.

@@ -2349,7 +2349,7 @@ Variables
Initial value:
=
{
"", N_("Default"),
"0", N_("0 — Windows 7 compatible"),
"1", N_("1"),
"2", N_("2"),
"3", N_("3"),
"4", N_("4"),
"5", N_("5"),
NULL
}
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2744 of file rdp_plugin.c.

+

Definition at line 2745 of file rdp_plugin.c.

@@ -2374,7 +2374,7 @@ Variables
Initial value:
=
N_("Options for redirection of USB device:\n"
" • [dbg,][id:<vid>:<pid>#…,][addr:<bus>:<addr>#…,][auto]\n"
" • auto\n"
" • id:054c:0268#4669:6e6b,addr:04:0c")
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2786 of file rdp_plugin.c.

+

Definition at line 2787 of file rdp_plugin.c.

diff --git a/public/rdp__plugin_8c_source.html b/public/rdp__plugin_8c_source.html index ca69ef883..3d3117479 100644 --- a/public/rdp__plugin_8c_source.html +++ b/public/rdp__plugin_8c_source.html @@ -86,22 +86,22 @@ $(document).ready(function(){initNavTree('rdp__plugin_8c_source.html','');});
rdp_plugin.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-2022 Antenore Gatta, Giovanni Panozzo
6  * Copyright (C) 2022-2023 Antenore Gatta, Giovanni Panozzo, Hiroyuki Tanaka
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  * In addition, as a special exception, the copyright holders give
24  * permission to link the code of portions of this program with the
25  * OpenSSL library under certain conditions as described in each
26  * individual source file, and distribute linked combinations
27  * including the two.
28  * You must obey the GNU General Public License in all respects
29  * for all of the code used other than OpenSSL. * If you modify
30  * file(s) with this exception, you may extend this exception to your
31  * version of the file(s), but you are not obligated to do so. * If you
32  * do not wish to do so, delete this exception statement from your
33  * version. * If you delete this exception statement from all source
34  * files in the program, then also delete it here.
35  *
36  */
37 
38 
39 #define _GNU_SOURCE
40 
41 #include "remmina/plugin.h"
42 #include "rdp_plugin.h"
43 #include "rdp_event.h"
44 #include "rdp_graphics.h"
45 #include "rdp_file.h"
46 #include "rdp_settings.h"
47 #include "rdp_cliprdr.h"
48 #include "rdp_monitor.h"
49 #include "rdp_channels.h"
50 
51 #include <errno.h>
52 #include <pthread.h>
53 #include <time.h>
54 #include <sys/time.h>
55 #ifdef GDK_WINDOWING_X11
56 #include <cairo/cairo-xlib.h>
57 #else
58 #include <cairo/cairo.h>
59 #endif
60 #include <freerdp/addin.h>
61 #include <freerdp/assistance.h>
62 #include <freerdp/settings.h>
63 #include <freerdp/freerdp.h>
64 #include <freerdp/constants.h>
65 #include <freerdp/client/cliprdr.h>
66 #include <freerdp/client/channels.h>
67 #include <freerdp/client/cmdline.h>
68 #include <freerdp/error.h>
69 #include <freerdp/event.h>
70 #include <winpr/memory.h>
71 #include <winpr/cmdline.h>
72 #include <ctype.h>
73 
74 #ifdef HAVE_CUPS
75 #include <cups/cups.h>
76 #endif
77 
78 #include <unistd.h>
79 #include <string.h>
80 
81 #ifdef GDK_WINDOWING_X11
82 #include <X11/Xlib.h>
83 #include <X11/XKBlib.h>
84 #include <gdk/gdkx.h>
85 #elif defined(GDK_WINDOWING_WAYLAND)
86 #include <gdk/gdkwayland.h>
87 #endif
88 
89 #if defined(__FreeBSD__)
90 #include <pthread_np.h>
91 #endif
92 
93 #include <freerdp/locale/keyboard.h>
94 
95 #define REMMINA_RDP_FEATURE_TOOL_REFRESH 1
96 #define REMMINA_RDP_FEATURE_SCALE 2
97 #define REMMINA_RDP_FEATURE_UNFOCUS 3
98 #define REMMINA_RDP_FEATURE_TOOL_SENDCTRLALTDEL 4
99 #define REMMINA_RDP_FEATURE_DYNRESUPDATE 5
100 #define REMMINA_RDP_FEATURE_MULTIMON 6
101 #define REMMINA_RDP_FEATURE_VIEWONLY 7
102 
103 #define REMMINA_CONNECTION_TYPE_NONE 0
104 
105 #ifdef WITH_FREERDP3
106  #define CLPARAM const char
107 #else
108  #define CLPARAM char
109 #endif
110 
111 
113 
114 static BOOL gfx_h264_available = FALSE;
115 
116 /* Compatibility: these functions have been introduced with https://github.com/FreeRDP/FreeRDP/commit/8c5d96784d
117  * and are missing on older FreeRDP, so we add them here.
118  * They should be removed from here after all distributed versions of FreeRDP (libwinpr) will have
119  * CommandLineParseCommaSeparatedValuesEx() onboard.
120  *
121  * (C) Copyright goes to the FreeRDP authors.
122  */
123 static CLPARAM **remmina_rdp_CommandLineParseCommaSeparatedValuesEx(const char *name, const char *list, size_t *count)
124 {
125  TRACE_CALL(__func__);
126 #if FREERDP_CHECK_VERSION(2, 0, 0)
127  return (CLPARAM **)CommandLineParseCommaSeparatedValuesEx(name, list, count);
128 #else
129  char **p;
130  char *str;
131  size_t nArgs;
132  size_t index;
133  size_t nCommas;
134  size_t prefix, len;
135 
136  nCommas = 0;
137 
138  if (count == NULL)
139  return NULL;
140 
141  *count = 0;
142 
143  if (!list) {
144  if (name) {
145  size_t len = strlen(name);
146  p = (char **)calloc(2UL + len, sizeof(char *));
147 
148  if (p) {
149  char *dst = (char *)&p[1];
150  p[0] = dst;
151  sprintf_s(dst, len + 1, "%s", name);
152  *count = 1;
153  return p;
154  }
155  }
156 
157  return NULL;
158  }
159 
160  {
161  const char *it = list;
162 
163  while ((it = strchr(it, ',')) != NULL) {
164  it++;
165  nCommas++;
166  }
167  }
168 
169  nArgs = nCommas + 1;
170 
171  if (name)
172  nArgs++;
173 
174  prefix = (nArgs + 1UL) * sizeof(char *);
175  len = strlen(list);
176  p = (char **)calloc(len + prefix + 1, sizeof(char *));
177 
178  if (!p)
179  return NULL;
180 
181  str = &((char *)p)[prefix];
182  memcpy(str, list, len);
183 
184  if (name)
185  p[0] = (char *)name;
186 
187  for (index = name ? 1 : 0; index < nArgs; index++) {
188  char *comma = strchr(str, ',');
189  p[index] = str;
190 
191  if (comma) {
192  str = comma + 1;
193  *comma = '\0';
194  }
195  }
196 
197  *count = nArgs;
198  return p;
199 #endif
200 }
201 
202 static CLPARAM **remmina_rdp_CommandLineParseCommaSeparatedValues(const char *list, size_t *count)
203 {
204  TRACE_CALL(__func__);
205  return remmina_rdp_CommandLineParseCommaSeparatedValuesEx(NULL, list, count);
206 }
207 
208 /*
209  * End of CommandLineParseCommaSeparatedValuesEx() compatibility and copyright
210  */
212 {
213  TRACE_CALL(__func__);
214  UINT16 flags;
215  rdpInput *input;
216  rfContext *rfi = GET_PLUGIN_DATA(gp);
217  RemminaPluginRdpEvent *event;
218  DISPLAY_CONTROL_MONITOR_LAYOUT *dcml;
219  CLIPRDR_FORMAT_DATA_RESPONSE response = { 0 };
220  RemminaFile *remminafile;
221 
222  if (rfi->event_queue == NULL)
223  return true;
224 
225  input = rfi->instance->input;
226 
227  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
228 
229  while ((event = (RemminaPluginRdpEvent *)g_async_queue_try_pop(rfi->event_queue)) != NULL) {
230  switch (event->type) {
232  flags = event->key_event.extended ? KBD_FLAGS_EXTENDED : 0;
233  flags |= event->key_event.up ? KBD_FLAGS_RELEASE : KBD_FLAGS_DOWN;
234  input->KeyboardEvent(input, flags, event->key_event.key_code);
235  break;
236 
238  /*
239  * TS_UNICODE_KEYBOARD_EVENT RDP message, see https://msdn.microsoft.com/en-us/library/cc240585.aspx
240  */
241  flags = event->key_event.up ? KBD_FLAGS_RELEASE : KBD_FLAGS_DOWN;
242  input->UnicodeKeyboardEvent(input, flags, event->key_event.unicode_code);
243  break;
244 
246  if (event->mouse_event.extended)
247  input->ExtendedMouseEvent(input, event->mouse_event.flags,
248  event->mouse_event.x, event->mouse_event.y);
249  else
250  input->MouseEvent(input, event->mouse_event.flags,
251  event->mouse_event.x, event->mouse_event.y);
252  break;
253 
255  rfi->clipboard.context->ClientFormatList(rfi->clipboard.context, event->clipboard_formatlist.pFormatList);
256  free(event->clipboard_formatlist.pFormatList);
257  break;
258 
260  response.msgFlags = (event->clipboard_formatdataresponse.data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
261  response.dataLen = event->clipboard_formatdataresponse.size;
262  response.requestedFormatData = event->clipboard_formatdataresponse.data;
263  rfi->clipboard.context->ClientFormatDataResponse(rfi->clipboard.context, &response);
264  break;
265 
267  REMMINA_PLUGIN_DEBUG("Sending client FormatDataRequest to server");
268  gettimeofday(&(rfi->clipboard.clientformatdatarequest_tv), NULL);
269  rfi->clipboard.context->ClientFormatDataRequest(rfi->clipboard.context, event->clipboard_formatdatarequest.pFormatDataRequest);
270  free(event->clipboard_formatdatarequest.pFormatDataRequest);
271  break;
272 
274  if (remmina_plugin_service->file_get_int(remminafile, "multimon", FALSE)) {
275  freerdp_settings_set_bool(rfi->settings, FreeRDP_UseMultimon, TRUE);
276  /* TODO Add an option for this */
277  freerdp_settings_set_bool(rfi->settings, FreeRDP_ForceMultimon, TRUE);
278  freerdp_settings_set_bool(rfi->settings, FreeRDP_Fullscreen, TRUE);
279  /* got some crashes with g_malloc0, to be investigated */
280  dcml = calloc(freerdp_settings_get_uint32(rfi->settings, FreeRDP_MonitorCount), sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
281  REMMINA_PLUGIN_DEBUG("REMMINA_RDP_EVENT_TYPE_SEND_MONITOR_LAYOUT:");
282  if (!dcml)
283  break;
284 
285  const rdpMonitor *base = freerdp_settings_get_pointer(rfi->settings, FreeRDP_MonitorDefArray);
286  for (gint i = 0; i < freerdp_settings_get_uint32(rfi->settings, FreeRDP_MonitorCount); ++i) {
287  const rdpMonitor *current = &base[i];
288  REMMINA_PLUGIN_DEBUG("Sending display layout for monitor n° %d", i);
289  dcml[i].Flags = (current->is_primary ? DISPLAY_CONTROL_MONITOR_PRIMARY : 0);
290  REMMINA_PLUGIN_DEBUG("Monitor %d is primary: %d", i, dcml[i].Flags);
291  dcml[i].Left = current->x;
292  REMMINA_PLUGIN_DEBUG("Monitor %d x: %d", i, dcml[i].Left);
293  dcml[i].Top = current->y;
294  REMMINA_PLUGIN_DEBUG("Monitor %d y: %d", i, dcml[i].Top);
295  dcml[i].Width = current->width;
296  REMMINA_PLUGIN_DEBUG("Monitor %d width: %d", i, dcml[i].Width);
297  dcml[i].Height = current->height;
298  REMMINA_PLUGIN_DEBUG("Monitor %d height: %d", i, dcml[i].Height);
299  dcml[i].PhysicalWidth = current->attributes.physicalWidth;
300  REMMINA_PLUGIN_DEBUG("Monitor %d physical width: %d", i, dcml[i].PhysicalWidth);
301  dcml[i].PhysicalHeight = current->attributes.physicalHeight;
302  REMMINA_PLUGIN_DEBUG("Monitor %d physical height: %d", i, dcml[i].PhysicalHeight);
303  if (current->attributes.orientation)
304  dcml[i].Orientation = current->attributes.orientation;
305  else
306  dcml[i].Orientation = event->monitor_layout.desktopOrientation;
307  REMMINA_PLUGIN_DEBUG("Monitor %d orientation: %d", i, dcml[i].Orientation);
308  dcml[i].DesktopScaleFactor = event->monitor_layout.desktopScaleFactor;
309  dcml[i].DeviceScaleFactor = event->monitor_layout.deviceScaleFactor;
310  }
311  rfi->dispcontext->SendMonitorLayout(rfi->dispcontext, freerdp_settings_get_uint32(rfi->settings, FreeRDP_MonitorCount), dcml);
312  g_free(dcml);
313  } else {
314  dcml = g_malloc0(sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
315  if (dcml) {
316  dcml->Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
317  dcml->Width = event->monitor_layout.width;
318  dcml->Height = event->monitor_layout.height;
319  dcml->Orientation = event->monitor_layout.desktopOrientation;
320  dcml->DesktopScaleFactor = event->monitor_layout.desktopScaleFactor;
321  dcml->DeviceScaleFactor = event->monitor_layout.deviceScaleFactor;
322  rfi->dispcontext->SendMonitorLayout(rfi->dispcontext, 1, dcml);
323  g_free(dcml); \
324  }
325  }
326  break;
328  /* Disconnect requested via GUI (i.e: tab destroy/close) */
329  freerdp_abort_connect(rfi->instance);
330  break;
331  }
332 
333  g_free(event);
334  }
335 
336  return true;
337 }
338 
340 {
341  TRACE_CALL(__func__);
342 
343  /* Opens the optional SSH tunnel if needed.
344  * Used also when reopening the same tunnel for a FreeRDP reconnect.
345  * Returns TRUE if all OK, and setups correct rfi->Settings values
346  * with connection and certificate parameters */
347 
348  gchar *hostport;
349  gchar *s;
350  gchar *host;
351  gchar *cert_host;
352  gint cert_port;
353  gint port;
354 
355  rfContext *rfi = GET_PLUGIN_DATA(gp);
356 
357  REMMINA_PLUGIN_DEBUG("Tunnel init");
358  hostport = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, 3389, FALSE);
359  if (hostport == NULL)
360  return FALSE;
361 
362  remmina_plugin_service->get_server_port(hostport, 3389, &host, &port);
363 
364  if (host[0] == 0)
365  return FALSE;
366 
367  REMMINA_PLUGIN_DEBUG("protocol_plugin_start_direct_tunnel() returned %s", hostport);
368 
369  cert_host = host;
370  cert_port = port;
371 
372  if (!rfi->is_reconnecting) {
373  /* settings->CertificateName and settings->ServerHostname is created
374  * only on 1st connect, not on reconnections */
375 
376  freerdp_settings_set_string(rfi->settings, FreeRDP_ServerHostname, host);
377 
378  if (cert_port == 3389) {
379  freerdp_settings_set_string(rfi->settings, FreeRDP_CertificateName, cert_host);
380  } else {
381  s = g_strdup_printf("%s:%d", cert_host, cert_port);
382  freerdp_settings_set_string(rfi->settings, FreeRDP_CertificateName, s);
383  g_free(s);
384  }
385  }
386 
387  REMMINA_PLUGIN_DEBUG("Tunnel has been optionally initialized. Now connecting to %s:%d", host, port);
388 
389  if (cert_host != host) g_free(cert_host);
390  g_free(host);
391  g_free(hostport);
392 
393  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ServerPort, port);
394 
395  return TRUE;
396 }
397 
398 static BOOL rf_auto_reconnect(rfContext *rfi)
399 {
400  TRACE_CALL(__func__);
401  rdpSettings *settings = rfi->instance->settings;
403  time_t treconn;
404  gchar *cval;
405  gint maxattempts;
406 
408  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
409 
410  rfi->is_reconnecting = TRUE;
411  rfi->stop_reconnecting_requested = FALSE;
412 
413  /* Get the value set in FreeRDP_AutoReconnectMaxRetries (20) */
414  maxattempts = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
415  REMMINA_PLUGIN_DEBUG("maxattempts from default: %d", maxattempts);
416  /* Get the value from the global preferences if any */
417  if ((cval = remmina_plugin_service->pref_get_value("rdp_reconnect_attempts")) != NULL)
418  maxattempts = atoi(cval);
419  REMMINA_PLUGIN_DEBUG("maxattempts from general preferences: %d", maxattempts);
420  /* Get the value from the profile if any, otherwise uses the value of maxattempts */
421  maxattempts = remmina_plugin_service->file_get_int(remminafile, "rdp_reconnect_attempts", maxattempts);
422  REMMINA_PLUGIN_DEBUG("maxattempts from general plugin: %d", maxattempts);
423  /* If maxattemps is <= 0, we get the value from FreeRDP_AutoReconnectMaxRetries (20) */
424  if (maxattempts <= 0)
425  maxattempts = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
426  freerdp_settings_set_uint32(settings, FreeRDP_AutoReconnectMaxRetries, maxattempts);
427  REMMINA_PLUGIN_DEBUG("maxattempts set to: %d", maxattempts);
428 
429  rfi->reconnect_maxattempts = maxattempts;
430  rfi->reconnect_nattempt = 0;
431 
432  /* Only auto reconnect on network disconnects. */
433  switch (freerdp_error_info(rfi->instance)) {
434  case ERRINFO_GRAPHICS_SUBSYSTEM_FAILED:
435  /* Disconnected by server hitting a bug or resource limit */
436  break;
437  case ERRINFO_SUCCESS:
438  /* A network disconnect was detected */
439  break;
440  default:
441  rfi->is_reconnecting = FALSE;
442  return FALSE;
443  }
444 
445  if (!freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled)) {
446  /* No auto-reconnect - just quit */
447  rfi->is_reconnecting = FALSE;
448  return FALSE;
449  }
450 
451  /* A network disconnect was detected and we should try to reconnect */
452  REMMINA_PLUGIN_DEBUG("[%s] network disconnection detected, initiating reconnection attempt",
453  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
454 
455  ui = g_new0(RemminaPluginRdpUiObject, 1);
458 
459  /* Sleep half a second to allow:
460  * - processing of the UI event we just pushed on the queue
461  * - better network conditions
462  * Remember: We hare on a thread, so the main gui won’t lock */
463  usleep(500000);
464 
465  /* Perform an auto-reconnect. */
466  while (TRUE) {
467  /* Quit retrying if max retries has been exceeded */
468  if (rfi->reconnect_nattempt++ >= rfi->reconnect_maxattempts) {
469  REMMINA_PLUGIN_DEBUG("[%s] maximum number of reconnection attempts exceeded.",
470  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
471  break;
472  }
473 
474  if (rfi->stop_reconnecting_requested) {
475  REMMINA_PLUGIN_DEBUG("[%s] reconnect request loop interrupted by user.",
476  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
477  break;
478  }
479 
480  /* Attempt the next reconnect */
481  REMMINA_PLUGIN_DEBUG("[%s] reconnection, attempt #%d of %d",
482  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname), rfi->reconnect_nattempt, rfi->reconnect_maxattempts);
483 
484  ui = g_new0(RemminaPluginRdpUiObject, 1);
487 
488  treconn = time(NULL);
489 
490  /* Reconnect the SSH tunnel, if needed */
492  REMMINA_PLUGIN_DEBUG("[%s] unable to recreate tunnel with remmina_rdp_tunnel_init.",
493  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
494  } else {
495  if (freerdp_reconnect(rfi->instance)) {
496  /* Reconnection is successful */
497  REMMINA_PLUGIN_DEBUG("[%s] reconnected.", freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
498  rfi->is_reconnecting = FALSE;
499  return TRUE;
500  }
501  }
502 
503  /* Wait until 5 secs have elapsed from last reconnect attempt, while checking for rfi->stop_reconnecting_requested */
504  while (time(NULL) - treconn < 5) {
506  break;
507  usleep(200000); // 200ms sleep
508  }
509  }
510 
511  rfi->is_reconnecting = FALSE;
512  return FALSE;
513 }
514 
515 static BOOL rf_begin_paint(rdpContext *context)
516 {
517  TRACE_CALL(__func__);
518  rdpGdi *gdi;
519 
520  if (!context)
521  return FALSE;
522 
523  gdi = context->gdi;
524  if (!gdi || !gdi->primary || !gdi->primary->hdc || !gdi->primary->hdc->hwnd)
525  return FALSE;
526 
527  return TRUE;
528 }
529 
530 static BOOL rf_end_paint(rdpContext *context)
531 {
532  TRACE_CALL(__func__);
533  rdpGdi *gdi;
534  rfContext *rfi;
536  int i, ninvalid;
537  region *reg;
538  HGDI_RGN cinvalid;
539 
540  gdi = context->gdi;
541  rfi = (rfContext *)context;
542 
543  if (gdi == NULL || gdi->primary == NULL || gdi->primary->hdc == NULL || gdi->primary->hdc->hwnd == NULL)
544  return TRUE;
545 
546  if (gdi->primary->hdc->hwnd->invalid->null)
547  return TRUE;
548 
549  if (gdi->primary->hdc->hwnd->ninvalid < 1)
550  return TRUE;
551 
552  ninvalid = gdi->primary->hdc->hwnd->ninvalid;
553  cinvalid = gdi->primary->hdc->hwnd->cinvalid;
554  reg = (region *)g_malloc(sizeof(region) * ninvalid);
555  for (i = 0; i < ninvalid; i++) {
556  reg[i].x = cinvalid[i].x;
557  reg[i].y = cinvalid[i].y;
558  reg[i].w = cinvalid[i].w;
559  reg[i].h = cinvalid[i].h;
560  }
561 
562  ui = g_new0(RemminaPluginRdpUiObject, 1);
564  ui->reg.ninvalid = ninvalid;
565  ui->reg.ureg = reg;
566 
568 
569 
570  gdi->primary->hdc->hwnd->invalid->null = TRUE;
571  gdi->primary->hdc->hwnd->ninvalid = 0;
572 
573 
574  return TRUE;
575 }
576 
577 static BOOL rf_desktop_resize(rdpContext *context)
578 {
579  TRACE_CALL(__func__);
580  rfContext *rfi;
583  UINT32 w, h;
584 
585  rfi = (rfContext *)context;
586  gp = rfi->protocol_widget;
587 
588  w = freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth);
589  h = freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopHeight);
590  remmina_plugin_service->protocol_plugin_set_width(gp, w);
591  remmina_plugin_service->protocol_plugin_set_height(gp, h);
592 
593  ui = g_new0(RemminaPluginRdpUiObject, 1);
597 
598  /* Tell libfreerdp to change its internal GDI bitmap width and heigt,
599  * this will also destroy gdi->primary_buffer, making our rfi->surface invalid */
600  gdi_resize(((rdpContext *)rfi)->gdi, w, h);
601 
602  /* Call to remmina_rdp_event_update_scale(gp) on the main UI thread,
603  * this will recreate rfi->surface from gdi->primary_buffer */
604 
605  ui = g_new0(RemminaPluginRdpUiObject, 1);
609 
610  remmina_plugin_service->protocol_plugin_desktop_resize(gp);
611 
612  return TRUE;
613 }
614 
615 static BOOL rf_play_sound(rdpContext *context, const PLAY_SOUND_UPDATE *play_sound)
616 {
617  TRACE_CALL(__func__);
618  rfContext *rfi;
620  GdkDisplay *disp;
621 
622  rfi = (rfContext *)context;
623  gp = rfi->protocol_widget;
624 
625  disp = gtk_widget_get_display(GTK_WIDGET(gp));
626  gdk_display_beep(disp);
627 
628  return TRUE;
629 }
630 
631 static BOOL rf_keyboard_set_indicators(rdpContext *context, UINT16 led_flags)
632 {
633  TRACE_CALL(__func__);
634  rfContext *rfi;
636  GdkDisplay *disp;
637 
638  rfi = (rfContext *)context;
639  gp = rfi->protocol_widget;
640  disp = gtk_widget_get_display(GTK_WIDGET(gp));
641 
642 #ifdef GDK_WINDOWING_X11
643  if (GDK_IS_X11_DISPLAY(disp)) {
644  /* TODO: We are not on the main thread. Will X.Org complain? */
645  Display *x11_display;
646  x11_display = gdk_x11_display_get_xdisplay(disp);
647  XkbLockModifiers(x11_display, XkbUseCoreKbd,
648  LockMask | Mod2Mask,
649  ((led_flags & KBD_SYNC_CAPS_LOCK) ? LockMask : 0) |
650  ((led_flags & KBD_SYNC_NUM_LOCK) ? Mod2Mask : 0)
651  );
652 
653  /* TODO: Add support to KANA_LOCK and SCROLL_LOCK */
654  }
655 #endif
656 
657  return TRUE;
658 }
659 
660 static BOOL rf_keyboard_set_ime_status(rdpContext *context, UINT16 imeId, UINT32 imeState,
661  UINT32 imeConvMode)
662 {
663  TRACE_CALL(__func__);
664  if (!context)
665  return FALSE;
666 
667  /* Unimplemented, we ignore it */
668 
669  return TRUE;
670 }
671 
672 static BOOL remmina_rdp_pre_connect(freerdp *instance)
673 {
674  TRACE_CALL(__func__);
675  rdpChannels *channels;
676  rdpSettings *settings;
677  rdpContext *context = instance->context;
678 
679  settings = instance->settings;
680  channels = context->channels;
681  freerdp_settings_set_uint32(settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNIX);
682  freerdp_settings_set_uint32(settings, FreeRDP_OsMinorType, OSMINORTYPE_UNSPECIFIED);
683  freerdp_settings_set_bool(settings, FreeRDP_BitmapCacheEnabled, TRUE);
684  freerdp_settings_set_uint32(settings, FreeRDP_OffscreenSupportLevel, 1);
685 
686  PubSub_SubscribeChannelConnected(instance->context->pubSub,
688  PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
690 
691  if (!freerdp_client_load_addins(channels, settings))
692  return FALSE;
693 
694  return true;
695 }
696 
697 static BOOL remmina_rdp_post_connect(freerdp *instance)
698 {
699  TRACE_CALL(__func__);
700  rfContext *rfi;
703  UINT32 freerdp_local_color_format;
704 
705  rfi = (rfContext *)instance->context;
706  gp = rfi->protocol_widget;
707  rfi->postconnect_error = REMMINA_POSTCONNECT_ERROR_OK;
708 
709  rfi->attempt_interactive_authentication = FALSE; // We authenticated!
710 
711  rfi->srcBpp = freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth);
712 
713  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_RemoteFxCodec) == FALSE)
714  rfi->sw_gdi = TRUE;
715 
716  rf_register_graphics(instance->context->graphics);
717 
718  REMMINA_PLUGIN_DEBUG("bpp: %d", rfi->bpp);
719  switch (rfi->bpp) {
720  case 24:
721  REMMINA_PLUGIN_DEBUG("CAIRO_FORMAT_RGB24");
722  freerdp_local_color_format = PIXEL_FORMAT_BGRX32;
723  rfi->cairo_format = CAIRO_FORMAT_RGB24;
724  break;
725  case 32:
730  REMMINA_PLUGIN_DEBUG("CAIRO_FORMAT_RGB24");
731  freerdp_local_color_format = PIXEL_FORMAT_BGRA32;
732  rfi->cairo_format = CAIRO_FORMAT_RGB24;
733  break;
734  default:
735  REMMINA_PLUGIN_DEBUG("CAIRO_FORMAT_RGB16_565");
736  freerdp_local_color_format = PIXEL_FORMAT_RGB16;
737  rfi->cairo_format = CAIRO_FORMAT_RGB16_565;
738  break;
739  }
740 
741  if (!gdi_init(instance, freerdp_local_color_format)) {
742  rfi->postconnect_error = REMMINA_POSTCONNECT_ERROR_GDI_INIT;
743  return FALSE;
744  }
745 
746  if (instance->context->codecs->h264 == NULL && freerdp_settings_get_bool(rfi->settings, FreeRDP_GfxH264)) {
747  gdi_free(instance);
748  rfi->postconnect_error = REMMINA_POSTCONNECT_ERROR_NO_H264;
749  return FALSE;
750  }
751 
752  // pointer_cache_register_callbacks(instance->update);
753 
754  instance->update->BeginPaint = rf_begin_paint;
755  instance->update->EndPaint = rf_end_paint;
756  instance->update->DesktopResize = rf_desktop_resize;
757 
758  instance->update->PlaySound = rf_play_sound;
759  instance->update->SetKeyboardIndicators = rf_keyboard_set_indicators;
760  instance->update->SetKeyboardImeStatus = rf_keyboard_set_ime_status;
761 
763  rfi->connected = true;
764 
765  ui = g_new0(RemminaPluginRdpUiObject, 1);
768 
769  return TRUE;
770 }
771 
772 static BOOL remmina_rdp_authenticate(freerdp *instance, char **username, char **password, char **domain)
773 {
774  TRACE_CALL(__func__);
775  gchar *s_username, *s_password, *s_domain;
776  gint ret;
777  rfContext *rfi;
779  gboolean save;
780  gboolean disablepasswordstoring;
781  RemminaFile *remminafile;
782 
783  rfi = (rfContext *)instance->context;
784  gp = rfi->protocol_widget;
785  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
786  disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
787 
788  ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
790  _("Enter RDP authentication credentials"),
791  remmina_plugin_service->file_get_string(remminafile, "username"),
792  remmina_plugin_service->file_get_string(remminafile, "password"),
793  remmina_plugin_service->file_get_string(remminafile, "domain"),
794  NULL);
795  if (ret == GTK_RESPONSE_OK) {
796  s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
797  if (s_username) freerdp_settings_set_string(rfi->settings, FreeRDP_Username, s_username);
798 
799  s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
800  if (s_password) freerdp_settings_set_string(rfi->settings, FreeRDP_Password, s_password);
801 
802  s_domain = remmina_plugin_service->protocol_plugin_init_get_domain(gp);
803  if (s_domain) freerdp_settings_set_string(rfi->settings, FreeRDP_Domain, s_domain);
804 
805  remmina_plugin_service->file_set_string(remminafile, "username", s_username);
806  remmina_plugin_service->file_set_string(remminafile, "domain", s_domain);
807 
808  save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
809  if (save) {
810  // User has requested to save credentials. We put the password
811  // into remminafile->settings. It will be saved later, on successful connection, by
812  // rcw.c
813  remmina_plugin_service->file_set_string(remminafile, "password", s_password);
814  } else {
815  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
816  }
817 
818 
819  if (s_username) g_free(s_username);
820  if (s_password) g_free(s_password);
821  if (s_domain) g_free(s_domain);
822 
823  return TRUE;
824  } else {
825  return FALSE;
826  }
827 
828  return TRUE;
829 }
830 
831 static BOOL remmina_rdp_gw_authenticate(freerdp *instance, char **username, char **password, char **domain)
832 {
833  TRACE_CALL(__func__);
834  gchar *s_username, *s_password, *s_domain;
835  gint ret;
836  rfContext *rfi;
838  gboolean save;
839  gboolean disablepasswordstoring;
840  gboolean basecredforgw;
841  RemminaFile *remminafile;
842 
843  rfi = (rfContext *)instance->context;
844  gp = rfi->protocol_widget;
845  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
846 
847  if (!remmina_plugin_service->file_get_string(remminafile, "gateway_server"))
848  return false;
849  disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
850  basecredforgw = remmina_plugin_service->file_get_int(remminafile, "base-cred-for-gw", FALSE);
851 
852  if (basecredforgw) {
853  ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
855  _("Enter RDP authentication credentials"),
856  remmina_plugin_service->file_get_string(remminafile, "username"),
857  remmina_plugin_service->file_get_string(remminafile, "password"),
858  remmina_plugin_service->file_get_string(remminafile, "domain"),
859  NULL);
860  } else {
861  ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
863  _("Enter RDP gateway authentication credentials"),
864  remmina_plugin_service->file_get_string(remminafile, "gateway_username"),
865  remmina_plugin_service->file_get_string(remminafile, "gateway_password"),
866  remmina_plugin_service->file_get_string(remminafile, "gateway_domain"),
867  NULL);
868  }
869 
870 
871  if (ret == GTK_RESPONSE_OK) {
872  s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
873  if (s_username) freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayUsername, s_username);
874 
875  s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
876  if (s_password) freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayPassword, s_password);
877 
878  s_domain = remmina_plugin_service->protocol_plugin_init_get_domain(gp);
879  if (s_domain) freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayDomain, s_domain);
880 
881  save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
882 
883  if (basecredforgw) {
884  remmina_plugin_service->file_set_string(remminafile, "username", s_username);
885  remmina_plugin_service->file_set_string(remminafile, "domain", s_domain);
886  if (save)
887  remmina_plugin_service->file_set_string(remminafile, "password", s_password);
888  else
889  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
890  } else {
891  remmina_plugin_service->file_set_string(remminafile, "gateway_username", s_username);
892  remmina_plugin_service->file_set_string(remminafile, "gateway_domain", s_domain);
893  if (save)
894  remmina_plugin_service->file_set_string(remminafile, "gateway_password", s_password);
895  else
896  remmina_plugin_service->file_set_string(remminafile, "gateway_password", NULL);
897  }
898 
899  if (s_username) g_free(s_username);
900  if (s_password) g_free(s_password);
901  if (s_domain) g_free(s_domain);
902 
903  return true;
904  } else {
905  return false;
906  }
907 
908  return true;
909 }
910 
911 static DWORD remmina_rdp_verify_certificate_ex(freerdp *instance, const char *host, UINT16 port,
912  const char *common_name, const char *subject,
913  const char *issuer, const char *fingerprint, DWORD flags)
914 {
915  TRACE_CALL(__func__);
916  gint status;
917  rfContext *rfi;
919 
920  rfi = (rfContext *)instance->context;
921  gp = rfi->protocol_widget;
922 
923  status = remmina_plugin_service->protocol_plugin_init_certificate(gp, subject, issuer, fingerprint);
924 
925  if (status == GTK_RESPONSE_OK)
926  return 1;
927 
928  return 0;
929 }
930 
931 static DWORD
932 remmina_rdp_verify_certificate(freerdp *instance, const char *common_name, const char *subject, const char *issuer, const char *fingerprint, BOOL host_mismatch) __attribute__ ((unused));
933 static DWORD
934 remmina_rdp_verify_certificate(freerdp *instance, const char *common_name, const char *subject,
935  const char *issuer, const char *fingerprint, BOOL host_mismatch)
936 {
937  TRACE_CALL(__func__);
938  gint status;
939  rfContext *rfi;
941 
942  rfi = (rfContext *)instance->context;
943  gp = rfi->protocol_widget;
944 
945  status = remmina_plugin_service->protocol_plugin_init_certificate(gp, subject, issuer, fingerprint);
946 
947  if (status == GTK_RESPONSE_OK)
948  return 1;
949 
950  return 0;
951 }
952 
953 static DWORD remmina_rdp_verify_changed_certificate_ex(freerdp *instance, const char *host, UINT16 port,
954  const char *common_name, const char *subject,
955  const char *issuer, const char *fingerprint,
956  const char *old_subject, const char *old_issuer,
957  const char *old_fingerprint, DWORD flags)
958 {
959  TRACE_CALL(__func__);
960  gint status;
961  rfContext *rfi;
963 
964  rfi = (rfContext *)instance->context;
965  gp = rfi->protocol_widget;
966 
967  status = remmina_plugin_service->protocol_plugin_changed_certificate(gp, subject, issuer, fingerprint, old_fingerprint);
968 
969  if (status == GTK_RESPONSE_OK)
970  return 1;
971 
972  return 0;
973 }
974 
975 static void remmina_rdp_post_disconnect(freerdp *instance)
976 {
977  TRACE_CALL(__func__);
978 
979  if (!instance || !instance->context)
980  return;
981 
982  PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
984  PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
986 
987  /* The remaining cleanup will be continued on main thread by complete_cleanup_on_main_thread() */
988 }
989 
991 {
992  TRACE_CALL(__func__);
993  DWORD status;
994  gchar buf[100];
995  rfContext *rfi = GET_PLUGIN_DATA(gp);
996 
997  while (!freerdp_shall_disconnect(rfi->instance)) {
998  HANDLE handles[64]={0};
999  DWORD nCount = freerdp_get_event_handles(rfi->instance->context, &handles[0], 64);
1000  if (rfi->event_handle)
1001  handles[nCount++] = rfi->event_handle;
1002 
1003  handles[nCount++] = rfi->instance->context->abortEvent;
1004 
1005  if (nCount == 0) {
1006  fprintf(stderr, "freerdp_get_event_handles failed\n");
1007  break;
1008  }
1009 
1010  status = WaitForMultipleObjects(nCount, handles, FALSE, 100);
1011 
1012  if (status == WAIT_FAILED) {
1013  fprintf(stderr, "WaitForMultipleObjects failed with %lu\n", (unsigned long)status);
1014  break;
1015  }
1016 
1017  if (rfi->event_handle && WaitForSingleObject(rfi->event_handle, 0) == WAIT_OBJECT_0) {
1018  if (!rf_process_event_queue(gp)) {
1019  fprintf(stderr, "Could not process local keyboard/mouse event queue\n");
1020  break;
1021  }
1022  if (read(rfi->event_pipe[0], buf, sizeof(buf))) {
1023  }
1024  }
1025 
1026  /* Check if a processed event called freerdp_abort_connect() and exit if true */
1027  if (WaitForSingleObject(rfi->instance->context->abortEvent, 0) == WAIT_OBJECT_0)
1028  /* Session disconnected by local user action */
1029  break;
1030 
1031  if (!freerdp_check_event_handles(rfi->instance->context)) {
1032  if (rf_auto_reconnect(rfi)) {
1033  /* Reset the possible reason/error which made us doing many reconnection reattempts and continue */
1034  remmina_plugin_service->protocol_plugin_set_error(gp, NULL);
1035  continue;
1036  }
1037  if (freerdp_get_last_error(rfi->instance->context) == FREERDP_ERROR_SUCCESS)
1038  fprintf(stderr, "Could not check FreeRDP file descriptor\n");
1039  break;
1040  }
1041  }
1042  const gchar *host = freerdp_settings_get_string (rfi->settings, FreeRDP_ServerHostname);
1043  // TRANSLATORS: the placeholder may be either an IP/FQDN or a server hostname
1044  REMMINA_PLUGIN_AUDIT(_("Disconnected from %s via RDP"), host);
1045  freerdp_disconnect(rfi->instance);
1046  REMMINA_PLUGIN_DEBUG("RDP client disconnected");
1047 }
1048 
1049 static int remmina_rdp_load_static_channel_addin(rdpChannels *channels, rdpSettings *settings, char *name, void *data)
1050 {
1051  TRACE_CALL(__func__);
1052  PVIRTUALCHANNELENTRY entry = NULL;
1053  PVIRTUALCHANNELENTRYEX entryEx = NULL;
1054 
1055  entryEx = (PVIRTUALCHANNELENTRYEX)(void *)freerdp_load_channel_addin_entry(
1056  name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC | FREERDP_ADDIN_CHANNEL_ENTRYEX);
1057 
1058  if (!entryEx)
1059  entry = freerdp_load_channel_addin_entry(name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC);
1060 
1061  if (entryEx) {
1062  if (freerdp_channels_client_load_ex(channels, settings, entryEx, data) == 0) {
1063  fprintf(stderr, "loading channel %s\n", name);
1064  return TRUE;
1065  }
1066  } else if (entry) {
1067  if (freerdp_channels_client_load(channels, settings, entry, data) == 0) {
1068  fprintf(stderr, "loading channel %s\n", name);
1069  return TRUE;
1070  }
1071  }
1072 
1073  return FALSE;
1074 }
1075 
1076 static gchar *remmina_rdp_find_prdriver(char *smap, char *prn)
1077 {
1078  char c, *p, *dr;
1079  int matching;
1080  size_t sz;
1081 
1082  enum { S_WAITPR,
1083  S_INPRINTER,
1084  S_WAITCOLON,
1085  S_WAITDRIVER,
1086  S_INDRIVER,
1087  S_WAITSEMICOLON } state = S_WAITPR;
1088 
1089  matching = 0;
1090  while ((c = *smap++) != 0) {
1091  switch (state) {
1092  case S_WAITPR:
1093  if (c != '\"') return NULL;
1094  state = S_INPRINTER;
1095  p = prn;
1096  matching = 1;
1097  break;
1098  case S_INPRINTER:
1099  if (matching && c == *p && *p != 0) {
1100  p++;
1101  } else if (c == '\"') {
1102  if (*p != 0)
1103  matching = 0;
1104  state = S_WAITCOLON;
1105  } else {
1106  matching = 0;
1107  }
1108  break;
1109  case S_WAITCOLON:
1110  if (c != ':')
1111  return NULL;
1112  state = S_WAITDRIVER;
1113  break;
1114  case S_WAITDRIVER:
1115  if (c != '\"')
1116  return NULL;
1117  state = S_INDRIVER;
1118  dr = smap;
1119  break;
1120  case S_INDRIVER:
1121  if (c == '\"') {
1122  if (matching)
1123  goto found;
1124  else
1125  state = S_WAITSEMICOLON;
1126  }
1127  break;
1128  case S_WAITSEMICOLON:
1129  if (c != ';')
1130  return NULL;
1131  state = S_WAITPR;
1132  break;
1133  }
1134  }
1135  return NULL;
1136 
1137 found:
1138  sz = smap - dr;
1139  p = (char *)malloc(sz);
1140  memcpy(p, dr, sz);
1141  p[sz - 1] = 0;
1142  return p;
1143 }
1144 
1145 #ifdef HAVE_CUPS
1146 
1151 static int remmina_rdp_set_printers(void *user_data, unsigned flags, cups_dest_t *dest)
1152 {
1153  rfContext *rfi = (rfContext *)user_data;
1155 
1170  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1171  const gchar *s = remmina_plugin_service->file_get_string(remminafile, "printer_overrides");
1172 
1173  RDPDR_PRINTER *printer;
1174  printer = (RDPDR_PRINTER *)calloc(1, sizeof(RDPDR_PRINTER));
1175 
1176 #ifdef WITH_FREERDP3
1177  RDPDR_DEVICE *pdev;
1178  pdev = &(printer->device);
1179 #else
1180  RDPDR_PRINTER *pdev;
1181  pdev = printer;
1182 #endif
1183 
1184  pdev->Type = RDPDR_DTYP_PRINT;
1185  REMMINA_PLUGIN_DEBUG("Printer Type: %d", pdev->Type);
1186 
1187  freerdp_settings_set_bool(rfi->settings, FreeRDP_RedirectPrinters, TRUE);
1188  freerdp_settings_set_bool(rfi->settings, FreeRDP_DeviceRedirection, TRUE);
1189 
1190  REMMINA_PLUGIN_DEBUG("Destination: %s", dest->name);
1191  if (!(pdev->Name = _strdup(dest->name))) {
1192  free(printer);
1193  return 1;
1194  }
1195 
1196  REMMINA_PLUGIN_DEBUG("Printer Name: %s", pdev->Name);
1197 
1198  if (s) {
1199  gchar *d = remmina_rdp_find_prdriver(strdup(s), pdev->Name);
1200  if (d) {
1201  printer->DriverName = strdup(d);
1202  REMMINA_PLUGIN_DEBUG("Printer DriverName set to: %s", printer->DriverName);
1203  g_free(d);
1204  } else {
1210  free(pdev->Name);
1211  free(printer);
1212  return 1;
1213  }
1214  } else {
1215  /* We set to a default driver*/
1216  printer->DriverName = _strdup("MS Publisher Imagesetter");
1217  }
1218 
1219  REMMINA_PLUGIN_DEBUG("Printer Driver: %s", printer->DriverName);
1220  if (!freerdp_device_collection_add(rfi->settings, (RDPDR_DEVICE *)printer)) {
1221  free(printer->DriverName);
1222  free(pdev->Name);
1223  free(printer);
1224  return 1;
1225  }
1226 
1227  return 1;
1228 }
1229 #endif /* HAVE_CUPS */
1230 
1231 /* Send Ctrl+Alt+Del keystrokes to the plugin drawing_area widget */
1233 {
1234  TRACE_CALL(__func__);
1235  guint keys[] = { GDK_KEY_Control_L, GDK_KEY_Alt_L, GDK_KEY_Delete };
1236  rfContext *rfi = GET_PLUGIN_DATA(gp);
1237 
1238  remmina_plugin_service->protocol_plugin_send_keys_signals(rfi->drawing_area,
1239  keys, G_N_ELEMENTS(keys), GDK_KEY_PRESS | GDK_KEY_RELEASE);
1240 }
1241 
1242 static gboolean remmina_rdp_set_connection_type(rdpSettings *settings, guint32 type)
1243 {
1244  freerdp_settings_set_uint32(settings, FreeRDP_ConnectionType, type);
1245 
1246  if (type == CONNECTION_TYPE_MODEM) {
1247  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, TRUE);
1248  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, FALSE);
1249  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, FALSE);
1250  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE);
1251  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE);
1252  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, TRUE);
1253  } else if (type == CONNECTION_TYPE_BROADBAND_LOW) {
1254  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, TRUE);
1255  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, FALSE);
1256  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, FALSE);
1257  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE);
1258  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE);
1259  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
1260  } else if (type == CONNECTION_TYPE_SATELLITE) {
1261  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, TRUE);
1262  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, FALSE);
1263  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
1264  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE);
1265  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE);
1266  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
1267  } else if (type == CONNECTION_TYPE_BROADBAND_HIGH) {
1268  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, TRUE);
1269  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, FALSE);
1270  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
1271  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE);
1272  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE);
1273  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
1274  } else if (type == CONNECTION_TYPE_WAN) {
1275  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, FALSE);
1276  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, TRUE);
1277  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
1278  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, FALSE);
1279  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, FALSE);
1280  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
1281  } else if (type == CONNECTION_TYPE_LAN) {
1282  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, FALSE);
1283  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, TRUE);
1284  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
1285  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, FALSE);
1286  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, FALSE);
1287  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
1288  } else if (type == CONNECTION_TYPE_AUTODETECT) {
1289  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, FALSE);
1290  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, TRUE);
1291  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
1292  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, FALSE);
1293  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, FALSE);
1294  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
1295  freerdp_settings_set_bool(settings, FreeRDP_NetworkAutoDetect, TRUE);
1296 
1297  /* Automatically activate GFX and RFX codec support */
1298 #ifdef WITH_GFX_H264
1299  freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, gfx_h264_available);
1300  freerdp_settings_set_bool(settings, FreeRDP_GfxH264, gfx_h264_available);
1301 #endif
1302  freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE);
1303  freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline, TRUE);
1304  } else if (type == REMMINA_CONNECTION_TYPE_NONE) {
1305  return FALSE;
1306  } else {
1307  return FALSE;
1308  }
1309 
1310  return TRUE;
1311 }
1312 
1313 #ifdef GDK_WINDOWING_X11
1314 #if FREERDP_CHECK_VERSION(2, 3, 0)
1315 static gchar *remmina_get_rdp_kbd_remap(const gchar *keymap)
1316 {
1317  TRACE_CALL(__func__);
1318  guint *table;
1319  gchar keys[20];
1320  gchar *rdp_kbd_remap = NULL;
1321  gint i;
1322  Display *display;
1323 
1324  table = remmina_plugin_service->pref_keymap_get_table(keymap);
1325  if (!table)
1326  return rdp_kbd_remap;
1327  rdp_kbd_remap = g_malloc0(512);
1328  display = XOpenDisplay(0);
1329  for (i = 0; table[i] > 0; i += 2) {
1330  g_snprintf(keys, sizeof(keys), "0x%02x=0x%02x", freerdp_keyboard_get_rdp_scancode_from_x11_keycode(XKeysymToKeycode(display, table[i])),
1331  freerdp_keyboard_get_rdp_scancode_from_x11_keycode(XKeysymToKeycode(display, table[i + 1])));
1332  if (i > 0)
1333  g_strlcat(rdp_kbd_remap, ",", 512);
1334  g_strlcat(rdp_kbd_remap, keys, 512);
1335  }
1336  XCloseDisplay(display);
1337 
1338  return rdp_kbd_remap;
1339 }
1340 #endif
1341 #endif
1342 
1344 {
1345  TRACE_CALL(__func__);
1346  const gchar *s;
1347  gchar *sm;
1348  gchar *value;
1349  const gchar *cs;
1350  RemminaFile *remminafile;
1351  rfContext *rfi = GET_PLUGIN_DATA(gp);
1352  rdpChannels *channels;
1353  gchar *gateway_host;
1354  gint gateway_port;
1355  gchar *datapath = NULL;
1356  gboolean status = TRUE;
1357 #ifdef GDK_WINDOWING_X11
1358  gchar *rdp_kbd_remap;
1359 #endif
1360  gint i;
1361 
1362  gint desktopOrientation, desktopScaleFactor, deviceScaleFactor;
1363 
1364  channels = rfi->instance->context->channels;
1365 
1366  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1367 
1368  datapath = g_build_path("/",
1369  remmina_plugin_service->file_get_user_datadir(),
1370  "RDP",
1371  NULL);
1372  REMMINA_PLUGIN_DEBUG("RDP data path is %s", datapath);
1373 
1374  if ((datapath != NULL) && (datapath[0] != '\0'))
1375  if (access(datapath, W_OK) == 0)
1376  freerdp_settings_set_string(rfi->settings, FreeRDP_ConfigPath, datapath);
1377  g_free(datapath);
1378 
1379  if (remmina_plugin_service->file_get_int(remminafile, "assistance_mode", 0)){
1380  rdpAssistanceFile* file = freerdp_assistance_file_new();
1381  if (!file){
1382  REMMINA_PLUGIN_DEBUG("Could not allocate assistance file structure");
1383  return FALSE;
1384  }
1385 
1386  if (remmina_plugin_service->file_get_string(remminafile, "assistance_file") == NULL ||
1387  remmina_plugin_service->file_get_string(remminafile, "assistance_pass") == NULL ){
1388 
1389  REMMINA_PLUGIN_DEBUG("Assistance file and password are not set while assistance mode is on");
1390  return FALSE;
1391  }
1392 
1393  status = freerdp_assistance_parse_file(file,
1394  remmina_plugin_service->file_get_string(remminafile, "assistance_file"),
1395  remmina_plugin_service->file_get_string(remminafile, "assistance_pass"));
1396 
1397  if (status < 0){
1398  REMMINA_PLUGIN_DEBUG("Could not parse assistance file");
1399  return FALSE;
1400  }
1401 
1402 
1403  if (!freerdp_assistance_populate_settings_from_assistance_file(file, rfi->settings))
1404  REMMINA_PLUGIN_DEBUG("Could not populate settings from assistance file");
1405  return FALSE;
1406  }
1407 
1408 
1409 #if defined(PROXY_TYPE_IGNORE)
1410  if (!remmina_plugin_service->file_get_int(remminafile, "useproxyenv", FALSE) ? TRUE : FALSE) {
1411  REMMINA_PLUGIN_DEBUG("Not using system proxy settings");
1412  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ProxyType, PROXY_TYPE_IGNORE);
1413  }
1414 #endif
1415 
1416  if (!remmina_rdp_tunnel_init(gp))
1417  return FALSE;
1418 
1419  freerdp_settings_set_bool(rfi->settings, FreeRDP_AutoReconnectionEnabled, (remmina_plugin_service->file_get_int(remminafile, "disableautoreconnect", FALSE) ? FALSE : TRUE));
1420  /* Disable RDP auto reconnection when SSH tunnel is enabled */
1421  if (remmina_plugin_service->file_get_int(remminafile, "ssh_tunnel_enabled", FALSE))
1422  freerdp_settings_set_bool(rfi->settings, FreeRDP_AutoReconnectionEnabled, FALSE);
1423 
1424  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, remmina_plugin_service->file_get_int(remminafile, "colordepth", 99));
1425 
1426  freerdp_settings_set_bool(rfi->settings, FreeRDP_SoftwareGdi, TRUE);
1427  REMMINA_PLUGIN_DEBUG("gfx_h264_available: %d", gfx_h264_available);
1428 
1429  /* Avoid using H.264 modes if they are not available on libfreerdp */
1430  if (!gfx_h264_available && (freerdp_settings_get_bool(rfi->settings, FreeRDP_ColorDepth) == 65 || freerdp_settings_get_bool(rfi->settings, FreeRDP_ColorDepth == 66)))
1431  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 64); // Fallback to GFX RFX
1432 
1433  if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth) == 0) {
1434  /* RFX (Win7)*/
1435  freerdp_settings_set_bool(rfi->settings, FreeRDP_RemoteFxCodec, TRUE);
1436  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline, FALSE);
1437  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1438  } else if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth) == 63) {
1439  /* /gfx (RFX Progressive) (Win8) */
1440  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1441  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline, TRUE);
1442  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxH264, FALSE);
1443  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxAVC444, FALSE);
1444  } else if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth) == 64) {
1445  /* /gfx:rfx (Win8) */
1446  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1447  freerdp_settings_set_bool(rfi->settings, FreeRDP_RemoteFxCodec, TRUE);
1448  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline, TRUE);
1449  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxH264, FALSE);
1450  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxAVC444, FALSE);
1451  } else if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth) == 65) {
1452  /* /gfx:avc420 (Win8.1) */
1453  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1454  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline, TRUE);
1455  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxH264, gfx_h264_available);
1456  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxAVC444, FALSE);
1457  } else if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth) == 66) {
1458  /* /gfx:avc444 (Win10) */
1459  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1460  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline, TRUE);
1461  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxH264, gfx_h264_available);
1462  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxAVC444, gfx_h264_available);
1463  } else if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth) == 99) {
1464  /* Automatic (Let the server choose its best format) */
1465  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1466  freerdp_settings_set_bool(rfi->settings, FreeRDP_RemoteFxCodec, TRUE);
1467  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline, TRUE);
1468  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxH264, gfx_h264_available);
1469  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxAVC444, gfx_h264_available);
1470  }
1471 
1472  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_RemoteFxCodec) ||
1473  freerdp_settings_get_bool(rfi->settings, FreeRDP_NSCodec) ||
1474  freerdp_settings_get_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline)) {
1475  freerdp_settings_set_bool(rfi->settings, FreeRDP_FastPathOutput, TRUE);
1476  freerdp_settings_set_bool(rfi->settings, FreeRDP_FrameMarkerCommandEnabled, TRUE);
1477  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1478  rfi->bpp = 32;
1479  }
1480 
1481  gint w = remmina_plugin_service->get_profile_remote_width(gp);
1482  gint h = remmina_plugin_service->get_profile_remote_height(gp);
1483  /* multiple of 4 */
1484  w = (w + 3) & ~0x3;
1485  h = (h + 3) & ~0x3;
1486  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopWidth, w);
1487  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopHeight, h);
1488  REMMINA_PLUGIN_DEBUG("Resolution set by the user: %dx%d", w, h);
1489 
1490  /* Workaround for FreeRDP issue #5417: in GFX AVC modes we can't go under
1491  * AVC_MIN_DESKTOP_WIDTH x AVC_MIN_DESKTOP_HEIGHT */
1492  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline) &&
1493  freerdp_settings_get_bool(rfi->settings, FreeRDP_GfxH264)) {
1494  if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth) <
1495  AVC_MIN_DESKTOP_WIDTH)
1496  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopWidth,
1497  AVC_MIN_DESKTOP_WIDTH);
1498  if (freerdp_settings_get_uint32(rfi->settings,
1499  FreeRDP_DesktopHeight) <
1500  AVC_MIN_DESKTOP_HEIGHT)
1501  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopHeight,
1502  AVC_MIN_DESKTOP_HEIGHT);
1503  }
1504 
1505  /* Workaround for FreeRDP issue #5119. This will make our horizontal resolution
1506  * an even value, but it will add a vertical black 1 pixel line on the
1507  * right of the desktop */
1508  if ((freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth) & 1) != 0) {
1509  UINT32 tmp = freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth);
1510  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopWidth, tmp - 1);
1511  }
1512 
1513  remmina_plugin_service->protocol_plugin_set_width(gp, freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth));
1514  remmina_plugin_service->protocol_plugin_set_height(gp, freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopHeight));
1515 
1516  w = freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth);
1517  h = freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopHeight);
1518  REMMINA_PLUGIN_DEBUG("Resolution set after workarounds: %dx%d", w, h);
1519 
1520 
1521  if (remmina_plugin_service->file_get_string(remminafile, "username"))
1522  freerdp_settings_set_string(rfi->settings, FreeRDP_Username, remmina_plugin_service->file_get_string(remminafile, "username"));
1523 
1524  if (remmina_plugin_service->file_get_string(remminafile, "domain"))
1525  freerdp_settings_set_string(rfi->settings, FreeRDP_Domain, remmina_plugin_service->file_get_string(remminafile, "domain"));
1526 
1527  s = remmina_plugin_service->file_get_string(remminafile, "password");
1528  if (s) freerdp_settings_set_string(rfi->settings, FreeRDP_Password, s);
1529 
1530  freerdp_settings_set_bool(rfi->settings, FreeRDP_AutoLogonEnabled, TRUE);
1531 
1536  gchar *proxy_type = g_strdup(remmina_plugin_service->file_get_string(remminafile, "proxy_type"));
1537  gchar *proxy_username = g_strdup(remmina_plugin_service->file_get_string(remminafile, "proxy_username"));
1538  gchar *proxy_password = g_strdup(remmina_plugin_service->file_get_string(remminafile, "proxy_password"));
1539  gchar *proxy_hostname = g_strdup(remmina_plugin_service->file_get_string(remminafile, "proxy_hostname"));
1540  gint proxy_port = remmina_plugin_service->file_get_int(remminafile, "proxy_port", 80);
1541  REMMINA_PLUGIN_DEBUG("proxy_type: %s", proxy_type);
1542  REMMINA_PLUGIN_DEBUG("proxy_username: %s", proxy_username);
1543  REMMINA_PLUGIN_DEBUG("proxy_password: %s", proxy_password);
1544  REMMINA_PLUGIN_DEBUG("proxy_hostname: %s", proxy_hostname);
1545  REMMINA_PLUGIN_DEBUG("proxy_port: %d", proxy_port);
1546  if (proxy_type && proxy_hostname) {
1547  if (g_strcmp0(proxy_type, "no_proxy") == 0)
1548  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ProxyType, PROXY_TYPE_IGNORE);
1549  else if (g_strcmp0(proxy_type, "http") == 0)
1550  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP);
1551  else if (g_strcmp0(proxy_type, "socks5") == 0)
1552  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ProxyType, PROXY_TYPE_SOCKS);
1553  else
1554  g_warning("Invalid proxy protocol, at the moment only no_proxy, HTTP and SOCKS5 are supported");
1555  REMMINA_PLUGIN_DEBUG("ProxyType set to: %" PRIu32, freerdp_settings_get_uint32(rfi->settings, FreeRDP_ProxyType));
1556  freerdp_settings_set_string(rfi->settings, FreeRDP_ProxyHostname, proxy_hostname);
1557  if (proxy_username)
1558  freerdp_settings_set_string(rfi->settings, FreeRDP_ProxyUsername, proxy_username);
1559  if (proxy_password)
1560  freerdp_settings_set_string(rfi->settings, FreeRDP_ProxyPassword, proxy_password);
1561  if (proxy_port)
1562  freerdp_settings_set_uint16(rfi->settings, FreeRDP_ProxyPort, proxy_port);
1563  }
1564  g_free(proxy_hostname);
1565  g_free(proxy_username);
1566  g_free(proxy_password);
1567 
1568  if (remmina_plugin_service->file_get_int(remminafile, "base-cred-for-gw", FALSE)) {
1569  // Reset gateway credentials
1570  remmina_plugin_service->file_set_string(remminafile, "gateway_username", NULL);
1571  remmina_plugin_service->file_set_string(remminafile, "gateway_domain", NULL);
1572  remmina_plugin_service->file_set_string(remminafile, "gateway_password", NULL);
1573  }
1574 
1575  /* Remote Desktop Gateway server address */
1576  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayEnabled, FALSE);
1577  s = remmina_plugin_service->file_get_string(remminafile, "gateway_server");
1578  if (s) {
1579  cs = remmina_plugin_service->file_get_string(remminafile, "gwtransp");
1580 #if FREERDP_CHECK_VERSION(2, 3, 1)
1581  if (remmina_plugin_service->file_get_int(remminafile, "websockets", FALSE))
1582  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayHttpUseWebsockets, TRUE);
1583  else
1584  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayHttpUseWebsockets, FALSE);
1585 #endif
1586  if (g_strcmp0(cs, "http") == 0) {
1587  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayRpcTransport, FALSE);
1588  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayHttpTransport, TRUE);
1589  } else if (g_strcmp0(cs, "rpc") == 0) {
1590  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayRpcTransport, TRUE);
1591  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayHttpTransport, FALSE);
1592  } else if (g_strcmp0(cs, "auto") == 0) {
1593  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayRpcTransport, TRUE);
1594  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayHttpTransport, TRUE);
1595  }
1596  remmina_plugin_service->get_server_port(s, 443, &gateway_host, &gateway_port);
1597  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayHostname, gateway_host);
1598  freerdp_settings_set_uint32(rfi->settings, FreeRDP_GatewayPort, gateway_port);
1599  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayEnabled, TRUE);
1600  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayUseSameCredentials, TRUE);
1601  }
1602  /* Remote Desktop Gateway domain */
1603  if (remmina_plugin_service->file_get_string(remminafile, "gateway_domain")) {
1604  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayDomain, remmina_plugin_service->file_get_string(remminafile, "gateway_domain"));
1605  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayUseSameCredentials, FALSE);
1606  }
1607  /* Remote Desktop Gateway username */
1608  if (remmina_plugin_service->file_get_string(remminafile, "gateway_username")) {
1609  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayUsername, remmina_plugin_service->file_get_string(remminafile, "gateway_username"));
1610  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayUseSameCredentials, FALSE);
1611  }
1612  /* Remote Desktop Gateway password */
1613  s = remmina_plugin_service->file_get_string(remminafile, "gateway_password");
1614  if (s) {
1615  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayPassword, s);
1616  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayUseSameCredentials, FALSE);
1617  }
1618  /* If no different credentials were provided for the Remote Desktop Gateway
1619  * use the same authentication credentials for the host */
1620  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_GatewayEnabled) && freerdp_settings_get_bool(rfi->settings, FreeRDP_GatewayUseSameCredentials)) {
1621  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayDomain, freerdp_settings_get_string(rfi->settings, FreeRDP_Domain));
1622  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayUsername, freerdp_settings_get_string(rfi->settings, FreeRDP_Username));
1623  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayPassword, freerdp_settings_get_string(rfi->settings, FreeRDP_Password));
1624  }
1625  /* Remote Desktop Gateway usage */
1626  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_GatewayEnabled))
1627  freerdp_set_gateway_usage_method(rfi->settings,
1628  remmina_plugin_service->file_get_int(remminafile, "gateway_usage", FALSE) ? TSC_PROXY_MODE_DETECT : TSC_PROXY_MODE_DIRECT);
1629 
1630  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayAccessToken,
1631  remmina_plugin_service->file_get_string(remminafile, "gatewayaccesstoken"));
1632 
1633  freerdp_settings_set_uint32(rfi->settings, FreeRDP_AuthenticationLevel, remmina_plugin_service->file_get_int(
1634  remminafile, "authentication level", freerdp_settings_get_uint32(rfi->settings, FreeRDP_AuthenticationLevel)));
1635 
1636  /* Certificate ignore */
1637  freerdp_settings_set_bool(rfi->settings, FreeRDP_IgnoreCertificate, remmina_plugin_service->file_get_int(remminafile, "cert_ignore", 0));
1638  freerdp_settings_set_bool(rfi->settings, FreeRDP_OldLicenseBehaviour, remmina_plugin_service->file_get_int(remminafile, "old-license", 0));
1639  freerdp_settings_set_bool(rfi->settings, FreeRDP_AllowUnanouncedOrdersFromServer, remmina_plugin_service->file_get_int(remminafile, "relax-order-checks", 0));
1640  freerdp_settings_set_uint32(rfi->settings, FreeRDP_GlyphSupportLevel, (remmina_plugin_service->file_get_int(remminafile, "glyph-cache", 0) ? GLYPH_SUPPORT_FULL : GLYPH_SUPPORT_NONE));
1641 
1642  if ((cs = remmina_plugin_service->file_get_string(remminafile, "clientname")))
1643  freerdp_settings_set_string(rfi->settings, FreeRDP_ClientHostname, cs);
1644  else
1645  freerdp_settings_set_string(rfi->settings, FreeRDP_ClientHostname, g_get_host_name());
1646 
1647  /* Client Build number is optional, if not specified defaults to 0, allow for comments to appear after number */
1648  if ((cs = remmina_plugin_service->file_get_string(remminafile, "clientbuild"))) {
1649  if (*cs) {
1650  UINT32 val = strtoul(cs, NULL, 0);
1651  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ClientBuild, val);
1652  }
1653  }
1654 
1655 
1656  if (remmina_plugin_service->file_get_string(remminafile, "loadbalanceinfo")) {
1657  char *tmp = strdup(remmina_plugin_service->file_get_string(remminafile, "loadbalanceinfo"));
1658  rfi->settings->LoadBalanceInfo = (BYTE *)tmp;
1659 
1660  freerdp_settings_set_uint32(rfi->settings, FreeRDP_LoadBalanceInfoLength, strlen(tmp));
1661  }
1662 
1663  if (remmina_plugin_service->file_get_string(remminafile, "exec"))
1664  freerdp_settings_set_string(rfi->settings, FreeRDP_AlternateShell, remmina_plugin_service->file_get_string(remminafile, "exec"));
1665 
1666  if (remmina_plugin_service->file_get_string(remminafile, "execpath"))
1667  freerdp_settings_set_string(rfi->settings, FreeRDP_ShellWorkingDirectory, remmina_plugin_service->file_get_string(remminafile, "execpath"));
1668 
1669  sm = g_strdup_printf("rdp_quality_%i", remmina_plugin_service->file_get_int(remminafile, "quality", DEFAULT_QUALITY_0));
1670  value = remmina_plugin_service->pref_get_value(sm);
1671  g_free(sm);
1672 
1673  if (value && value[0]) {
1674  freerdp_settings_set_uint32(rfi->settings, FreeRDP_PerformanceFlags, strtoul(value, NULL, 16));
1675  } else {
1676  switch (remmina_plugin_service->file_get_int(remminafile, "quality", DEFAULT_QUALITY_0)) {
1677  case 9:
1678  freerdp_settings_set_uint32(rfi->settings, FreeRDP_PerformanceFlags, DEFAULT_QUALITY_9);
1679  break;
1680 
1681  case 2:
1682  freerdp_settings_set_uint32(rfi->settings, FreeRDP_PerformanceFlags, DEFAULT_QUALITY_2);
1683  break;
1684 
1685  case 1:
1686  freerdp_settings_set_uint32(rfi->settings, FreeRDP_PerformanceFlags, DEFAULT_QUALITY_1);
1687  break;
1688 
1689  case 0:
1690  default:
1691  freerdp_settings_set_uint32(rfi->settings, FreeRDP_PerformanceFlags, DEFAULT_QUALITY_0);
1692  break;
1693  }
1694  }
1695  g_free(value);
1696 
1697  if ((cs = remmina_plugin_service->file_get_string(remminafile, "network"))) {
1698  guint32 type = 0;
1699 
1700  if (g_strcmp0(cs, "modem") == 0)
1701  type = CONNECTION_TYPE_MODEM;
1702  else if (g_strcmp0(cs, "broadband") == 0)
1703  type = CONNECTION_TYPE_BROADBAND_HIGH;
1704  else if (g_strcmp0(cs, "broadband-low") == 0)
1705  type = CONNECTION_TYPE_BROADBAND_LOW;
1706  else if (g_strcmp0(cs, "broadband-high") == 0)
1707  type = CONNECTION_TYPE_BROADBAND_HIGH;
1708  else if (g_strcmp0(cs, "wan") == 0)
1709  type = CONNECTION_TYPE_WAN;
1710  else if (g_strcmp0(cs, "lan") == 0)
1711  type = CONNECTION_TYPE_LAN;
1712  else if ((g_strcmp0(cs, "autodetect") == 0))
1713  type = CONNECTION_TYPE_AUTODETECT;
1714  else if ((g_strcmp0(cs, "none") == 0))
1715  type = REMMINA_CONNECTION_TYPE_NONE;
1716  else
1717  type = REMMINA_CONNECTION_TYPE_NONE;
1718 
1719  if (!remmina_rdp_set_connection_type(rfi->settings, type))
1720  REMMINA_PLUGIN_DEBUG("Network settings not set");
1721  }
1722 
1723  /* PerformanceFlags bitmask need also to be splitted into BOOL variables
1724  * like freerdp_settings_set_bool(rfi->settings, FreeRDP_DisableWallpaper, freerdp_settings_set_bool(rfi->settings, FreeRDP_AllowFontSmoothing…
1725  * or freerdp_get_param_bool() function will return the wrong value
1726  */
1727  freerdp_performance_flags_split(rfi->settings);
1728 
1729 #ifdef GDK_WINDOWING_X11
1730 #if FREERDP_CHECK_VERSION(2, 3, 0)
1731  rdp_kbd_remap = remmina_get_rdp_kbd_remap(remmina_plugin_service->file_get_string(remminafile, "keymap"));
1732  if (rdp_kbd_remap != NULL) {
1733  freerdp_settings_set_string(rfi->settings, FreeRDP_KeyboardRemappingList, rdp_kbd_remap);
1734  REMMINA_PLUGIN_DEBUG("rdp_keyboard_remapping_list: %s", rfi->settings->KeyboardRemappingList);
1735  g_free(rdp_kbd_remap);
1736  }
1737  else {
1738  freerdp_settings_set_string(rfi->settings, FreeRDP_KeyboardRemappingList, remmina_plugin_service->pref_get_value("rdp_kbd_remap"));
1739  REMMINA_PLUGIN_DEBUG("rdp_keyboard_remapping_list: %s", rfi->settings->KeyboardRemappingList);
1740  }
1741 #endif
1742 #endif
1743 
1744  freerdp_settings_set_uint32(rfi->settings, FreeRDP_KeyboardLayout, remmina_rdp_settings_get_keyboard_layout());
1745 
1746  if (remmina_plugin_service->file_get_int(remminafile, "console", FALSE))
1747  freerdp_settings_set_bool(rfi->settings, FreeRDP_ConsoleSession, TRUE);
1748 
1749  if (remmina_plugin_service->file_get_int(remminafile, "restricted-admin", FALSE)) {
1750  freerdp_settings_set_bool(rfi->settings, FreeRDP_ConsoleSession, TRUE);
1751  freerdp_settings_set_bool(rfi->settings, FreeRDP_RestrictedAdminModeRequired, TRUE);
1752  }
1753 
1754  if (remmina_plugin_service->file_get_string(remminafile, "pth")) {
1755  freerdp_settings_set_bool(rfi->settings, FreeRDP_ConsoleSession, TRUE);
1756  freerdp_settings_set_bool(rfi->settings, FreeRDP_RestrictedAdminModeRequired, TRUE);
1757  freerdp_settings_set_string(rfi->settings, FreeRDP_PasswordHash, remmina_plugin_service->file_get_string(remminafile, "pth"));
1758  remmina_plugin_service->file_set_int(remminafile, "restricted-admin", TRUE);
1759  }
1760 
1761  cs = remmina_plugin_service->file_get_string(remminafile, "security");
1762  if (g_strcmp0(cs, "rdp") == 0) {
1763  freerdp_settings_set_bool(rfi->settings, FreeRDP_RdpSecurity, TRUE);
1764  freerdp_settings_set_bool(rfi->settings, FreeRDP_TlsSecurity, FALSE);
1765  freerdp_settings_set_bool(rfi->settings, FreeRDP_NlaSecurity, FALSE);
1766  freerdp_settings_set_bool(rfi->settings, FreeRDP_ExtSecurity, FALSE);
1767  freerdp_settings_set_bool(rfi->settings, FreeRDP_UseRdpSecurityLayer, TRUE);
1768  } else if (g_strcmp0(cs, "tls") == 0) {
1769  freerdp_settings_set_bool(rfi->settings, FreeRDP_RdpSecurity, FALSE);
1770  freerdp_settings_set_bool(rfi->settings, FreeRDP_TlsSecurity, TRUE);
1771  freerdp_settings_set_bool(rfi->settings, FreeRDP_NlaSecurity, FALSE);
1772  freerdp_settings_set_bool(rfi->settings, FreeRDP_ExtSecurity, FALSE);
1773  } else if (g_strcmp0(cs, "nla") == 0) {
1774  freerdp_settings_set_bool(rfi->settings, FreeRDP_RdpSecurity, FALSE);
1775  freerdp_settings_set_bool(rfi->settings, FreeRDP_TlsSecurity, FALSE);
1776  freerdp_settings_set_bool(rfi->settings, FreeRDP_NlaSecurity, TRUE);
1777  freerdp_settings_set_bool(rfi->settings, FreeRDP_ExtSecurity, FALSE);
1778  } else if (g_strcmp0(cs, "ext") == 0) {
1779  freerdp_settings_set_bool(rfi->settings, FreeRDP_RdpSecurity, FALSE);
1780  freerdp_settings_set_bool(rfi->settings, FreeRDP_TlsSecurity, FALSE);
1781  freerdp_settings_set_bool(rfi->settings, FreeRDP_NlaSecurity, FALSE);
1782  freerdp_settings_set_bool(rfi->settings, FreeRDP_ExtSecurity, TRUE);
1783  } else {
1784  /* This is "-nego" switch of xfreerdp */
1785  freerdp_settings_set_bool(rfi->settings, FreeRDP_NegotiateSecurityLayer, TRUE);
1786  }
1787 
1788  cs = remmina_plugin_service->file_get_string(remminafile, "tls-seclevel");
1789  if (cs && g_strcmp0(cs,"")!=0) {
1790  i = atoi(cs);
1791  freerdp_settings_set_uint32(rfi->settings, FreeRDP_TlsSecLevel, i);
1792  }
1793 
1794  freerdp_settings_set_bool(rfi->settings, FreeRDP_CompressionEnabled, TRUE);
1795  if (remmina_plugin_service->file_get_int(remminafile, "disable_fastpath", FALSE)) {
1796  freerdp_settings_set_bool(rfi->settings, FreeRDP_FastPathInput, FALSE);
1797  freerdp_settings_set_bool(rfi->settings, FreeRDP_FastPathOutput, FALSE);
1798  } else {
1799  freerdp_settings_set_bool(rfi->settings, FreeRDP_FastPathInput, TRUE);
1800  freerdp_settings_set_bool(rfi->settings, FreeRDP_FastPathOutput, TRUE);
1801  }
1802 
1803  /* Orientation and scaling settings */
1804  remmina_rdp_settings_get_orientation_scale_prefs(&desktopOrientation, &desktopScaleFactor, &deviceScaleFactor);
1805 
1806  freerdp_settings_set_uint16(rfi->settings, FreeRDP_DesktopOrientation, desktopOrientation);
1807  if (desktopScaleFactor != 0 && deviceScaleFactor != 0) {
1808  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopScaleFactor, desktopScaleFactor);
1809  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DeviceScaleFactor, deviceScaleFactor);
1810  }
1811 
1812  /* Try to enable "Display Control Virtual Channel Extension", needed to
1813  * dynamically resize remote desktop. This will automatically open
1814  * the "disp" dynamic channel, if available */
1815  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportDisplayControl, TRUE);
1816 
1817  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_SupportDisplayControl)) {
1818  CLPARAM *d[1];
1819  int dcount;
1820 
1821  dcount = 1;
1822  d[0] = "disp";
1823  freerdp_client_add_dynamic_channel(rfi->settings, dcount, d);
1824  }
1825 
1826  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline)) {
1827  CLPARAM *d[1];
1828 
1829  int dcount;
1830 
1831  dcount = 1;
1832  d[0] = "rdpgfx";
1833  freerdp_client_add_dynamic_channel(rfi->settings, dcount, d);
1834  }
1835 
1836  /* Sound settings */
1837  cs = remmina_plugin_service->file_get_string(remminafile, "sound");
1838  if (g_strcmp0(cs, "remote") == 0) {
1839  freerdp_settings_set_bool(rfi->settings, FreeRDP_RemoteConsoleAudio, TRUE);
1840  } else if (g_str_has_prefix(cs, "local")) {
1841  freerdp_settings_set_bool(rfi->settings, FreeRDP_AudioPlayback, TRUE);
1842  freerdp_settings_set_bool(rfi->settings, FreeRDP_AudioCapture, TRUE);
1843  } else {
1844  /* Disable sound */
1845  freerdp_settings_set_bool(rfi->settings, FreeRDP_AudioPlayback, FALSE);
1846  freerdp_settings_set_bool(rfi->settings, FreeRDP_RemoteConsoleAudio, FALSE);
1847  }
1848 
1849  cs = remmina_plugin_service->file_get_string(remminafile, "microphone");
1850  if (cs != NULL && cs[0] != '\0') {
1851  if (g_strcmp0(cs, "0") == 0) {
1852  REMMINA_PLUGIN_DEBUG("“microphone” was set to 0, setting to \"\"");
1853  remmina_plugin_service->file_set_string(remminafile, "microphone", "");
1854  } else {
1855  freerdp_settings_set_bool(rfi->settings, FreeRDP_AudioCapture, TRUE);
1856  REMMINA_PLUGIN_DEBUG("“microphone” set to “%s”", cs);
1857  CLPARAM **p;
1858  size_t count;
1859 
1860  p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("audin", g_strdup(cs), &count);
1861 
1862  freerdp_client_add_dynamic_channel(rfi->settings, count, p);
1863  g_free(p);
1864  }
1865  }
1866 
1867  cs = remmina_plugin_service->file_get_string(remminafile, "audio-output");
1868  if (cs != NULL && cs[0] != '\0') {
1869  REMMINA_PLUGIN_DEBUG("audio output set to %s", cs);
1870  CLPARAM **p;
1871  size_t count;
1872 
1873  p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("rdpsnd", g_strdup(cs), &count);
1874  status = freerdp_client_add_static_channel(rfi->settings, count, p);
1875  if (status)
1876  status = freerdp_client_add_dynamic_channel(rfi->settings, count, p);
1877  g_free(p);
1878  }
1879 
1880 
1881  cs = remmina_plugin_service->file_get_string(remminafile, "freerdp_log_level");
1882  if (cs != NULL && cs[0] != '\0')
1883  REMMINA_PLUGIN_DEBUG("Log level set to to %s", cs);
1884  else
1885  cs = g_strdup("INFO");
1886  wLog *root = WLog_GetRoot();
1887  WLog_SetStringLogLevel(root, cs);
1888 
1889  cs = remmina_plugin_service->file_get_string(remminafile, "freerdp_log_filters");
1890  if (cs != NULL && cs[0] != '\0') {
1891  REMMINA_PLUGIN_DEBUG("Log filters set to to %s", cs);
1892  WLog_AddStringLogFilters(cs);
1893  } else {
1894  WLog_AddStringLogFilters(NULL);
1895  }
1896 
1897 
1898  cs = remmina_plugin_service->file_get_string(remminafile, "usb");
1899  if (cs != NULL && cs[0] != '\0') {
1900  CLPARAM **p;
1901  size_t count;
1902  p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("urbdrc", g_strdup(cs), &count);
1903  freerdp_client_add_dynamic_channel(rfi->settings, count, p);
1904  g_free(p);
1905  }
1906 
1907  cs = remmina_plugin_service->file_get_string(remminafile, "vc");
1908  if (cs != NULL && cs[0] != '\0') {
1909  CLPARAM **p;
1910  size_t count;
1911  p = remmina_rdp_CommandLineParseCommaSeparatedValues(g_strdup(cs), &count);
1912  freerdp_client_add_static_channel(rfi->settings, count, p);
1913  g_free(p);
1914  }
1915 
1916  cs = remmina_plugin_service->file_get_string(remminafile, "dvc");
1917  if (cs != NULL && cs[0] != '\0') {
1918  CLPARAM **p;
1919  size_t count;
1920  p = remmina_rdp_CommandLineParseCommaSeparatedValues(g_strdup(cs), &count);
1921  freerdp_client_add_dynamic_channel(rfi->settings, count, p);
1922  g_free(p);
1923  }
1924 
1925  cs = remmina_plugin_service->file_get_string(remminafile, "rdp2tcp");
1926  if (cs != NULL && cs[0] != '\0') {
1927  g_free(rfi->settings->RDP2TCPArgs);
1928  rfi->settings->RDP2TCPArgs = g_strdup(cs);
1929  REMMINA_PLUGIN_DEBUG("rdp2tcp set to %s", rfi->settings->RDP2TCPArgs);
1930  remmina_rdp_load_static_channel_addin(channels, rfi->settings, "rdp2tcp", rfi->settings->RDP2TCPArgs);
1931  }
1932 
1933  int vermaj, vermin, verrev;
1934  freerdp_get_version(&vermaj, &vermin, &verrev);
1935 
1936 #if FREERDP_CHECK_VERSION(2, 1, 0)
1937  cs = remmina_plugin_service->file_get_string(remminafile, "timeout");
1938  if (cs != NULL && cs[0] != '\0') {
1939  const gchar *endptr = NULL;
1940  guint64 val = g_ascii_strtoull(cs, (gchar **)&endptr, 10);
1941  if (val > 600000 || val <= 0)
1942  val = 600000;
1943  freerdp_settings_set_uint32(rfi->settings, FreeRDP_TcpAckTimeout, (UINT32)val);
1944  }
1945 #endif
1946 
1947  if (remmina_plugin_service->file_get_int(remminafile, "preferipv6", FALSE) ? TRUE : FALSE)
1948  freerdp_settings_set_bool(rfi->settings, FreeRDP_PreferIPv6OverIPv4, TRUE);
1949 
1950  freerdp_settings_set_bool(rfi->settings, FreeRDP_RedirectClipboard, remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE) ? FALSE : TRUE);
1951 
1952  cs = remmina_plugin_service->file_get_string(remminafile, "sharefolder");
1953  if (cs != NULL && cs[0] != '\0') {
1954  gchar *ccs = g_strdup(cs);
1955  REMMINA_PLUGIN_DEBUG("[Deprecated->migrating] - Old sharefolder %s to \"drive \"", ccs);
1956  if (!remmina_plugin_service->file_get_string(remminafile, "drive")) {
1957  remmina_plugin_service->file_set_string(remminafile, "drive", g_strdup(ccs));
1958  remmina_plugin_service->file_set_string(remminafile, "sharefolder", NULL);
1959  REMMINA_PLUGIN_DEBUG("[Deprecated->migrated] - drive set to %s", g_strdup(ccs));
1960  }
1961  g_free(ccs);
1962  //CLPARAM **p;
1963  //size_t count;
1964  //p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("drive", g_strdup(cs), &count);
1965  //status = freerdp_client_add_device_channel(rfi->settings, count, p);
1966  //g_free(p);
1967  }
1968  cs = remmina_plugin_service->file_get_string(remminafile, "drive");
1969  if (cs != NULL && cs[0] != '\0') {
1970  REMMINA_PLUGIN_DEBUG("Redirect directory set to %s", cs);
1971  CLPARAM **p;
1972  size_t count;
1973 
1974  gchar **folders = g_strsplit(cs, ";", -1);
1975  for (i = 0; folders[i] != NULL; i++) {
1976  REMMINA_PLUGIN_DEBUG("Parsing folder %s", folders[i]);
1977  p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("drive", g_strdup(folders[i]), &count);
1978  status = freerdp_client_add_device_channel(rfi->settings, count, p);
1979  g_free(p);
1980  }
1981  g_strfreev(folders);
1982  }
1983 
1984  if (remmina_plugin_service->file_get_int(remminafile, "shareprinter", FALSE)) {
1985 #ifdef HAVE_CUPS
1986  REMMINA_PLUGIN_DEBUG("Sharing printers");
1987  const gchar *po = remmina_plugin_service->file_get_string(remminafile, "printer_overrides");
1988  if (po && po[0] != 0) {
1989  /* Fallback to remmina code to override print drivers */
1990  if (cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, 0, 0, remmina_rdp_set_printers, rfi))
1991  REMMINA_PLUGIN_DEBUG("All printers have been shared");
1992  else
1993  REMMINA_PLUGIN_DEBUG("Cannot share printers, are there any available?");
1994  } else {
1995  /* Use libfreerdp code to map all printers */
1996  CLPARAM *d[1];
1997  int dcount;
1998  dcount = 1;
1999  d[0] = "printer";
2000  freerdp_client_add_device_channel(rfi->settings, dcount, d);
2001  }
2002 #endif /* HAVE_CUPS */
2003  }
2004 
2005  if (remmina_plugin_service->file_get_int(remminafile, "span", FALSE)) {
2006  freerdp_settings_set_bool(rfi->settings, FreeRDP_SpanMonitors, TRUE);
2007  freerdp_settings_set_bool(rfi->settings, FreeRDP_UseMultimon, TRUE);
2008  freerdp_settings_set_bool(rfi->settings, FreeRDP_Fullscreen, TRUE);
2009  remmina_plugin_service->file_set_int(remminafile, "multimon", 1);
2010  }
2011 
2012  if (remmina_plugin_service->file_get_int(remminafile, "multimon", FALSE)) {
2013  guint32 maxwidth = 0;
2014  guint32 maxheight = 0;
2015  gchar *monitorids;
2016  guint32 i;
2017  freerdp_settings_set_bool(rfi->settings, FreeRDP_UseMultimon, TRUE);
2018  /* TODO Add an option for this */
2019  freerdp_settings_set_bool(rfi->settings, FreeRDP_ForceMultimon, TRUE);
2020  freerdp_settings_set_bool(rfi->settings, FreeRDP_Fullscreen, TRUE);
2021 
2022  gchar *monitorids_string = g_strdup(remmina_plugin_service->file_get_string(remminafile, "monitorids"));
2023  /* Otherwise we get all the attached monitors
2024  * monitorids may contains desktop orientation values.
2025  * But before we check if there are orientation attributes
2026  */
2027  if (monitorids_string != NULL && monitorids_string[0] != '\0') {
2028  if (g_strstr_len(monitorids_string, -1, ",") != NULL) {
2029  if (g_strstr_len(monitorids_string, -1, ":") != NULL) {
2030  rdpMonitor *base = (rdpMonitor *)freerdp_settings_get_pointer(rfi->settings, FreeRDP_MonitorDefArray);
2031  /* We have an ID and an orientation degree */
2032  gchar **temp_items;
2033  gchar **rot_items;
2034  temp_items = g_strsplit(monitorids_string, ",", -1);
2035  for (i = 0; i < g_strv_length(temp_items); i++) {
2036  rot_items = g_strsplit(temp_items[i], ":", -1);
2037  rdpMonitor *current = &base[atoi(rot_items[0])];
2038  if (i == 0)
2039  monitorids = g_strdup(rot_items[0]);
2040  else
2041  monitorids = g_strdup_printf("%s,%s", monitorids, rot_items[0]);
2042  current->attributes.orientation = atoi(rot_items[1]);
2043  REMMINA_PLUGIN_DEBUG("Monitor n %d orientation: %d", i, current->attributes.orientation);
2044  }
2045  } else {
2046  monitorids = g_strdup(monitorids_string);
2047  }
2048  } else {
2049  monitorids = g_strdup(monitorids_string);
2050  }
2051  } else {
2052  monitorids = g_strdup(monitorids_string);
2053  }
2054  remmina_rdp_monitor_get(rfi, &monitorids, &maxwidth, &maxheight);
2055  if (monitorids != NULL && monitorids[0] != '\0') {
2056  UINT32 *base = (UINT32 *)freerdp_settings_get_pointer(rfi->settings, FreeRDP_MonitorIds);
2057  gchar **items;
2058  items = g_strsplit(monitorids, ",", -1);
2059  freerdp_settings_set_uint32(rfi->settings, FreeRDP_NumMonitorIds, g_strv_length(items));
2060  REMMINA_PLUGIN_DEBUG("NumMonitorIds: %d", freerdp_settings_get_uint32(rfi->settings, FreeRDP_NumMonitorIds));
2061  for (i = 0; i < g_strv_length(items); i++) {
2062  UINT32 *current = &base[i];
2063  *current = atoi(items[i]);
2064  REMMINA_PLUGIN_DEBUG("Added monitor with ID %" PRIu32, *current);
2065  }
2066  g_free(monitorids);
2067  g_strfreev(items);
2068  }
2069  if (maxwidth && maxheight) {
2070  REMMINA_PLUGIN_DEBUG("Setting DesktopWidth and DesktopHeight to: %dx%d", maxwidth, maxheight);
2071  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopWidth, maxwidth);
2072  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopHeight, maxheight);
2073  REMMINA_PLUGIN_DEBUG("DesktopWidth and DesktopHeight set to: %dx%d", freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth), freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopHeight));
2074  } else {
2075  REMMINA_PLUGIN_DEBUG("Cannot set Desktop Size, we are using the previously set values: %dx%d", freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth), freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopHeight));
2076  }
2077  remmina_plugin_service->protocol_plugin_set_width(gp, freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth));
2078  remmina_plugin_service->protocol_plugin_set_height(gp, freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopHeight));
2079  }
2080 
2081  const gchar *sn = remmina_plugin_service->file_get_string(remminafile, "smartcardname");
2082  if (remmina_plugin_service->file_get_int(remminafile, "sharesmartcard", FALSE) ||
2083  (sn != NULL && sn[0] != '\0')) {
2084  RDPDR_SMARTCARD *smartcard;
2085  smartcard = (RDPDR_SMARTCARD *)calloc(1, sizeof(RDPDR_SMARTCARD));
2086 
2087 #ifdef WITH_FREERDP3
2088  RDPDR_DEVICE *sdev;
2089  sdev = &(smartcard->device);
2090 #else
2091  RDPDR_SMARTCARD *sdev;
2092  sdev = smartcard;
2093 #endif
2094 
2095  sdev->Type = RDPDR_DTYP_SMARTCARD;
2096 
2097  freerdp_settings_set_bool(rfi->settings, FreeRDP_DeviceRedirection, TRUE);
2098 
2099  if (sn != NULL && sn[0] != '\0')
2100  sdev->Name = _strdup(sn);
2101 
2102  freerdp_settings_set_bool(rfi->settings, FreeRDP_RedirectSmartCards, TRUE);
2103 
2104  freerdp_device_collection_add(rfi->settings, (RDPDR_DEVICE *)smartcard);
2105  }
2106 
2107  if (remmina_plugin_service->file_get_int(remminafile, "passwordispin", FALSE))
2108  /* Option works only combined with Username and Domain, because FreeRDP
2109  * doesn’t know anything about info on smart card */
2110  freerdp_settings_set_bool(rfi->settings, FreeRDP_PasswordIsSmartcardPin, TRUE);
2111 
2112  /* /serial[:<name>[,<path>[,<driver>[,permissive]]]] */
2113  if (remmina_plugin_service->file_get_int(remminafile, "shareserial", FALSE)) {
2114  RDPDR_SERIAL *serial;
2115  serial = (RDPDR_SERIAL *)calloc(1, sizeof(RDPDR_SERIAL));
2116 
2117 #ifdef WITH_FREERDP3
2118  RDPDR_DEVICE *sdev;
2119  sdev = &(serial->device);
2120 #else
2121  RDPDR_SERIAL *sdev;
2122  sdev = serial;
2123 #endif
2124 
2125  sdev->Type = RDPDR_DTYP_SERIAL;
2126 
2127  freerdp_settings_set_bool(rfi->settings, FreeRDP_DeviceRedirection, TRUE);
2128 
2129  const gchar *sn = remmina_plugin_service->file_get_string(remminafile, "serialname");
2130  if (sn != NULL && sn[0] != '\0')
2131  sdev->Name = _strdup(sn);
2132 
2133  const gchar *sd = remmina_plugin_service->file_get_string(remminafile, "serialdriver");
2134  if (sd != NULL && sd[0] != '\0')
2135  serial->Driver = _strdup(sd);
2136 
2137  const gchar *sp = remmina_plugin_service->file_get_string(remminafile, "serialpath");
2138  if (sp != NULL && sp[0] != '\0')
2139  serial->Path = _strdup(sp);
2140 
2141  if (remmina_plugin_service->file_get_int(remminafile, "serialpermissive", FALSE))
2142  serial->Permissive = _strdup("permissive");
2143 
2144  freerdp_settings_set_bool(rfi->settings, FreeRDP_RedirectSerialPorts, TRUE);
2145 
2146  freerdp_device_collection_add(rfi->settings, (RDPDR_DEVICE *)serial);
2147  }
2148 
2149  if (remmina_plugin_service->file_get_int(remminafile, "shareparallel", FALSE)) {
2150  RDPDR_PARALLEL *parallel;
2151  parallel = (RDPDR_PARALLEL *)calloc(1, sizeof(RDPDR_PARALLEL));
2152 
2153 #ifdef WITH_FREERDP3
2154  RDPDR_DEVICE *pdev;
2155  pdev = &(parallel->device);
2156 #else
2157  RDPDR_PARALLEL *pdev;
2158  pdev = parallel;
2159 #endif
2160 
2161  pdev->Type = RDPDR_DTYP_PARALLEL;
2162 
2163  freerdp_settings_set_bool(rfi->settings, FreeRDP_DeviceRedirection, TRUE);
2164 
2165  freerdp_settings_set_bool(rfi->settings, FreeRDP_RedirectParallelPorts, TRUE);
2166 
2167  const gchar *pn = remmina_plugin_service->file_get_string(remminafile, "parallelname");
2168  if (pn != NULL && pn[0] != '\0')
2169  pdev->Name = _strdup(pn);
2170  const gchar *dp = remmina_plugin_service->file_get_string(remminafile, "parallelpath");
2171  if (dp != NULL && dp[0] != '\0')
2172  parallel->Path = _strdup(dp);
2173 
2174  freerdp_device_collection_add(rfi->settings, (RDPDR_DEVICE *)parallel);
2175  }
2176 
2180  if (remmina_plugin_service->file_get_int(remminafile, "multitransport", FALSE)) {
2181  freerdp_settings_set_bool(rfi->settings, FreeRDP_DeviceRedirection, TRUE);
2182  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportMultitransport, TRUE);
2183  freerdp_settings_set_uint32(rfi->settings, FreeRDP_MultitransportFlags,
2184  (TRANSPORT_TYPE_UDP_FECR | TRANSPORT_TYPE_UDP_FECL | TRANSPORT_TYPE_UDP_PREFERRED));
2185  } else {
2186  freerdp_settings_set_uint32(rfi->settings, FreeRDP_MultitransportFlags, 0);
2187  }
2188 
2189  /* If needed, force interactive authentication by deleting all authentication fields,
2190  * forcing libfreerdp to call our callbacks for authentication.
2191  * This usually happens from a second attempt of connection, never on the 1st one. */
2193  freerdp_settings_set_string(rfi->settings, FreeRDP_Username, NULL);
2194  freerdp_settings_set_string(rfi->settings, FreeRDP_Password, NULL);
2195  freerdp_settings_set_string(rfi->settings, FreeRDP_Domain, NULL);
2196 
2197  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayDomain, NULL);
2198  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayUsername, NULL);
2199  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayPassword, NULL);
2200 
2201  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayUseSameCredentials, FALSE);
2202  }
2203 
2204  gboolean orphaned;
2205 
2206  if (!freerdp_connect(rfi->instance)) {
2207  orphaned = (GET_PLUGIN_DATA(rfi->protocol_widget) == NULL);
2208  if (!orphaned) {
2209  UINT32 e;
2210 
2211  e = freerdp_get_last_error(rfi->instance->context);
2212 
2213  switch (e) {
2214  case FREERDP_ERROR_AUTHENTICATION_FAILED:
2215  case STATUS_LOGON_FAILURE: // wrong return code from FreeRDP introduced at the end of July 2016? (fixed with b86c0ba)
2216 #ifdef FREERDP_ERROR_CONNECT_LOGON_FAILURE
2217  case FREERDP_ERROR_CONNECT_LOGON_FAILURE:
2218 #endif
2219  /* Logon failure, will retry with interactive authentication */
2221  break;
2222  case STATUS_ACCOUNT_LOCKED_OUT:
2223 #ifdef FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT
2224  case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT:
2225 #endif
2226  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nAccount locked out."),
2227  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2228  break;
2229  case STATUS_ACCOUNT_EXPIRED:
2230 #ifdef FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED
2231  case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED:
2232 #endif
2233  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nAccount expired."),
2234  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2235  break;
2236  case STATUS_PASSWORD_EXPIRED:
2237 #ifdef FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED
2238  case FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED:
2239 #endif
2240  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nPassword expired."),
2241  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2242  break;
2243  case STATUS_ACCOUNT_DISABLED:
2244 #ifdef FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED
2245  case FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED:
2246 #endif
2247  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nAccount disabled."),
2248  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2249  break;
2250 #ifdef FREERDP_ERROR_SERVER_INSUFFICIENT_PRIVILEGES
2251  /* https://msdn.microsoft.com/en-us/library/ee392247.aspx */
2252  case FREERDP_ERROR_SERVER_INSUFFICIENT_PRIVILEGES:
2253  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nInsufficient user privileges."),
2254  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2255  break;
2256 #endif
2257  case STATUS_ACCOUNT_RESTRICTION:
2258 #ifdef FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION
2259  case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION:
2260 #endif
2261  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nAccount restricted."),
2262  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2263  break;
2264 
2265  case STATUS_PASSWORD_MUST_CHANGE:
2266 #ifdef FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE
2267  case FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE:
2268 #endif
2269  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nChange user password before connecting."),
2270  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2271  break;
2272 
2273  case FREERDP_ERROR_CONNECT_FAILED:
2274  remmina_plugin_service->protocol_plugin_set_error(gp, _("Lost connection to the RDP server “%s”."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2275  break;
2276  case FREERDP_ERROR_DNS_NAME_NOT_FOUND:
2277  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not find the address for the RDP server “%s”."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2278  break;
2279  case FREERDP_ERROR_TLS_CONNECT_FAILED:
2280  remmina_plugin_service->protocol_plugin_set_error(gp,
2281  _("Could not connect to the RDP server “%s” via TLS. See the DEBUG traces from a terminal for more information."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2282  break;
2283  case FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED:
2284  // TRANSLATORS: the placeholder may be either an IP/FQDN or a server hostname
2285  remmina_plugin_service->protocol_plugin_set_error(gp, _("Unable to establish a connection to the RDP server “%s”. Check “Security protocol negotiation”."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2286  break;
2287 #ifdef FREERDP_ERROR_POST_CONNECT_FAILED
2288  case FREERDP_ERROR_POST_CONNECT_FAILED:
2289  /* remmina_rdp_post_connect() returned FALSE to libfreerdp. We saved the error on rfi->postconnect_error */
2290  switch (rfi->postconnect_error) {
2291  case REMMINA_POSTCONNECT_ERROR_OK:
2292  /* We should never come here */
2293  remmina_plugin_service->protocol_plugin_set_error(gp, _("Cannot connect to the RDP server “%s”."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2294  break;
2295  case REMMINA_POSTCONNECT_ERROR_GDI_INIT:
2296  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not start libfreerdp-gdi."));
2297  break;
2298  case REMMINA_POSTCONNECT_ERROR_NO_H264:
2299  remmina_plugin_service->protocol_plugin_set_error(gp, _("You requested a H.264 GFX mode for the server “%s”, but your libfreerdp does not support H.264. Please use a non-AVC colour depth setting."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2300  break;
2301  }
2302  break;
2303 #endif
2304 #ifdef FREERDP_ERROR_SERVER_DENIED_CONNECTION
2305  case FREERDP_ERROR_SERVER_DENIED_CONNECTION:
2306  remmina_plugin_service->protocol_plugin_set_error(gp, _("The “%s” server refused the connection."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2307  break;
2308 #endif
2309  case 0x800759DB:
2310  // E_PROXY_NAP_ACCESSDENIED https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tsgu/84cd92e4-592c-4219-95d8-18021ac654b0
2311  remmina_plugin_service->protocol_plugin_set_error(gp, _("The Remote Desktop Gateway “%s” denied the user “%s\\%s” access due to policy."),
2312  freerdp_settings_get_string(rfi->settings, FreeRDP_GatewayHostname), freerdp_settings_get_string(rfi->settings, FreeRDP_GatewayDomain), freerdp_settings_get_string(rfi->settings, FreeRDP_GatewayUsername));
2313  break;
2314 
2315  case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS:
2316  rfi->user_cancelled = TRUE;
2317  break;
2318 
2319  default:
2320  g_printf("libfreerdp returned code is %08X\n", e);
2321  remmina_plugin_service->protocol_plugin_set_error(gp, _("Cannot connect to the “%s” RDP server."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2322  break;
2323  }
2324  }
2325 
2326  return FALSE;
2327  }
2328 
2329  if (GET_PLUGIN_DATA(rfi->protocol_widget) == NULL) orphaned = true; else orphaned = false;
2330  if (!orphaned && freerdp_get_last_error(rfi->instance->context) == FREERDP_ERROR_SUCCESS && !rfi->user_cancelled)
2332 
2333  return TRUE;
2334 }
2335 
2336 static void rfi_uninit(rfContext *rfi)
2337 {
2338  freerdp *instance;
2339 
2340  instance = rfi->instance;
2341 
2342  if (rfi->remmina_plugin_thread) {
2343  rfi->thread_cancelled = TRUE; // Avoid all rf_queue function to run
2344  pthread_cancel(rfi->remmina_plugin_thread);
2345  if (rfi->remmina_plugin_thread)
2346  pthread_join(rfi->remmina_plugin_thread, NULL);
2347  }
2348 
2349  if (instance) {
2350  if (rfi->connected) {
2351  freerdp_abort_connect(instance);
2352  rfi->connected = false;
2353  }
2354  }
2355 
2356  if (instance) {
2357  RDP_CLIENT_ENTRY_POINTS *pEntryPoints = instance->pClientEntryPoints;
2358  if (pEntryPoints)
2359  IFCALL(pEntryPoints->GlobalUninit);
2360  free(instance->pClientEntryPoints);
2361  freerdp_context_free(instance); /* context is rfContext* rfi */
2362  freerdp_free(instance); /* This implicitly frees instance->context and rfi is no longer valid */
2363  }
2364 }
2365 
2366 static gboolean complete_cleanup_on_main_thread(gpointer data)
2367 {
2368  TRACE_CALL(__func__);
2369 
2370  gboolean orphaned;
2371  rfContext *rfi = (rfContext *)data;
2373 
2375 
2376  gdi_free(rfi->instance);
2377 
2378  gp = rfi->protocol_widget;
2379  if (GET_PLUGIN_DATA(gp) == NULL) orphaned = true; else orphaned = false;
2380 
2382  if (!orphaned) remmina_rdp_event_uninit(gp);
2383 
2384  if (!orphaned) g_object_steal_data(G_OBJECT(gp), "plugin-data");
2385 
2386  rfi_uninit(rfi);
2387 
2388  /* Notify the RemminaProtocolWidget that we closed our connection, and the GUI interface
2389  * can be removed */
2390  if (!orphaned)
2391  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
2392 
2393  return G_SOURCE_REMOVE;
2394 }
2395 
2396 static gpointer remmina_rdp_main_thread(gpointer data)
2397 {
2398  TRACE_CALL(__func__);
2400  rfContext *rfi;
2401 
2402  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
2403  CANCEL_ASYNC
2404 
2405  gp = (RemminaProtocolWidget *)data;
2406 
2407  rfi = GET_PLUGIN_DATA(gp);
2408 
2410  do
2411  remmina_rdp_main(gp);
2412  while (!remmina_plugin_service->protocol_plugin_has_error(gp) && rfi->attempt_interactive_authentication == TRUE && !rfi->user_cancelled);
2413 
2414  rfi->remmina_plugin_thread = 0;
2415 
2416  /* cleanup */
2417  g_idle_add(complete_cleanup_on_main_thread, (gpointer)rfi);
2418 
2419 
2420  return NULL;
2421 }
2422 
2424 {
2425  TRACE_CALL(__func__);
2426  freerdp *instance;
2427  rfContext *rfi;
2428 
2429  instance = freerdp_new();
2430  instance->PreConnect = remmina_rdp_pre_connect;
2431  instance->PostConnect = remmina_rdp_post_connect;
2432  instance->PostDisconnect = remmina_rdp_post_disconnect;
2433  instance->Authenticate = remmina_rdp_authenticate;
2434  instance->GatewayAuthenticate = remmina_rdp_gw_authenticate;
2435  //instance->VerifyCertificate = remmina_rdp_verify_certificate;
2436  instance->VerifyCertificateEx = remmina_rdp_verify_certificate_ex;
2437  //instance->VerifyChangedCertificate = remmina_rdp_verify_changed_certificate;
2438  instance->VerifyChangedCertificateEx = remmina_rdp_verify_changed_certificate_ex;
2439 
2440  instance->ContextSize = sizeof(rfContext);
2441  freerdp_context_new(instance);
2442  rfi = (rfContext *)instance->context;
2443 
2444  g_object_set_data_full(G_OBJECT(gp), "plugin-data", rfi, free);
2445 
2446  rfi->protocol_widget = gp;
2447  rfi->instance = instance;
2448  rfi->settings = instance->settings;
2449  rfi->connected = false;
2450  rfi->is_reconnecting = false;
2451  rfi->stop_reconnecting_requested = false;
2452  rfi->user_cancelled = FALSE;
2453 
2454  freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0);
2455 
2457 }
2458 
2460 {
2461  TRACE_CALL(__func__);
2462  rfContext *rfi = GET_PLUGIN_DATA(gp);
2463  RemminaFile *remminafile;
2464  const gchar *profile_name, *p;
2465  gchar thname[16], c;
2466  gint nthname = 0;
2467 
2468  rfi->scale = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
2469 
2470  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
2471 
2472 
2473  if (pthread_create(&rfi->remmina_plugin_thread, NULL, remmina_rdp_main_thread, gp)) {
2474  remmina_plugin_service->protocol_plugin_set_error(gp, "%s",
2475  "Could not start pthread.");
2476 
2477  rfi->remmina_plugin_thread = 0;
2478 
2479  return FALSE;
2480  }
2481 
2482  /* Generate a thread name to be used with pthread_setname_np() for debugging */
2483  profile_name = remmina_plugin_service->file_get_string(remminafile, "name");
2484  p = profile_name;
2485  strcpy(thname, "RemmRDP:");
2486  if (p) {
2487  nthname = strlen(thname);
2488  while ((c = *p) != 0 && nthname < sizeof(thname) - 1) {
2489  if (isalnum(c))
2490  thname[nthname++] = c;
2491  p++;
2492  }
2493  } else {
2494  strcat(thname, "<NONAM>");
2495  nthname = strlen(thname);
2496  }
2497  thname[nthname] = 0;
2498 #if defined(__linux__)
2499  pthread_setname_np(rfi->remmina_plugin_thread, thname);
2500 #elif defined(__FreeBSD__)
2501  pthread_set_name_np(rfi->remmina_plugin_thread, thname);
2502 #endif
2503 
2504  return TRUE;
2505 }
2506 
2508 {
2509  TRACE_CALL(__func__);
2510 
2511  REMMINA_PLUGIN_DEBUG("Requesting to close the connection");
2512  RemminaPluginRdpEvent rdp_event = { 0 };
2513  rfContext *rfi = GET_PLUGIN_DATA(gp);
2514 
2515  if (!remmina_plugin_service->is_main_thread())
2516  g_warning("WARNING: %s called on a subthread, which may not work or crash Remmina.", __func__);
2517 
2518  if (rfi && !rfi->connected) {
2519  /* libfreerdp is attempting to connect, we cannot interrupt our main thread
2520  * in the connect phase.
2521  * So we remove "plugin-data" from gp, so our rfi remains "orphan"
2522  */
2524  g_object_steal_data(G_OBJECT(gp), "plugin-data");
2525  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
2526  return FALSE;
2527  }
2528 
2529 
2530  if (rfi && rfi->clipboard.srv_clip_data_wait == SCDW_BUSY_WAIT) {
2531  REMMINA_PLUGIN_DEBUG("[RDP] requesting clipboard transfer to abort");
2532  /* Allow clipboard transfer from server to terminate */
2533  rfi->clipboard.srv_clip_data_wait = SCDW_ABORTING;
2534  usleep(100000);
2535  }
2536 
2537  if (rfi->is_reconnecting) {
2538  /* Special case: window closed when attempting to reconnect */
2539  rfi->stop_reconnecting_requested = TRUE;
2540  return FALSE;
2541  }
2542 
2543  rdp_event.type = REMMINA_RDP_EVENT_DISCONNECT;
2544  remmina_rdp_event_event_push(gp, &rdp_event);
2545 
2546  return FALSE;
2547 }
2548 
2550 {
2551  TRACE_CALL(__func__);
2552  return TRUE;
2553 }
2554 
2556 {
2557  TRACE_CALL(__func__);
2558  rfContext *rfi = GET_PLUGIN_DATA(gp);
2559 
2560  switch (feature->id) {
2561  case REMMINA_RDP_FEATURE_UNFOCUS:
2563  break;
2564 
2565  case REMMINA_RDP_FEATURE_SCALE:
2566  if (rfi) {
2567  rfi->scale = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
2569  } else {
2570  REMMINA_PLUGIN_DEBUG("Remmina RDP plugin warning: Null value for rfi by REMMINA_RDP_FEATURE_SCALE");
2571  }
2572  break;
2573 
2574  case REMMINA_RDP_FEATURE_MULTIMON:
2575  if (rfi) {
2576  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
2577  if (remmina_plugin_service->file_get_int(remminafile, "multimon", FALSE)) {
2578  freerdp_settings_set_bool(rfi->settings, FreeRDP_UseMultimon, TRUE);
2579  /* TODO Add an option for this */
2580  freerdp_settings_set_bool(rfi->settings, FreeRDP_ForceMultimon, TRUE);
2581  freerdp_settings_set_bool(rfi->settings, FreeRDP_Fullscreen, TRUE);
2583  }
2584  } else {
2585  REMMINA_PLUGIN_DEBUG("Remmina RDP plugin warning: Null value for rfi by REMMINA_RDP_FEATURE_MULTIMON");
2586  }
2587  break;
2588 
2589  case REMMINA_RDP_FEATURE_DYNRESUPDATE:
2590  break;
2591 
2592  case REMMINA_RDP_FEATURE_TOOL_REFRESH:
2593  if (rfi)
2594  gtk_widget_queue_draw_area(rfi->drawing_area, 0, 0,
2595  remmina_plugin_service->protocol_plugin_get_width(gp),
2596  remmina_plugin_service->protocol_plugin_get_height(gp));
2597  else
2598  REMMINA_PLUGIN_DEBUG("Remmina RDP plugin warning: Null value for rfi by REMMINA_RDP_FEATURE_TOOL_REFRESH");
2599  break;
2600 
2601  case REMMINA_RDP_FEATURE_TOOL_SENDCTRLALTDEL:
2603  break;
2604 
2605  default:
2606  break;
2607  }
2608 }
2609 
2610 /* Send a keystroke to the plugin window */
2611 static void remmina_rdp_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
2612 {
2613  TRACE_CALL(__func__);
2614  rfContext *rfi = GET_PLUGIN_DATA(gp);
2615 
2616  remmina_plugin_service->protocol_plugin_send_keys_signals(rfi->drawing_area,
2617  keystrokes, keylen, GDK_KEY_PRESS | GDK_KEY_RELEASE);
2618  return;
2619 }
2620 
2622 {
2623  rfContext *rfi = GET_PLUGIN_DATA(gp);
2624  rdpGdi *gdi;
2625  size_t szmem;
2626 
2627  UINT32 bytesPerPixel;
2628  UINT32 bitsPerPixel;
2629 
2630  if (!rfi)
2631  return FALSE;
2632 
2633  gdi = ((rdpContext *)rfi)->gdi;
2634 
2635  bytesPerPixel = GetBytesPerPixel(gdi->hdc->format);
2636  bitsPerPixel = GetBitsPerPixel(gdi->hdc->format);
2637 
2641  szmem = gdi->width * gdi->height * bytesPerPixel;
2642 
2643  REMMINA_PLUGIN_DEBUG("allocating %zu bytes for a full screenshot", szmem);
2644  rpsd->buffer = malloc(szmem);
2645  if (!rpsd->buffer) {
2646  REMMINA_PLUGIN_DEBUG("could not set aside %zu bytes for a full screenshot", szmem);
2647  return FALSE;
2648  }
2649  rpsd->width = gdi->width;
2650  rpsd->height = gdi->height;
2651  rpsd->bitsPerPixel = bitsPerPixel;
2652  rpsd->bytesPerPixel = bytesPerPixel;
2653 
2654  memcpy(rpsd->buffer, gdi->primary_buffer, szmem);
2655 
2656  /* Returning TRUE instruct also the caller to deallocate rpsd->buffer */
2657  return TRUE;
2658 }
2659 
2660 /* Array of key/value pairs for colour depths */
2661 static gpointer colordepth_list[] =
2662 {
2663  /* 1st one is the default in a new install */
2664  "99", N_("Automatic (32 bpp) (Server chooses its best format)"),
2665  "66", N_("GFX AVC444 (32 bpp)"),
2666  "65", N_("GFX AVC420 (32 bpp)"),
2667  "64", N_("GFX RFX (32 bpp)"),
2668  "63", N_("GFX RFX Progressive (32 bpp)"),
2669  "0", N_("RemoteFX (32 bpp)"),
2670  "32", N_("True colour (32 bpp)"),
2671  "24", N_("True colour (24 bpp)"),
2672  "16", N_("High colour (16 bpp)"),
2673  "15", N_("High colour (15 bpp)"),
2674  "8", N_("256 colours (8 bpp)"),
2675  NULL
2676 };
2677 
2678 /* Array of key/value pairs for the FreeRDP logging level */
2679 static gpointer log_level[] =
2680 {
2681  "INFO", "INFO",
2682  "FATAL", "FATAL",
2683  "ERROR", "ERROR",
2684  "WARN", "WARN",
2685  "DEBUG", "DEBUG",
2686  "TRACE", "TRACE",
2687  "OFF", "OFF",
2688  NULL
2689 };
2690 
2691 
2692 /* Array of key/value pairs for quality selection */
2693 static gpointer quality_list[] =
2694 {
2695  "0", N_("Poor (fastest)"),
2696  "1", N_("Medium"),
2697  "2", N_("Good"),
2698  "9", N_("Best (slowest)"),
2699  NULL
2700 };
2701 
2702 /* Array of key/value pairs for quality selection */
2703 static gpointer network_list[] =
2704 {
2705  "none", N_("None"),
2706  "autodetect", N_("Auto-detect"),
2707  "modem", N_("Modem"),
2708  "broadband-low", N_("Low performance broadband"),
2709  "satellite", N_("Satellite"),
2710  "broadband-high", N_("High performance broadband"),
2711  "wan", N_("WAN"),
2712  "lan", N_("LAN"),
2713  NULL
2714 };
2715 
2716 /* Array of key/value pairs for sound options */
2717 static gpointer sound_list[] =
2718 {
2719  "off", N_("Off"),
2720  "local", N_("Local"),
2721  "remote", N_("Remote"),
2722  NULL
2723 };
2724 
2725 /* Array of key/value pairs for security */
2726 static gpointer security_list[] =
2727 {
2728  "", N_("Automatic negotiation"),
2729  "nla", N_("NLA protocol security"),
2730  "tls", N_("TLS protocol security"),
2731  "rdp", N_("RDP protocol security"),
2732  "ext", N_("NLA extended protocol security"),
2733  NULL
2734 };
2735 
2736 static gpointer gwtransp_list[] =
2737 {
2738  "http", "HTTP",
2739  "rpc", "RPC",
2740  "auto", "Auto",
2741  NULL
2742 };
2743 
2744 static gpointer tls_seclevel[] =
2745 {
2746  "", N_("Default"),
2747  "0", N_("0 — Windows 7 compatible"),
2748  "1", N_("1"),
2749  "2", N_("2"),
2750  "3", N_("3"),
2751  "4", N_("4"),
2752  "5", N_("5"),
2753  NULL
2754 };
2755 
2756 static gchar clientbuild_list[] =
2757  N_("2600 (Windows XP), 7601 (Windows Vista/7), 9600 (Windows 8 and newer)");
2758 
2759 static gchar clientbuild_tooltip[] =
2760  N_("Used i.a. by terminal services in a smart card channel to distinguish client capabilities:\n"
2761  " • < 4034: Windows XP base smart card functions\n"
2762  " • 4034-7064: Windows Vista/7: SCardReadCache(),\n"
2763  " SCardWriteCache(), SCardGetTransmitCount()\n"
2764  " • >= 7065: Windows 8 and newer: SCardGetReaderIcon(),\n"
2765  " SCardGetDeviceTypeId()");
2766 
2767 static gchar microphone_tooltip[] =
2768  N_("Options for redirection of audio input:\n"
2769  " • [sys:<sys>,][dev:<dev>,][format:<format>,][rate:<rate>,]\n"
2770  " [channel:<channel>] Audio input (microphone)\n"
2771  " • sys:pulse\n"
2772  " • format:1\n"
2773  " • sys:oss,dev:1,format:1\n"
2774  " • sys:alsa");
2775 
2776 static gchar audio_tooltip[] =
2777  N_("Options for redirection of audio output:\n"
2778  " • [sys:<sys>,][dev:<dev>,][format:<format>,][rate:<rate>,]\n"
2779  " [channel:<channel>] Audio output\n"
2780  " • sys:pulse\n"
2781  " • format:1\n"
2782  " • sys:oss,dev:1,format:1\n"
2783  " • sys:alsa");
2784 
2785 
2786 static gchar usb_tooltip[] =
2787  N_("Options for redirection of USB device:\n"
2788  " • [dbg,][id:<vid>:<pid>#…,][addr:<bus>:<addr>#…,][auto]\n"
2789  " • auto\n"
2790  " • id:054c:0268#4669:6e6b,addr:04:0c");
2791 
2792 static gchar timeout_tooltip[] =
2793  N_("Advanced setting for high latency links:\n"
2794  "Adjusts the connection timeout. Use if your connection times out.\n"
2795  "The highest possible value is 600000 ms (10 minutes).\n");
2796 
2797 static gchar network_tooltip[] =
2798  N_("Performance optimisations based on the network connection type:\n"
2799  "Using auto-detection is advised.\n"
2800  "If “Auto-detect” fails, choose the most appropriate option in the list.\n");
2801 
2802 static gchar monitorids_tooltip[] =
2803  N_("Comma-separated list of monitor IDs and desktop orientations:\n"
2804  " • [<id>:<orientation-in-degrees>,]\n"
2805  " • 0,1,2,3\n"
2806  " • 0:270,1:90\n"
2807  "Orientations are specified in degrees, valid values are:\n"
2808  " • 0 (landscape)\n"
2809  " • 90 (portrait)\n"
2810  " • 180 (landscape flipped)\n"
2811  " • 270 (portrait flipped)\n"
2812  "\n");
2813 
2814 static gchar drive_tooltip[] =
2815  N_("Redirect directory <path> as named share <name>.\n"
2816  " • <name>,<fullpath>[;<name>,<fullpath>[;…]]\n"
2817  " • MyHome,/home/remminer\n"
2818  " • /home/remminer\n"
2819  " • MyHome,/home/remminer;SomePath,/path/to/somepath\n"
2820  "Hotplug support is enabled with:\n"
2821  " • hotplug,*\n"
2822  "\n");
2823 
2824 /* Array of RemminaProtocolSetting for basic settings.
2825  * Each item is composed by:
2826  * a) RemminaProtocolSettingType for setting type
2827  * b) Setting name
2828  * c) Setting description
2829  * d) Compact disposition
2830  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
2831  * f) Setting tooltip
2832  * g) Validation data pointer, will be passed to the validation callback method.
2833  * h) Validation callback method (Can be NULL. Every entry will be valid then.)
2834  * use following prototype:
2835  * gboolean mysetting_validator_method(gpointer key, gpointer value,
2836  * gpointer validator_data);
2837  * gpointer key is a gchar* containing the setting's name,
2838  * gpointer value contains the value which should be validated,
2839  * gpointer validator_data contains your passed data.
2840  */
2842 {
2843  { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, NULL, NULL, NULL, NULL },
2844  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL, NULL, NULL },
2845  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("Password"), FALSE, NULL, NULL, NULL, NULL },
2846  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "domain", N_("Domain"), FALSE, NULL, NULL, NULL, NULL },
2847  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "drive", N_("Share folder"), FALSE, NULL, drive_tooltip, NULL, NULL },
2848  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "restricted-admin", N_("Restricted admin mode"), FALSE, NULL, NULL, NULL, NULL },
2849  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "pth", N_("Password hash"), FALSE, NULL, N_("Restricted admin mode password hash"), NULL, NULL },
2850  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "left-handed", N_("Left-handed mouse support"), TRUE, NULL, N_("Swap left and right mouse buttons for left-handed mouse support"), NULL, NULL },
2851  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disable-smooth-scrolling", N_("Disable smooth scrolling"), TRUE, NULL, NULL, NULL, NULL },
2852  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "multimon", N_("Enable multi monitor"), TRUE, NULL, NULL, NULL, NULL },
2853  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "span", N_("Span screen over multiple monitors"), TRUE, NULL, NULL, NULL, NULL },
2854  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "monitorids", N_("List monitor IDs"), FALSE, NULL, monitorids_tooltip, NULL, NULL },
2855  { REMMINA_PROTOCOL_SETTING_TYPE_RESOLUTION, "resolution", NULL, FALSE, NULL, NULL, NULL, NULL },
2856  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "colordepth", N_("Colour depth"), FALSE, colordepth_list, NULL, NULL, NULL },
2857  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "network", N_("Network connection type"), FALSE, network_list, network_tooltip, NULL, NULL },
2858  { REMMINA_PROTOCOL_SETTING_TYPE_KEYMAP, "keymap", NULL, FALSE, NULL, NULL, NULL, NULL },
2859  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL }
2860 };
2861 
2862 /* Array of RemminaProtocolSetting for advanced settings.
2863  * Each item is composed by:
2864  * a) RemminaProtocolSettingType for setting type
2865  * b) Setting name
2866  * c) Setting description
2867  * d) Compact disposition
2868  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
2869  * f) Setting Tooltip
2870  */
2872 {
2873  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "quality", N_("Quality"), FALSE, quality_list, NULL },
2874  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "security", N_("Security protocol negotiation"), FALSE, security_list, NULL },
2875  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "gwtransp", N_("Gateway transport type"), FALSE, gwtransp_list, NULL },
2876  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "tls-seclevel", N_("TLS Security Level"), FALSE, tls_seclevel, NULL },
2877  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "freerdp_log_level", N_("FreeRDP log level"), FALSE, log_level, NULL },
2878  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "freerdp_log_filters", N_("FreeRDP log filters"), FALSE, NULL, N_("tag:level[,tag:level[,…]]") },
2879  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "sound", N_("Audio output mode"), FALSE, sound_list, NULL },
2880  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "audio-output", N_("Redirect local audio output"), TRUE, NULL, audio_tooltip },
2881  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "microphone", N_("Redirect local microphone"), TRUE, NULL, microphone_tooltip },
2882  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "timeout", N_("Connection timeout in ms"), TRUE, NULL, timeout_tooltip },
2883  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "gateway_server", N_("Remote Desktop Gateway server"), FALSE, NULL, NULL },
2884  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "gateway_username", N_("Remote Desktop Gateway username"), FALSE, NULL, NULL },
2885  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "gateway_password", N_("Remote Desktop Gateway password"), FALSE, NULL, NULL },
2886  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "gateway_domain", N_("Remote Desktop Gateway domain"), FALSE, NULL, NULL },
2887  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "clientname", N_("Client name"), FALSE, NULL, NULL },
2888  { REMMINA_PROTOCOL_SETTING_TYPE_COMBO, "clientbuild", N_("Client build"), FALSE, clientbuild_list, clientbuild_tooltip },
2889  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "exec", N_("Start-up program"), FALSE, NULL, NULL },
2890  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "execpath", N_("Start-up path"), FALSE, NULL, NULL },
2891  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "loadbalanceinfo", N_("Load balance info"), FALSE, NULL, NULL },
2892  // TRANSLATORS: Do not use typographic quotation marks, these must stay as "double quote", also know as “Typewriter ("programmer's") quote, ambidextrous.”
2893  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "printer_overrides", N_("Override printer drivers"), FALSE, NULL, N_("\"Samsung_CLX-3300_Series\":\"Samsung CLX-3300 Series PS\";\"Canon MF410\":\"Canon MF410 Series UFR II\"") },
2894  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "usb", N_("USB device redirection"), TRUE, NULL, usb_tooltip },
2895  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "serialname", N_("Local serial name"), FALSE, NULL, N_("COM1, COM2, etc.") },
2896  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "serialdriver", N_("Local serial driver"), FALSE, NULL, N_("Serial") },
2897  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "serialpath", N_("Local serial path"), FALSE, NULL, N_("/dev/ttyS0, /dev/ttyS1, etc.") },
2898  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "parallelname", N_("Local parallel name"), FALSE, NULL, NULL },
2899  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "parallelpath", N_("Local parallel device"), FALSE, NULL, NULL },
2900  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "smartcardname", N_("Name of smart card"), FALSE, NULL, NULL },
2901  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "dvc", N_("Dynamic virtual channel"), FALSE, NULL, N_("<channel>[,<options>]") },
2902  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "vc", N_("Static virtual channel"), FALSE, NULL, N_("<channel>[,<options>]") },
2903  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "rdp2tcp", N_("TCP redirection"), FALSE, NULL, N_("/PATH/TO/rdp2tcp") },
2904  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "rdp_reconnect_attempts", N_("Reconnect attempts number"), FALSE, NULL, N_("The maximum number of reconnect attempts upon an RDP disconnect (default: 20)") },
2905  { REMMINA_PROTOCOL_SETTING_TYPE_ASSISTANCE, "assistance_mode", N_("Attempt to connect in assistance mode"), TRUE, NULL },
2906  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "preferipv6", N_("Prefer IPv6 AAAA record over IPv4 A record"), TRUE, NULL, NULL },
2907  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "shareprinter", N_("Share printers"), TRUE, NULL, NULL },
2908  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "shareserial", N_("Share serial ports"), TRUE, NULL, NULL },
2909  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "serialpermissive", N_("(SELinux) permissive mode for serial ports"), TRUE, NULL, NULL },
2910  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "shareparallel", N_("Share parallel ports"), TRUE, NULL, NULL },
2911  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "sharesmartcard", N_("Share a smart card"), TRUE, NULL, NULL },
2912  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", N_("Turn off clipboard sync"), TRUE, NULL, NULL },
2913  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "cert_ignore", N_("Ignore certificate"), TRUE, NULL, NULL },
2914  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "old-license", N_("Use the old license workflow"), TRUE, NULL, N_("It disables CAL and hwId is set to 0") },
2915  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"), TRUE, NULL, NULL },
2916  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "console", N_("Attach to console (2003/2003 R2)"), TRUE, NULL, NULL },
2917  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disable_fastpath", N_("Turn off fast-path"), TRUE, NULL, NULL },
2918  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "gateway_usage", N_("Server detection using Remote Desktop Gateway"), TRUE, NULL, NULL },
2919 #if defined(PROXY_TYPE_IGNORE)
2920  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "useproxyenv", N_("Use system proxy settings"), TRUE, NULL, NULL },
2921 #endif
2922  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableautoreconnect", N_("Turn off automatic reconnection"), TRUE, NULL, NULL },
2923  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "relax-order-checks", N_("Relax order checks"), TRUE, NULL, NULL },
2924  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "glyph-cache", N_("Glyph cache"), TRUE, NULL, NULL },
2925  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "multitransport", N_("Enable multitransport protocol (UDP)"), TRUE, NULL, N_("Using the UDP protocol may improve performance") },
2926  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "base-cred-for-gw", N_("Use base credentials for gateway too"), TRUE, NULL, NULL },
2927 #if FREERDP_CHECK_VERSION(2, 3, 1)
2928  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "websockets", N_("Enable Gateway websockets support"), TRUE, NULL, NULL },
2929 #endif
2930  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "no-suppress", N_("Update framebuffer even when not visible"), TRUE, NULL },
2931  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
2932 };
2933 
2934 /* Array for available features.
2935  * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
2937 {
2938  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_RDP_FEATURE_VIEWONLY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "viewonly",
2939  N_("View only") },
2940  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_RDP_FEATURE_TOOL_REFRESH, N_("Refresh"), NULL, NULL },
2941  { REMMINA_PROTOCOL_FEATURE_TYPE_SCALE, REMMINA_RDP_FEATURE_SCALE, NULL, NULL, NULL },
2942  { REMMINA_PROTOCOL_FEATURE_TYPE_DYNRESUPDATE, REMMINA_RDP_FEATURE_DYNRESUPDATE, NULL, NULL, NULL },
2943  { REMMINA_PROTOCOL_FEATURE_TYPE_MULTIMON, REMMINA_RDP_FEATURE_MULTIMON, NULL, NULL, NULL },
2944  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_RDP_FEATURE_TOOL_SENDCTRLALTDEL, N_("Send Ctrl+Alt+Delete"), NULL, NULL },
2945  { REMMINA_PROTOCOL_FEATURE_TYPE_UNFOCUS, REMMINA_RDP_FEATURE_UNFOCUS, NULL, NULL, NULL },
2946  { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
2947 };
2948 
2949 /* This will be filled with version info string */
2951 
2952 /* Protocol plugin definition and features */
2954 {
2956  "RDP", // Name
2957  N_("RDP - Remote Desktop Protocol"), // Description
2958  GETTEXT_PACKAGE, // Translation domain
2959  remmina_plugin_rdp_version, // Version number
2960  "org.remmina.Remmina-rdp-symbolic", // Icon for normal connection
2961  "org.remmina.Remmina-rdp-ssh-symbolic", // Icon for SSH connection
2962  remmina_rdp_basic_settings, // Array for basic settings
2963  remmina_rdp_advanced_settings, // Array for advanced settings
2964  REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type
2965  remmina_rdp_features, // Array for available features
2966  remmina_rdp_init, // Plugin initialization
2967  remmina_rdp_open_connection, // Plugin open connection
2968  remmina_rdp_close_connection, // Plugin close connection
2969  remmina_rdp_query_feature, // Query for available features
2970  remmina_rdp_call_feature, // Call a feature
2971  remmina_rdp_keystroke, // Send a keystroke
2972  remmina_rdp_get_screenshot, // Screenshot
2973  remmina_rdp_event_on_map, // RCW map event
2974  remmina_rdp_event_on_unmap // RCW unmap event
2975 };
2976 
2977 /* File plugin definition and features */
2979 {
2980  REMMINA_PLUGIN_TYPE_FILE, // Type
2981  "RDPF", // Name
2982  N_("RDP - RDP File Handler"), // Description
2983  GETTEXT_PACKAGE, // Translation domain
2984  remmina_plugin_rdp_version, // Version number
2985  remmina_rdp_file_import_test, // Test import function
2986  remmina_rdp_file_import, // Import function
2987  remmina_rdp_file_export_test, // Test export function
2988  remmina_rdp_file_export, // Export function
2989  NULL
2990 };
2991 
2992 /* Preferences plugin definition and features */
2994 {
2995  REMMINA_PLUGIN_TYPE_PREF, // Type
2996  "RDPS", // Name
2997  N_("RDP - Preferences"), // Description
2998  GETTEXT_PACKAGE, // Translation domain
2999  remmina_plugin_rdp_version, // Version number
3000  "RDP", // Label
3001  remmina_rdp_settings_new // Preferences body function
3002 };
3003 
3004 static char *buildconfig_strstr(const char *bc, const char *option)
3005 {
3006  TRACE_CALL(__func__);
3007 
3008  char *p, *n;
3009 
3010  p = strcasestr(bc, option);
3011  if (p == NULL)
3012  return NULL;
3013 
3014  if (p > bc && *(p - 1) > ' ')
3015  return NULL;
3016 
3017  n = p + strlen(option);
3018  if (*n > ' ')
3019  return NULL;
3020 
3021  return p;
3022 }
3023 
3024 G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
3025 {
3026  int vermaj, vermin, verrev;
3027 
3028  TRACE_CALL(__func__);
3029  remmina_plugin_service = service;
3030 
3031  /* Check that we are linked to the correct version of libfreerdp */
3032 
3033  freerdp_get_version(&vermaj, &vermin, &verrev);
3034  if (vermaj < FREERDP_REQUIRED_MAJOR ||
3035  (vermaj == FREERDP_REQUIRED_MAJOR && (vermin < FREERDP_REQUIRED_MINOR ||
3036  (vermin == FREERDP_REQUIRED_MINOR && verrev < FREERDP_REQUIRED_REVISION)))) {
3037  g_printf("Upgrade your FreeRDP library version from %d.%d.%d to at least libfreerdp %d.%d.%d "
3038  "to run the RDP plugin.\n",
3039  vermaj, vermin, verrev,
3040  FREERDP_REQUIRED_MAJOR, FREERDP_REQUIRED_MINOR, FREERDP_REQUIRED_REVISION);
3041  return FALSE;
3042  }
3043 
3044  bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
3045  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
3046 
3047  if (!service->register_plugin((RemminaPlugin *)&remmina_rdp))
3048  return FALSE;
3049 
3050  remmina_rdpf.export_hints = _("Export connection in Windows .rdp file format");
3051 
3052  if (!service->register_plugin((RemminaPlugin *)&remmina_rdpf))
3053  return FALSE;
3054 
3055  if (!service->register_plugin((RemminaPlugin *)&remmina_rdps))
3056  return FALSE;
3057 
3058  if (buildconfig_strstr(freerdp_get_build_config(), "WITH_GFX_H264=ON")) {
3059  gfx_h264_available = TRUE;
3060  REMMINA_PLUGIN_DEBUG("gfx_h264_available: %d", gfx_h264_available);
3061  } else {
3062  gfx_h264_available = FALSE;
3063  REMMINA_PLUGIN_DEBUG("gfx_h264_available: %d", gfx_h264_available);
3064  /* Remove values 65 and 66 from colordepth_list array by shifting it */
3065  gpointer *src, *dst;
3066  dst = src = colordepth_list;
3067  while (*src) {
3068  if (strcmp(*src, "66") != 0 && strcmp(*src, "65") != 0) {
3069  if (dst != src) {
3070  *dst = *src;
3071  *(dst + 1) = *(src + 1);
3072  }
3073  dst += 2;
3074  }
3075  src += 2;
3076  }
3077  *dst = NULL;
3078  }
3079 
3081  "RDP plugin: %s (Git %s), Compiled with libfreerdp %s (%s), Running with libfreerdp %s (rev %s), H.264 %s",
3082  VERSION, REMMINA_GIT_REVISION,
3083 #ifdef WITH_FREERDP3
3084  FREERDP_VERSION_FULL, FREERDP_GIT_REVISION,
3085 #else
3086  FREERDP_VERSION_FULL, GIT_REVISION,
3087 #endif
3088  freerdp_get_version_string(),
3089  freerdp_get_build_revision(),
3090  gfx_h264_available ? "Yes" : "No"
3091  );
3092 
3094 
3095  return TRUE;
3096 }
CliprdrClientContext * context
Definition: rdp_plugin.h:135
+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-2022 Antenore Gatta, Giovanni Panozzo
6  * Copyright (C) 2022-2023 Antenore Gatta, Giovanni Panozzo, Hiroyuki Tanaka
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  * In addition, as a special exception, the copyright holders give
24  * permission to link the code of portions of this program with the
25  * OpenSSL library under certain conditions as described in each
26  * individual source file, and distribute linked combinations
27  * including the two.
28  * You must obey the GNU General Public License in all respects
29  * for all of the code used other than OpenSSL. * If you modify
30  * file(s) with this exception, you may extend this exception to your
31  * version of the file(s), but you are not obligated to do so. * If you
32  * do not wish to do so, delete this exception statement from your
33  * version. * If you delete this exception statement from all source
34  * files in the program, then also delete it here.
35  *
36  */
37 
38 
39 #define _GNU_SOURCE
40 
41 #include "remmina/plugin.h"
42 #include "rdp_plugin.h"
43 #include "rdp_event.h"
44 #include "rdp_graphics.h"
45 #include "rdp_file.h"
46 #include "rdp_settings.h"
47 #include "rdp_cliprdr.h"
48 #include "rdp_monitor.h"
49 #include "rdp_channels.h"
50 
51 #include <errno.h>
52 #include <pthread.h>
53 #include <time.h>
54 #include <sys/time.h>
55 #ifdef GDK_WINDOWING_X11
56 #include <cairo/cairo-xlib.h>
57 #else
58 #include <cairo/cairo.h>
59 #endif
60 #include <freerdp/addin.h>
61 #include <freerdp/assistance.h>
62 #include <freerdp/settings.h>
63 #include <freerdp/freerdp.h>
64 #include <freerdp/constants.h>
65 #include <freerdp/client/cliprdr.h>
66 #include <freerdp/client/channels.h>
67 #include <freerdp/client/cmdline.h>
68 #include <freerdp/error.h>
69 #include <freerdp/event.h>
70 #include <winpr/memory.h>
71 #include <winpr/cmdline.h>
72 #include <ctype.h>
73 
74 #ifdef HAVE_CUPS
75 #include <cups/cups.h>
76 #endif
77 
78 #include <unistd.h>
79 #include <string.h>
80 
81 #ifdef GDK_WINDOWING_X11
82 #include <X11/Xlib.h>
83 #include <X11/XKBlib.h>
84 #include <gdk/gdkx.h>
85 #elif defined(GDK_WINDOWING_WAYLAND)
86 #include <gdk/gdkwayland.h>
87 #endif
88 
89 #if defined(__FreeBSD__)
90 #include <pthread_np.h>
91 #endif
92 
93 #include <freerdp/locale/keyboard.h>
94 
95 #define REMMINA_RDP_FEATURE_TOOL_REFRESH 1
96 #define REMMINA_RDP_FEATURE_SCALE 2
97 #define REMMINA_RDP_FEATURE_UNFOCUS 3
98 #define REMMINA_RDP_FEATURE_TOOL_SENDCTRLALTDEL 4
99 #define REMMINA_RDP_FEATURE_DYNRESUPDATE 5
100 #define REMMINA_RDP_FEATURE_MULTIMON 6
101 #define REMMINA_RDP_FEATURE_VIEWONLY 7
102 
103 #define REMMINA_CONNECTION_TYPE_NONE 0
104 
105 #ifdef WITH_FREERDP3
106  #define CLPARAM const char
107 #else
108  #define CLPARAM char
109 #endif
110 
111 
113 
114 static BOOL gfx_h264_available = FALSE;
115 
116 /* Compatibility: these functions have been introduced with https://github.com/FreeRDP/FreeRDP/commit/8c5d96784d
117  * and are missing on older FreeRDP, so we add them here.
118  * They should be removed from here after all distributed versions of FreeRDP (libwinpr) will have
119  * CommandLineParseCommaSeparatedValuesEx() onboard.
120  *
121  * (C) Copyright goes to the FreeRDP authors.
122  */
123 static CLPARAM **remmina_rdp_CommandLineParseCommaSeparatedValuesEx(const char *name, const char *list, size_t *count)
124 {
125  TRACE_CALL(__func__);
126 #if FREERDP_CHECK_VERSION(2, 0, 0)
127  return (CLPARAM **)CommandLineParseCommaSeparatedValuesEx(name, list, count);
128 #else
129  char **p;
130  char *str;
131  size_t nArgs;
132  size_t index;
133  size_t nCommas;
134  size_t prefix, len;
135 
136  nCommas = 0;
137 
138  if (count == NULL)
139  return NULL;
140 
141  *count = 0;
142 
143  if (!list) {
144  if (name) {
145  size_t len = strlen(name);
146  p = (char **)calloc(2UL + len, sizeof(char *));
147 
148  if (p) {
149  char *dst = (char *)&p[1];
150  p[0] = dst;
151  sprintf_s(dst, len + 1, "%s", name);
152  *count = 1;
153  return p;
154  }
155  }
156 
157  return NULL;
158  }
159 
160  {
161  const char *it = list;
162 
163  while ((it = strchr(it, ',')) != NULL) {
164  it++;
165  nCommas++;
166  }
167  }
168 
169  nArgs = nCommas + 1;
170 
171  if (name)
172  nArgs++;
173 
174  prefix = (nArgs + 1UL) * sizeof(char *);
175  len = strlen(list);
176  p = (char **)calloc(len + prefix + 1, sizeof(char *));
177 
178  if (!p)
179  return NULL;
180 
181  str = &((char *)p)[prefix];
182  memcpy(str, list, len);
183 
184  if (name)
185  p[0] = (char *)name;
186 
187  for (index = name ? 1 : 0; index < nArgs; index++) {
188  char *comma = strchr(str, ',');
189  p[index] = str;
190 
191  if (comma) {
192  str = comma + 1;
193  *comma = '\0';
194  }
195  }
196 
197  *count = nArgs;
198  return p;
199 #endif
200 }
201 
202 static CLPARAM **remmina_rdp_CommandLineParseCommaSeparatedValues(const char *list, size_t *count)
203 {
204  TRACE_CALL(__func__);
205  return remmina_rdp_CommandLineParseCommaSeparatedValuesEx(NULL, list, count);
206 }
207 
208 /*
209  * End of CommandLineParseCommaSeparatedValuesEx() compatibility and copyright
210  */
212 {
213  TRACE_CALL(__func__);
214  UINT16 flags;
215  rdpInput *input;
216  rfContext *rfi = GET_PLUGIN_DATA(gp);
217  RemminaPluginRdpEvent *event;
218  DISPLAY_CONTROL_MONITOR_LAYOUT *dcml;
219  CLIPRDR_FORMAT_DATA_RESPONSE response = { 0 };
220  RemminaFile *remminafile;
221 
222  if (rfi->event_queue == NULL)
223  return true;
224 
225  input = rfi->instance->input;
226 
227  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
228 
229  while ((event = (RemminaPluginRdpEvent *)g_async_queue_try_pop(rfi->event_queue)) != NULL) {
230  switch (event->type) {
232  flags = event->key_event.extended ? KBD_FLAGS_EXTENDED : 0;
233  flags |= event->key_event.up ? KBD_FLAGS_RELEASE : KBD_FLAGS_DOWN;
234  input->KeyboardEvent(input, flags, event->key_event.key_code);
235  break;
236 
238  /*
239  * TS_UNICODE_KEYBOARD_EVENT RDP message, see https://msdn.microsoft.com/en-us/library/cc240585.aspx
240  */
241  flags = event->key_event.up ? KBD_FLAGS_RELEASE : KBD_FLAGS_DOWN;
242  input->UnicodeKeyboardEvent(input, flags, event->key_event.unicode_code);
243  break;
244 
246  if (event->mouse_event.extended)
247  input->ExtendedMouseEvent(input, event->mouse_event.flags,
248  event->mouse_event.x, event->mouse_event.y);
249  else
250  input->MouseEvent(input, event->mouse_event.flags,
251  event->mouse_event.x, event->mouse_event.y);
252  break;
253 
255  rfi->clipboard.context->ClientFormatList(rfi->clipboard.context, event->clipboard_formatlist.pFormatList);
256  free(event->clipboard_formatlist.pFormatList);
257  break;
258 
260  response.msgFlags = (event->clipboard_formatdataresponse.data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
261  response.dataLen = event->clipboard_formatdataresponse.size;
262  response.requestedFormatData = event->clipboard_formatdataresponse.data;
263  rfi->clipboard.context->ClientFormatDataResponse(rfi->clipboard.context, &response);
264  break;
265 
267  REMMINA_PLUGIN_DEBUG("Sending client FormatDataRequest to server");
268  gettimeofday(&(rfi->clipboard.clientformatdatarequest_tv), NULL);
269  rfi->clipboard.context->ClientFormatDataRequest(rfi->clipboard.context, event->clipboard_formatdatarequest.pFormatDataRequest);
270  free(event->clipboard_formatdatarequest.pFormatDataRequest);
271  break;
272 
274  if (remmina_plugin_service->file_get_int(remminafile, "multimon", FALSE)) {
275  freerdp_settings_set_bool(rfi->settings, FreeRDP_UseMultimon, TRUE);
276  /* TODO Add an option for this */
277  freerdp_settings_set_bool(rfi->settings, FreeRDP_ForceMultimon, TRUE);
278  freerdp_settings_set_bool(rfi->settings, FreeRDP_Fullscreen, TRUE);
279  /* got some crashes with g_malloc0, to be investigated */
280  dcml = calloc(freerdp_settings_get_uint32(rfi->settings, FreeRDP_MonitorCount), sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
281  REMMINA_PLUGIN_DEBUG("REMMINA_RDP_EVENT_TYPE_SEND_MONITOR_LAYOUT:");
282  if (!dcml)
283  break;
284 
285  const rdpMonitor *base = freerdp_settings_get_pointer(rfi->settings, FreeRDP_MonitorDefArray);
286  for (gint i = 0; i < freerdp_settings_get_uint32(rfi->settings, FreeRDP_MonitorCount); ++i) {
287  const rdpMonitor *current = &base[i];
288  REMMINA_PLUGIN_DEBUG("Sending display layout for monitor n° %d", i);
289  dcml[i].Flags = (current->is_primary ? DISPLAY_CONTROL_MONITOR_PRIMARY : 0);
290  REMMINA_PLUGIN_DEBUG("Monitor %d is primary: %d", i, dcml[i].Flags);
291  dcml[i].Left = current->x;
292  REMMINA_PLUGIN_DEBUG("Monitor %d x: %d", i, dcml[i].Left);
293  dcml[i].Top = current->y;
294  REMMINA_PLUGIN_DEBUG("Monitor %d y: %d", i, dcml[i].Top);
295  dcml[i].Width = current->width;
296  REMMINA_PLUGIN_DEBUG("Monitor %d width: %d", i, dcml[i].Width);
297  dcml[i].Height = current->height;
298  REMMINA_PLUGIN_DEBUG("Monitor %d height: %d", i, dcml[i].Height);
299  dcml[i].PhysicalWidth = current->attributes.physicalWidth;
300  REMMINA_PLUGIN_DEBUG("Monitor %d physical width: %d", i, dcml[i].PhysicalWidth);
301  dcml[i].PhysicalHeight = current->attributes.physicalHeight;
302  REMMINA_PLUGIN_DEBUG("Monitor %d physical height: %d", i, dcml[i].PhysicalHeight);
303  if (current->attributes.orientation)
304  dcml[i].Orientation = current->attributes.orientation;
305  else
306  dcml[i].Orientation = event->monitor_layout.desktopOrientation;
307  REMMINA_PLUGIN_DEBUG("Monitor %d orientation: %d", i, dcml[i].Orientation);
308  dcml[i].DesktopScaleFactor = event->monitor_layout.desktopScaleFactor;
309  dcml[i].DeviceScaleFactor = event->monitor_layout.deviceScaleFactor;
310  }
311  rfi->dispcontext->SendMonitorLayout(rfi->dispcontext, freerdp_settings_get_uint32(rfi->settings, FreeRDP_MonitorCount), dcml);
312  g_free(dcml);
313  } else {
314  dcml = g_malloc0(sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
315  if (dcml) {
316  dcml->Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
317  dcml->Width = event->monitor_layout.width;
318  dcml->Height = event->monitor_layout.height;
319  dcml->Orientation = event->monitor_layout.desktopOrientation;
320  dcml->DesktopScaleFactor = event->monitor_layout.desktopScaleFactor;
321  dcml->DeviceScaleFactor = event->monitor_layout.deviceScaleFactor;
322  rfi->dispcontext->SendMonitorLayout(rfi->dispcontext, 1, dcml);
323  g_free(dcml); \
324  }
325  }
326  break;
328  /* Disconnect requested via GUI (i.e: tab destroy/close) */
329  freerdp_abort_connect(rfi->instance);
330  break;
331  }
332 
333  g_free(event);
334  }
335 
336  return true;
337 }
338 
340 {
341  TRACE_CALL(__func__);
342 
343  /* Opens the optional SSH tunnel if needed.
344  * Used also when reopening the same tunnel for a FreeRDP reconnect.
345  * Returns TRUE if all OK, and setups correct rfi->Settings values
346  * with connection and certificate parameters */
347 
348  gchar *hostport;
349  gchar *s;
350  gchar *host;
351  gchar *cert_host;
352  gint cert_port;
353  gint port;
354 
355  rfContext *rfi = GET_PLUGIN_DATA(gp);
356 
357  REMMINA_PLUGIN_DEBUG("Tunnel init");
358  hostport = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, 3389, FALSE);
359  if (hostport == NULL)
360  return FALSE;
361 
362  remmina_plugin_service->get_server_port(hostport, 3389, &host, &port);
363 
364  if (host[0] == 0)
365  return FALSE;
366 
367  REMMINA_PLUGIN_DEBUG("protocol_plugin_start_direct_tunnel() returned %s", hostport);
368 
369  cert_host = host;
370  cert_port = port;
371 
372  if (!rfi->is_reconnecting) {
373  /* settings->CertificateName and settings->ServerHostname is created
374  * only on 1st connect, not on reconnections */
375 
376  freerdp_settings_set_string(rfi->settings, FreeRDP_ServerHostname, host);
377 
378  if (cert_port == 3389) {
379  freerdp_settings_set_string(rfi->settings, FreeRDP_CertificateName, cert_host);
380  } else {
381  s = g_strdup_printf("%s:%d", cert_host, cert_port);
382  freerdp_settings_set_string(rfi->settings, FreeRDP_CertificateName, s);
383  g_free(s);
384  }
385  }
386 
387  REMMINA_PLUGIN_DEBUG("Tunnel has been optionally initialized. Now connecting to %s:%d", host, port);
388 
389  if (cert_host != host) g_free(cert_host);
390  g_free(host);
391  g_free(hostport);
392 
393  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ServerPort, port);
394 
395  return TRUE;
396 }
397 
398 static BOOL rf_auto_reconnect(rfContext *rfi)
399 {
400  TRACE_CALL(__func__);
401  rdpSettings *settings = rfi->instance->settings;
403  time_t treconn;
404  gchar *cval;
405  gint maxattempts;
406 
408  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
409 
410  rfi->is_reconnecting = TRUE;
411  rfi->stop_reconnecting_requested = FALSE;
412 
413  /* Get the value set in FreeRDP_AutoReconnectMaxRetries (20) */
414  maxattempts = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
415  REMMINA_PLUGIN_DEBUG("maxattempts from default: %d", maxattempts);
416  /* Get the value from the global preferences if any */
417  if ((cval = remmina_plugin_service->pref_get_value("rdp_reconnect_attempts")) != NULL)
418  maxattempts = atoi(cval);
419  REMMINA_PLUGIN_DEBUG("maxattempts from general preferences: %d", maxattempts);
420  /* Get the value from the profile if any, otherwise uses the value of maxattempts */
421  maxattempts = remmina_plugin_service->file_get_int(remminafile, "rdp_reconnect_attempts", maxattempts);
422  REMMINA_PLUGIN_DEBUG("maxattempts from general plugin: %d", maxattempts);
423  /* If maxattemps is <= 0, we get the value from FreeRDP_AutoReconnectMaxRetries (20) */
424  if (maxattempts <= 0)
425  maxattempts = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
426  freerdp_settings_set_uint32(settings, FreeRDP_AutoReconnectMaxRetries, maxattempts);
427  REMMINA_PLUGIN_DEBUG("maxattempts set to: %d", maxattempts);
428 
429  rfi->reconnect_maxattempts = maxattempts;
430  rfi->reconnect_nattempt = 0;
431 
432  /* Only auto reconnect on network disconnects. */
433  switch (freerdp_error_info(rfi->instance)) {
434  case ERRINFO_GRAPHICS_SUBSYSTEM_FAILED:
435  /* Disconnected by server hitting a bug or resource limit */
436  break;
437  case ERRINFO_SUCCESS:
438  /* A network disconnect was detected */
439  break;
440  default:
441  rfi->is_reconnecting = FALSE;
442  return FALSE;
443  }
444 
445  if (!freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled)) {
446  /* No auto-reconnect - just quit */
447  rfi->is_reconnecting = FALSE;
448  return FALSE;
449  }
450 
451  /* A network disconnect was detected and we should try to reconnect */
452  REMMINA_PLUGIN_DEBUG("[%s] network disconnection detected, initiating reconnection attempt",
453  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
454 
455  ui = g_new0(RemminaPluginRdpUiObject, 1);
458 
459  /* Sleep half a second to allow:
460  * - processing of the UI event we just pushed on the queue
461  * - better network conditions
462  * Remember: We hare on a thread, so the main gui won’t lock */
463  usleep(500000);
464 
465  /* Perform an auto-reconnect. */
466  while (TRUE) {
467  /* Quit retrying if max retries has been exceeded */
468  if (rfi->reconnect_nattempt++ >= rfi->reconnect_maxattempts) {
469  REMMINA_PLUGIN_DEBUG("[%s] maximum number of reconnection attempts exceeded.",
470  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
471  break;
472  }
473 
474  if (rfi->stop_reconnecting_requested) {
475  REMMINA_PLUGIN_DEBUG("[%s] reconnect request loop interrupted by user.",
476  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
477  break;
478  }
479 
480  /* Attempt the next reconnect */
481  REMMINA_PLUGIN_DEBUG("[%s] reconnection, attempt #%d of %d",
482  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname), rfi->reconnect_nattempt, rfi->reconnect_maxattempts);
483 
484  ui = g_new0(RemminaPluginRdpUiObject, 1);
487 
488  treconn = time(NULL);
489 
490  /* Reconnect the SSH tunnel, if needed */
492  REMMINA_PLUGIN_DEBUG("[%s] unable to recreate tunnel with remmina_rdp_tunnel_init.",
493  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
494  } else {
495  if (freerdp_reconnect(rfi->instance)) {
496  /* Reconnection is successful */
497  REMMINA_PLUGIN_DEBUG("[%s] reconnected.", freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
498  rfi->is_reconnecting = FALSE;
499  return TRUE;
500  }
501  }
502 
503  /* Wait until 5 secs have elapsed from last reconnect attempt, while checking for rfi->stop_reconnecting_requested */
504  while (time(NULL) - treconn < 5) {
506  break;
507  usleep(200000); // 200ms sleep
508  }
509  }
510 
511  rfi->is_reconnecting = FALSE;
512  return FALSE;
513 }
514 
515 static BOOL rf_begin_paint(rdpContext *context)
516 {
517  TRACE_CALL(__func__);
518  rdpGdi *gdi;
519 
520  if (!context)
521  return FALSE;
522 
523  gdi = context->gdi;
524  if (!gdi || !gdi->primary || !gdi->primary->hdc || !gdi->primary->hdc->hwnd)
525  return FALSE;
526 
527  return TRUE;
528 }
529 
530 static BOOL rf_end_paint(rdpContext *context)
531 {
532  TRACE_CALL(__func__);
533  rdpGdi *gdi;
534  rfContext *rfi;
536  int i, ninvalid;
537  region *reg;
538  HGDI_RGN cinvalid;
539 
540  gdi = context->gdi;
541  rfi = (rfContext *)context;
542 
543  if (gdi == NULL || gdi->primary == NULL || gdi->primary->hdc == NULL || gdi->primary->hdc->hwnd == NULL)
544  return TRUE;
545 
546  if (gdi->primary->hdc->hwnd->invalid->null)
547  return TRUE;
548 
549  if (gdi->primary->hdc->hwnd->ninvalid < 1)
550  return TRUE;
551 
552  ninvalid = gdi->primary->hdc->hwnd->ninvalid;
553  cinvalid = gdi->primary->hdc->hwnd->cinvalid;
554  reg = (region *)g_malloc(sizeof(region) * ninvalid);
555  for (i = 0; i < ninvalid; i++) {
556  reg[i].x = cinvalid[i].x;
557  reg[i].y = cinvalid[i].y;
558  reg[i].w = cinvalid[i].w;
559  reg[i].h = cinvalid[i].h;
560  }
561 
562  ui = g_new0(RemminaPluginRdpUiObject, 1);
564  ui->reg.ninvalid = ninvalid;
565  ui->reg.ureg = reg;
566 
568 
569 
570  gdi->primary->hdc->hwnd->invalid->null = TRUE;
571  gdi->primary->hdc->hwnd->ninvalid = 0;
572 
573 
574  return TRUE;
575 }
576 
577 static BOOL rf_desktop_resize(rdpContext *context)
578 {
579  TRACE_CALL(__func__);
580  rfContext *rfi;
583  UINT32 w, h;
584 
585  rfi = (rfContext *)context;
586  gp = rfi->protocol_widget;
587 
588  w = freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth);
589  h = freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopHeight);
590  remmina_plugin_service->protocol_plugin_set_width(gp, w);
591  remmina_plugin_service->protocol_plugin_set_height(gp, h);
592 
593  ui = g_new0(RemminaPluginRdpUiObject, 1);
597 
598  /* Tell libfreerdp to change its internal GDI bitmap width and heigt,
599  * this will also destroy gdi->primary_buffer, making our rfi->surface invalid */
600  gdi_resize(((rdpContext *)rfi)->gdi, w, h);
601 
602  /* Call to remmina_rdp_event_update_scale(gp) on the main UI thread,
603  * this will recreate rfi->surface from gdi->primary_buffer */
604 
605  ui = g_new0(RemminaPluginRdpUiObject, 1);
609 
610  remmina_plugin_service->protocol_plugin_desktop_resize(gp);
611 
612  return TRUE;
613 }
614 
615 static BOOL rf_play_sound(rdpContext *context, const PLAY_SOUND_UPDATE *play_sound)
616 {
617  TRACE_CALL(__func__);
618  rfContext *rfi;
620  GdkDisplay *disp;
621 
622  rfi = (rfContext *)context;
623  gp = rfi->protocol_widget;
624 
625  disp = gtk_widget_get_display(GTK_WIDGET(gp));
626  gdk_display_beep(disp);
627 
628  return TRUE;
629 }
630 
631 static BOOL rf_keyboard_set_indicators(rdpContext *context, UINT16 led_flags)
632 {
633  TRACE_CALL(__func__);
634  rfContext *rfi;
636  GdkDisplay *disp;
637 
638  rfi = (rfContext *)context;
639  gp = rfi->protocol_widget;
640  disp = gtk_widget_get_display(GTK_WIDGET(gp));
641 
642 #ifdef GDK_WINDOWING_X11
643  if (GDK_IS_X11_DISPLAY(disp)) {
644  /* TODO: We are not on the main thread. Will X.Org complain? */
645  Display *x11_display;
646  x11_display = gdk_x11_display_get_xdisplay(disp);
647  XkbLockModifiers(x11_display, XkbUseCoreKbd,
648  LockMask | Mod2Mask,
649  ((led_flags & KBD_SYNC_CAPS_LOCK) ? LockMask : 0) |
650  ((led_flags & KBD_SYNC_NUM_LOCK) ? Mod2Mask : 0)
651  );
652 
653  /* TODO: Add support to KANA_LOCK and SCROLL_LOCK */
654  }
655 #endif
656 
657  return TRUE;
658 }
659 
660 static BOOL rf_keyboard_set_ime_status(rdpContext *context, UINT16 imeId, UINT32 imeState,
661  UINT32 imeConvMode)
662 {
663  TRACE_CALL(__func__);
664  if (!context)
665  return FALSE;
666 
667  /* Unimplemented, we ignore it */
668 
669  return TRUE;
670 }
671 
672 static BOOL remmina_rdp_pre_connect(freerdp *instance)
673 {
674  TRACE_CALL(__func__);
675  rdpChannels *channels;
676  rdpSettings *settings;
677  rdpContext *context = instance->context;
678 
679  settings = instance->settings;
680  channels = context->channels;
681  freerdp_settings_set_uint32(settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNIX);
682  freerdp_settings_set_uint32(settings, FreeRDP_OsMinorType, OSMINORTYPE_UNSPECIFIED);
683  freerdp_settings_set_bool(settings, FreeRDP_BitmapCacheEnabled, TRUE);
684  freerdp_settings_set_uint32(settings, FreeRDP_OffscreenSupportLevel, 1);
685 
686  PubSub_SubscribeChannelConnected(instance->context->pubSub,
688  PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
690 
691  if (!freerdp_client_load_addins(channels, settings))
692  return FALSE;
693 
694  return true;
695 }
696 
697 static BOOL remmina_rdp_post_connect(freerdp *instance)
698 {
699  TRACE_CALL(__func__);
700  rfContext *rfi;
703  UINT32 freerdp_local_color_format;
704 
705  rfi = (rfContext *)instance->context;
706  gp = rfi->protocol_widget;
707  rfi->postconnect_error = REMMINA_POSTCONNECT_ERROR_OK;
708 
709  rfi->attempt_interactive_authentication = FALSE; // We authenticated!
710 
711  rfi->srcBpp = freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth);
712 
713  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_RemoteFxCodec) == FALSE)
714  rfi->sw_gdi = TRUE;
715 
716  rf_register_graphics(instance->context->graphics);
717 
718  REMMINA_PLUGIN_DEBUG("bpp: %d", rfi->bpp);
719  switch (rfi->bpp) {
720  case 24:
721  REMMINA_PLUGIN_DEBUG("CAIRO_FORMAT_RGB24");
722  freerdp_local_color_format = PIXEL_FORMAT_BGRX32;
723  rfi->cairo_format = CAIRO_FORMAT_RGB24;
724  break;
725  case 32:
730  REMMINA_PLUGIN_DEBUG("CAIRO_FORMAT_RGB24");
731  freerdp_local_color_format = PIXEL_FORMAT_BGRA32;
732  rfi->cairo_format = CAIRO_FORMAT_RGB24;
733  break;
734  default:
735  REMMINA_PLUGIN_DEBUG("CAIRO_FORMAT_RGB16_565");
736  freerdp_local_color_format = PIXEL_FORMAT_RGB16;
737  rfi->cairo_format = CAIRO_FORMAT_RGB16_565;
738  break;
739  }
740 
741  if (!gdi_init(instance, freerdp_local_color_format)) {
742  rfi->postconnect_error = REMMINA_POSTCONNECT_ERROR_GDI_INIT;
743  return FALSE;
744  }
745 
746  if (instance->context->codecs->h264 == NULL && freerdp_settings_get_bool(rfi->settings, FreeRDP_GfxH264)) {
747  gdi_free(instance);
748  rfi->postconnect_error = REMMINA_POSTCONNECT_ERROR_NO_H264;
749  return FALSE;
750  }
751 
752  // pointer_cache_register_callbacks(instance->update);
753 
754  instance->update->BeginPaint = rf_begin_paint;
755  instance->update->EndPaint = rf_end_paint;
756  instance->update->DesktopResize = rf_desktop_resize;
757 
758  instance->update->PlaySound = rf_play_sound;
759  instance->update->SetKeyboardIndicators = rf_keyboard_set_indicators;
760  instance->update->SetKeyboardImeStatus = rf_keyboard_set_ime_status;
761 
763  rfi->connected = true;
764 
765  ui = g_new0(RemminaPluginRdpUiObject, 1);
768 
769  return TRUE;
770 }
771 
772 static BOOL remmina_rdp_authenticate(freerdp *instance, char **username, char **password, char **domain)
773 {
774  TRACE_CALL(__func__);
775  gchar *s_username, *s_password, *s_domain;
776  gint ret;
777  rfContext *rfi;
779  gboolean save;
780  gboolean disablepasswordstoring;
781  RemminaFile *remminafile;
782 
783  rfi = (rfContext *)instance->context;
784  gp = rfi->protocol_widget;
785  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
786  disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
787 
788  ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
790  _("Enter RDP authentication credentials"),
791  remmina_plugin_service->file_get_string(remminafile, "username"),
792  remmina_plugin_service->file_get_string(remminafile, "password"),
793  remmina_plugin_service->file_get_string(remminafile, "domain"),
794  NULL);
795  if (ret == GTK_RESPONSE_OK) {
796  s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
797  if (s_username) freerdp_settings_set_string(rfi->settings, FreeRDP_Username, s_username);
798 
799  s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
800  if (s_password) freerdp_settings_set_string(rfi->settings, FreeRDP_Password, s_password);
801 
802  s_domain = remmina_plugin_service->protocol_plugin_init_get_domain(gp);
803  if (s_domain) freerdp_settings_set_string(rfi->settings, FreeRDP_Domain, s_domain);
804 
805  remmina_plugin_service->file_set_string(remminafile, "username", s_username);
806  remmina_plugin_service->file_set_string(remminafile, "domain", s_domain);
807 
808  save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
809  if (save) {
810  // User has requested to save credentials. We put the password
811  // into remminafile->settings. It will be saved later, on successful connection, by
812  // rcw.c
813  remmina_plugin_service->file_set_string(remminafile, "password", s_password);
814  } else {
815  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
816  }
817 
818 
819  if (s_username) g_free(s_username);
820  if (s_password) g_free(s_password);
821  if (s_domain) g_free(s_domain);
822 
823  return TRUE;
824  } else {
825  return FALSE;
826  }
827 
828  return TRUE;
829 }
830 
831 static BOOL remmina_rdp_gw_authenticate(freerdp *instance, char **username, char **password, char **domain)
832 {
833  TRACE_CALL(__func__);
834  gchar *s_username, *s_password, *s_domain;
835  gint ret;
836  rfContext *rfi;
838  gboolean save;
839  gboolean disablepasswordstoring;
840  gboolean basecredforgw;
841  RemminaFile *remminafile;
842 
843  rfi = (rfContext *)instance->context;
844  gp = rfi->protocol_widget;
845  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
846 
847  if (!remmina_plugin_service->file_get_string(remminafile, "gateway_server"))
848  return false;
849  disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
850  basecredforgw = remmina_plugin_service->file_get_int(remminafile, "base-cred-for-gw", FALSE);
851 
852  if (basecredforgw) {
853  ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
855  _("Enter RDP authentication credentials"),
856  remmina_plugin_service->file_get_string(remminafile, "username"),
857  remmina_plugin_service->file_get_string(remminafile, "password"),
858  remmina_plugin_service->file_get_string(remminafile, "domain"),
859  NULL);
860  } else {
861  ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
863  _("Enter RDP gateway authentication credentials"),
864  remmina_plugin_service->file_get_string(remminafile, "gateway_username"),
865  remmina_plugin_service->file_get_string(remminafile, "gateway_password"),
866  remmina_plugin_service->file_get_string(remminafile, "gateway_domain"),
867  NULL);
868  }
869 
870 
871  if (ret == GTK_RESPONSE_OK) {
872  s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
873  if (s_username) freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayUsername, s_username);
874 
875  s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
876  if (s_password) freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayPassword, s_password);
877 
878  s_domain = remmina_plugin_service->protocol_plugin_init_get_domain(gp);
879  if (s_domain) freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayDomain, s_domain);
880 
881  save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
882 
883  if (basecredforgw) {
884  remmina_plugin_service->file_set_string(remminafile, "username", s_username);
885  remmina_plugin_service->file_set_string(remminafile, "domain", s_domain);
886  if (save)
887  remmina_plugin_service->file_set_string(remminafile, "password", s_password);
888  else
889  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
890  } else {
891  remmina_plugin_service->file_set_string(remminafile, "gateway_username", s_username);
892  remmina_plugin_service->file_set_string(remminafile, "gateway_domain", s_domain);
893  if (save)
894  remmina_plugin_service->file_set_string(remminafile, "gateway_password", s_password);
895  else
896  remmina_plugin_service->file_set_string(remminafile, "gateway_password", NULL);
897  }
898 
899  if (s_username) g_free(s_username);
900  if (s_password) g_free(s_password);
901  if (s_domain) g_free(s_domain);
902 
903  return true;
904  } else {
905  return false;
906  }
907 
908  return true;
909 }
910 
911 static DWORD remmina_rdp_verify_certificate_ex(freerdp *instance, const char *host, UINT16 port,
912  const char *common_name, const char *subject,
913  const char *issuer, const char *fingerprint, DWORD flags)
914 {
915  TRACE_CALL(__func__);
916  gint status;
917  rfContext *rfi;
919 
920  rfi = (rfContext *)instance->context;
921  gp = rfi->protocol_widget;
922 
923  status = remmina_plugin_service->protocol_plugin_init_certificate(gp, subject, issuer, fingerprint);
924 
925  if (status == GTK_RESPONSE_OK)
926  return 1;
927 
928  return 0;
929 }
930 
931 static DWORD
932 remmina_rdp_verify_certificate(freerdp *instance, const char *common_name, const char *subject, const char *issuer, const char *fingerprint, BOOL host_mismatch) __attribute__ ((unused));
933 static DWORD
934 remmina_rdp_verify_certificate(freerdp *instance, const char *common_name, const char *subject,
935  const char *issuer, const char *fingerprint, BOOL host_mismatch)
936 {
937  TRACE_CALL(__func__);
938  gint status;
939  rfContext *rfi;
941 
942  rfi = (rfContext *)instance->context;
943  gp = rfi->protocol_widget;
944 
945  status = remmina_plugin_service->protocol_plugin_init_certificate(gp, subject, issuer, fingerprint);
946 
947  if (status == GTK_RESPONSE_OK)
948  return 1;
949 
950  return 0;
951 }
952 
953 static DWORD remmina_rdp_verify_changed_certificate_ex(freerdp *instance, const char *host, UINT16 port,
954  const char *common_name, const char *subject,
955  const char *issuer, const char *fingerprint,
956  const char *old_subject, const char *old_issuer,
957  const char *old_fingerprint, DWORD flags)
958 {
959  TRACE_CALL(__func__);
960  gint status;
961  rfContext *rfi;
963 
964  rfi = (rfContext *)instance->context;
965  gp = rfi->protocol_widget;
966 
967  status = remmina_plugin_service->protocol_plugin_changed_certificate(gp, subject, issuer, fingerprint, old_fingerprint);
968 
969  if (status == GTK_RESPONSE_OK)
970  return 1;
971 
972  return 0;
973 }
974 
975 static void remmina_rdp_post_disconnect(freerdp *instance)
976 {
977  TRACE_CALL(__func__);
978 
979  if (!instance || !instance->context)
980  return;
981 
982  PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
984  PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
986 
987  /* The remaining cleanup will be continued on main thread by complete_cleanup_on_main_thread() */
988 }
989 
991 {
992  TRACE_CALL(__func__);
993  DWORD status;
994  gchar buf[100];
995  rfContext *rfi = GET_PLUGIN_DATA(gp);
996 
997  while (!freerdp_shall_disconnect(rfi->instance)) {
998  HANDLE handles[64]={0};
999  DWORD nCount = freerdp_get_event_handles(rfi->instance->context, &handles[0], 64);
1000  if (rfi->event_handle)
1001  handles[nCount++] = rfi->event_handle;
1002 
1003  handles[nCount++] = rfi->instance->context->abortEvent;
1004 
1005  if (nCount == 0) {
1006  fprintf(stderr, "freerdp_get_event_handles failed\n");
1007  break;
1008  }
1009 
1010  status = WaitForMultipleObjects(nCount, handles, FALSE, 100);
1011 
1012  if (status == WAIT_FAILED) {
1013  fprintf(stderr, "WaitForMultipleObjects failed with %lu\n", (unsigned long)status);
1014  break;
1015  }
1016 
1017  if (rfi->event_handle && WaitForSingleObject(rfi->event_handle, 0) == WAIT_OBJECT_0) {
1018  if (!rf_process_event_queue(gp)) {
1019  fprintf(stderr, "Could not process local keyboard/mouse event queue\n");
1020  break;
1021  }
1022  if (read(rfi->event_pipe[0], buf, sizeof(buf))) {
1023  }
1024  }
1025 
1026  /* Check if a processed event called freerdp_abort_connect() and exit if true */
1027  if (WaitForSingleObject(rfi->instance->context->abortEvent, 0) == WAIT_OBJECT_0)
1028  /* Session disconnected by local user action */
1029  break;
1030 
1031  if (!freerdp_check_event_handles(rfi->instance->context)) {
1032  if (rf_auto_reconnect(rfi)) {
1033  /* Reset the possible reason/error which made us doing many reconnection reattempts and continue */
1034  remmina_plugin_service->protocol_plugin_set_error(gp, NULL);
1035  continue;
1036  }
1037  if (freerdp_get_last_error(rfi->instance->context) == FREERDP_ERROR_SUCCESS)
1038  fprintf(stderr, "Could not check FreeRDP file descriptor\n");
1039  break;
1040  }
1041  }
1042  const gchar *host = freerdp_settings_get_string (rfi->settings, FreeRDP_ServerHostname);
1043  // TRANSLATORS: the placeholder may be either an IP/FQDN or a server hostname
1044  REMMINA_PLUGIN_AUDIT(_("Disconnected from %s via RDP"), host);
1045  freerdp_disconnect(rfi->instance);
1046  REMMINA_PLUGIN_DEBUG("RDP client disconnected");
1047 }
1048 
1049 static int remmina_rdp_load_static_channel_addin(rdpChannels *channels, rdpSettings *settings, char *name, void *data)
1050 {
1051  TRACE_CALL(__func__);
1052  PVIRTUALCHANNELENTRY entry = NULL;
1053  PVIRTUALCHANNELENTRYEX entryEx = NULL;
1054 
1055  entryEx = (PVIRTUALCHANNELENTRYEX)(void *)freerdp_load_channel_addin_entry(
1056  name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC | FREERDP_ADDIN_CHANNEL_ENTRYEX);
1057 
1058  if (!entryEx)
1059  entry = freerdp_load_channel_addin_entry(name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC);
1060 
1061  if (entryEx) {
1062  if (freerdp_channels_client_load_ex(channels, settings, entryEx, data) == 0) {
1063  fprintf(stderr, "loading channel %s\n", name);
1064  return TRUE;
1065  }
1066  } else if (entry) {
1067  if (freerdp_channels_client_load(channels, settings, entry, data) == 0) {
1068  fprintf(stderr, "loading channel %s\n", name);
1069  return TRUE;
1070  }
1071  }
1072 
1073  return FALSE;
1074 }
1075 
1076 static gchar *remmina_rdp_find_prdriver(char *smap, char *prn)
1077 {
1078  char c, *p, *dr;
1079  int matching;
1080  size_t sz;
1081 
1082  enum { S_WAITPR,
1083  S_INPRINTER,
1084  S_WAITCOLON,
1085  S_WAITDRIVER,
1086  S_INDRIVER,
1087  S_WAITSEMICOLON } state = S_WAITPR;
1088 
1089  matching = 0;
1090  while ((c = *smap++) != 0) {
1091  switch (state) {
1092  case S_WAITPR:
1093  if (c != '\"') return NULL;
1094  state = S_INPRINTER;
1095  p = prn;
1096  matching = 1;
1097  break;
1098  case S_INPRINTER:
1099  if (matching && c == *p && *p != 0) {
1100  p++;
1101  } else if (c == '\"') {
1102  if (*p != 0)
1103  matching = 0;
1104  state = S_WAITCOLON;
1105  } else {
1106  matching = 0;
1107  }
1108  break;
1109  case S_WAITCOLON:
1110  if (c != ':')
1111  return NULL;
1112  state = S_WAITDRIVER;
1113  break;
1114  case S_WAITDRIVER:
1115  if (c != '\"')
1116  return NULL;
1117  state = S_INDRIVER;
1118  dr = smap;
1119  break;
1120  case S_INDRIVER:
1121  if (c == '\"') {
1122  if (matching)
1123  goto found;
1124  else
1125  state = S_WAITSEMICOLON;
1126  }
1127  break;
1128  case S_WAITSEMICOLON:
1129  if (c != ';')
1130  return NULL;
1131  state = S_WAITPR;
1132  break;
1133  }
1134  }
1135  return NULL;
1136 
1137 found:
1138  sz = smap - dr;
1139  p = (char *)malloc(sz);
1140  memcpy(p, dr, sz);
1141  p[sz - 1] = 0;
1142  return p;
1143 }
1144 
1145 #ifdef HAVE_CUPS
1146 
1151 static int remmina_rdp_set_printers(void *user_data, unsigned flags, cups_dest_t *dest)
1152 {
1153  rfContext *rfi = (rfContext *)user_data;
1155 
1170  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1171  const gchar *s = remmina_plugin_service->file_get_string(remminafile, "printer_overrides");
1172 
1173  RDPDR_PRINTER *printer;
1174  printer = (RDPDR_PRINTER *)calloc(1, sizeof(RDPDR_PRINTER));
1175 
1176 #ifdef WITH_FREERDP3
1177  RDPDR_DEVICE *pdev;
1178  pdev = &(printer->device);
1179 #else
1180  RDPDR_PRINTER *pdev;
1181  pdev = printer;
1182 #endif
1183 
1184  pdev->Type = RDPDR_DTYP_PRINT;
1185  REMMINA_PLUGIN_DEBUG("Printer Type: %d", pdev->Type);
1186 
1187  freerdp_settings_set_bool(rfi->settings, FreeRDP_RedirectPrinters, TRUE);
1188  freerdp_settings_set_bool(rfi->settings, FreeRDP_DeviceRedirection, TRUE);
1189 
1190  REMMINA_PLUGIN_DEBUG("Destination: %s", dest->name);
1191  if (!(pdev->Name = _strdup(dest->name))) {
1192  free(printer);
1193  return 1;
1194  }
1195 
1196  REMMINA_PLUGIN_DEBUG("Printer Name: %s", pdev->Name);
1197 
1198  if (s) {
1199  gchar *d = remmina_rdp_find_prdriver(strdup(s), pdev->Name);
1200  if (d) {
1201  printer->DriverName = strdup(d);
1202  REMMINA_PLUGIN_DEBUG("Printer DriverName set to: %s", printer->DriverName);
1203  g_free(d);
1204  } else {
1210  free(pdev->Name);
1211  free(printer);
1212  return 1;
1213  }
1214  } else {
1215  /* We set to a default driver*/
1216  printer->DriverName = _strdup("MS Publisher Imagesetter");
1217  }
1218 
1219  REMMINA_PLUGIN_DEBUG("Printer Driver: %s", printer->DriverName);
1220  if (!freerdp_device_collection_add(rfi->settings, (RDPDR_DEVICE *)printer)) {
1221  free(printer->DriverName);
1222  free(pdev->Name);
1223  free(printer);
1224  return 1;
1225  }
1226 
1227  return 1;
1228 }
1229 #endif /* HAVE_CUPS */
1230 
1231 /* Send Ctrl+Alt+Del keystrokes to the plugin drawing_area widget */
1233 {
1234  TRACE_CALL(__func__);
1235  guint keys[] = { GDK_KEY_Control_L, GDK_KEY_Alt_L, GDK_KEY_Delete };
1236  rfContext *rfi = GET_PLUGIN_DATA(gp);
1237 
1238  remmina_plugin_service->protocol_plugin_send_keys_signals(rfi->drawing_area,
1239  keys, G_N_ELEMENTS(keys), GDK_KEY_PRESS | GDK_KEY_RELEASE);
1240 }
1241 
1242 static gboolean remmina_rdp_set_connection_type(rdpSettings *settings, guint32 type)
1243 {
1244  freerdp_settings_set_uint32(settings, FreeRDP_ConnectionType, type);
1245 
1246  if (type == CONNECTION_TYPE_MODEM) {
1247  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, TRUE);
1248  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, FALSE);
1249  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, FALSE);
1250  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE);
1251  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE);
1252  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, TRUE);
1253  } else if (type == CONNECTION_TYPE_BROADBAND_LOW) {
1254  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, TRUE);
1255  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, FALSE);
1256  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, FALSE);
1257  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE);
1258  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE);
1259  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
1260  } else if (type == CONNECTION_TYPE_SATELLITE) {
1261  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, TRUE);
1262  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, FALSE);
1263  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
1264  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE);
1265  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE);
1266  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
1267  } else if (type == CONNECTION_TYPE_BROADBAND_HIGH) {
1268  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, TRUE);
1269  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, FALSE);
1270  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
1271  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE);
1272  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE);
1273  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
1274  } else if (type == CONNECTION_TYPE_WAN) {
1275  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, FALSE);
1276  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, TRUE);
1277  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
1278  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, FALSE);
1279  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, FALSE);
1280  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
1281  } else if (type == CONNECTION_TYPE_LAN) {
1282  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, FALSE);
1283  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, TRUE);
1284  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
1285  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, FALSE);
1286  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, FALSE);
1287  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
1288  } else if (type == CONNECTION_TYPE_AUTODETECT) {
1289  freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, FALSE);
1290  freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, TRUE);
1291  freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
1292  freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, FALSE);
1293  freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, FALSE);
1294  freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
1295  freerdp_settings_set_bool(settings, FreeRDP_NetworkAutoDetect, TRUE);
1296 
1297  /* Automatically activate GFX and RFX codec support */
1298 #ifdef WITH_GFX_H264
1299  freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, gfx_h264_available);
1300  freerdp_settings_set_bool(settings, FreeRDP_GfxH264, gfx_h264_available);
1301 #endif
1302  freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE);
1303  freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline, TRUE);
1304  } else if (type == REMMINA_CONNECTION_TYPE_NONE) {
1305  return FALSE;
1306  } else {
1307  return FALSE;
1308  }
1309 
1310  return TRUE;
1311 }
1312 
1313 #ifdef GDK_WINDOWING_X11
1314 #if FREERDP_CHECK_VERSION(2, 3, 0)
1315 static gchar *remmina_get_rdp_kbd_remap(const gchar *keymap)
1316 {
1317  TRACE_CALL(__func__);
1318  guint *table;
1319  gchar keys[20];
1320  gchar *rdp_kbd_remap = NULL;
1321  gint i;
1322  Display *display;
1323 
1324  table = remmina_plugin_service->pref_keymap_get_table(keymap);
1325  if (!table)
1326  return rdp_kbd_remap;
1327  rdp_kbd_remap = g_malloc0(512);
1328  display = XOpenDisplay(0);
1329  for (i = 0; table[i] > 0; i += 2) {
1330  g_snprintf(keys, sizeof(keys), "0x%02x=0x%02x", freerdp_keyboard_get_rdp_scancode_from_x11_keycode(XKeysymToKeycode(display, table[i])),
1331  freerdp_keyboard_get_rdp_scancode_from_x11_keycode(XKeysymToKeycode(display, table[i + 1])));
1332  if (i > 0)
1333  g_strlcat(rdp_kbd_remap, ",", 512);
1334  g_strlcat(rdp_kbd_remap, keys, 512);
1335  }
1336  XCloseDisplay(display);
1337 
1338  return rdp_kbd_remap;
1339 }
1340 #endif
1341 #endif
1342 
1344 {
1345  TRACE_CALL(__func__);
1346  const gchar *s;
1347  gchar *sm;
1348  gchar *value;
1349  const gchar *cs;
1350  RemminaFile *remminafile;
1351  rfContext *rfi = GET_PLUGIN_DATA(gp);
1352  rdpChannels *channels;
1353  gchar *gateway_host;
1354  gint gateway_port;
1355  gchar *datapath = NULL;
1356  gboolean status = TRUE;
1357 #ifdef GDK_WINDOWING_X11
1358  gchar *rdp_kbd_remap;
1359 #endif
1360  gint i;
1361 
1362  gint desktopOrientation, desktopScaleFactor, deviceScaleFactor;
1363 
1364  channels = rfi->instance->context->channels;
1365 
1366  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1367 
1368  datapath = g_build_path("/",
1369  remmina_plugin_service->file_get_user_datadir(),
1370  "RDP",
1371  NULL);
1372  REMMINA_PLUGIN_DEBUG("RDP data path is %s", datapath);
1373 
1374  if ((datapath != NULL) && (datapath[0] != '\0'))
1375  if (access(datapath, W_OK) == 0)
1376  freerdp_settings_set_string(rfi->settings, FreeRDP_ConfigPath, datapath);
1377  g_free(datapath);
1378 
1379  if (remmina_plugin_service->file_get_int(remminafile, "assistance_mode", 0)){
1380  rdpAssistanceFile* file = freerdp_assistance_file_new();
1381  if (!file){
1382  REMMINA_PLUGIN_DEBUG("Could not allocate assistance file structure");
1383  return FALSE;
1384  }
1385 
1386  if (remmina_plugin_service->file_get_string(remminafile, "assistance_file") == NULL ||
1387  remmina_plugin_service->file_get_string(remminafile, "assistance_pass") == NULL ){
1388 
1389  REMMINA_PLUGIN_DEBUG("Assistance file and password are not set while assistance mode is on");
1390  return FALSE;
1391  }
1392 
1393  status = freerdp_assistance_parse_file(file,
1394  remmina_plugin_service->file_get_string(remminafile, "assistance_file"),
1395  remmina_plugin_service->file_get_string(remminafile, "assistance_pass"));
1396 
1397  if (status < 0){
1398  REMMINA_PLUGIN_DEBUG("Could not parse assistance file");
1399  return FALSE;
1400  }
1401 
1402 
1403  if (!freerdp_assistance_populate_settings_from_assistance_file(file, rfi->settings)){
1404  REMMINA_PLUGIN_DEBUG("Could not populate settings from assistance file");
1405  return FALSE;
1406  }
1407  }
1408 
1409 
1410 #if defined(PROXY_TYPE_IGNORE)
1411  if (!remmina_plugin_service->file_get_int(remminafile, "useproxyenv", FALSE) ? TRUE : FALSE) {
1412  REMMINA_PLUGIN_DEBUG("Not using system proxy settings");
1413  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ProxyType, PROXY_TYPE_IGNORE);
1414  }
1415 #endif
1416 
1417  if (!remmina_rdp_tunnel_init(gp))
1418  return FALSE;
1419 
1420  freerdp_settings_set_bool(rfi->settings, FreeRDP_AutoReconnectionEnabled, (remmina_plugin_service->file_get_int(remminafile, "disableautoreconnect", FALSE) ? FALSE : TRUE));
1421  /* Disable RDP auto reconnection when SSH tunnel is enabled */
1422  if (remmina_plugin_service->file_get_int(remminafile, "ssh_tunnel_enabled", FALSE))
1423  freerdp_settings_set_bool(rfi->settings, FreeRDP_AutoReconnectionEnabled, FALSE);
1424 
1425  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, remmina_plugin_service->file_get_int(remminafile, "colordepth", 99));
1426 
1427  freerdp_settings_set_bool(rfi->settings, FreeRDP_SoftwareGdi, TRUE);
1428  REMMINA_PLUGIN_DEBUG("gfx_h264_available: %d", gfx_h264_available);
1429 
1430  /* Avoid using H.264 modes if they are not available on libfreerdp */
1431  if (!gfx_h264_available && (freerdp_settings_get_bool(rfi->settings, FreeRDP_ColorDepth) == 65 || freerdp_settings_get_bool(rfi->settings, FreeRDP_ColorDepth == 66)))
1432  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 64); // Fallback to GFX RFX
1433 
1434  if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth) == 0) {
1435  /* RFX (Win7)*/
1436  freerdp_settings_set_bool(rfi->settings, FreeRDP_RemoteFxCodec, TRUE);
1437  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline, FALSE);
1438  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1439  } else if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth) == 63) {
1440  /* /gfx (RFX Progressive) (Win8) */
1441  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1442  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline, TRUE);
1443  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxH264, FALSE);
1444  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxAVC444, FALSE);
1445  } else if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth) == 64) {
1446  /* /gfx:rfx (Win8) */
1447  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1448  freerdp_settings_set_bool(rfi->settings, FreeRDP_RemoteFxCodec, TRUE);
1449  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline, TRUE);
1450  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxH264, FALSE);
1451  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxAVC444, FALSE);
1452  } else if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth) == 65) {
1453  /* /gfx:avc420 (Win8.1) */
1454  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1455  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline, TRUE);
1456  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxH264, gfx_h264_available);
1457  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxAVC444, FALSE);
1458  } else if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth) == 66) {
1459  /* /gfx:avc444 (Win10) */
1460  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1461  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline, TRUE);
1462  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxH264, gfx_h264_available);
1463  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxAVC444, gfx_h264_available);
1464  } else if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_ColorDepth) == 99) {
1465  /* Automatic (Let the server choose its best format) */
1466  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1467  freerdp_settings_set_bool(rfi->settings, FreeRDP_RemoteFxCodec, TRUE);
1468  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline, TRUE);
1469  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxH264, gfx_h264_available);
1470  freerdp_settings_set_bool(rfi->settings, FreeRDP_GfxAVC444, gfx_h264_available);
1471  }
1472 
1473  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_RemoteFxCodec) ||
1474  freerdp_settings_get_bool(rfi->settings, FreeRDP_NSCodec) ||
1475  freerdp_settings_get_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline)) {
1476  freerdp_settings_set_bool(rfi->settings, FreeRDP_FastPathOutput, TRUE);
1477  freerdp_settings_set_bool(rfi->settings, FreeRDP_FrameMarkerCommandEnabled, TRUE);
1478  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ColorDepth, 32);
1479  rfi->bpp = 32;
1480  }
1481 
1482  gint w = remmina_plugin_service->get_profile_remote_width(gp);
1483  gint h = remmina_plugin_service->get_profile_remote_height(gp);
1484  /* multiple of 4 */
1485  w = (w + 3) & ~0x3;
1486  h = (h + 3) & ~0x3;
1487  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopWidth, w);
1488  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopHeight, h);
1489  REMMINA_PLUGIN_DEBUG("Resolution set by the user: %dx%d", w, h);
1490 
1491  /* Workaround for FreeRDP issue #5417: in GFX AVC modes we can't go under
1492  * AVC_MIN_DESKTOP_WIDTH x AVC_MIN_DESKTOP_HEIGHT */
1493  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline) &&
1494  freerdp_settings_get_bool(rfi->settings, FreeRDP_GfxH264)) {
1495  if (freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth) <
1496  AVC_MIN_DESKTOP_WIDTH)
1497  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopWidth,
1498  AVC_MIN_DESKTOP_WIDTH);
1499  if (freerdp_settings_get_uint32(rfi->settings,
1500  FreeRDP_DesktopHeight) <
1501  AVC_MIN_DESKTOP_HEIGHT)
1502  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopHeight,
1503  AVC_MIN_DESKTOP_HEIGHT);
1504  }
1505 
1506  /* Workaround for FreeRDP issue #5119. This will make our horizontal resolution
1507  * an even value, but it will add a vertical black 1 pixel line on the
1508  * right of the desktop */
1509  if ((freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth) & 1) != 0) {
1510  UINT32 tmp = freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth);
1511  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopWidth, tmp - 1);
1512  }
1513 
1514  remmina_plugin_service->protocol_plugin_set_width(gp, freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth));
1515  remmina_plugin_service->protocol_plugin_set_height(gp, freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopHeight));
1516 
1517  w = freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth);
1518  h = freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopHeight);
1519  REMMINA_PLUGIN_DEBUG("Resolution set after workarounds: %dx%d", w, h);
1520 
1521 
1522  if (remmina_plugin_service->file_get_string(remminafile, "username"))
1523  freerdp_settings_set_string(rfi->settings, FreeRDP_Username, remmina_plugin_service->file_get_string(remminafile, "username"));
1524 
1525  if (remmina_plugin_service->file_get_string(remminafile, "domain"))
1526  freerdp_settings_set_string(rfi->settings, FreeRDP_Domain, remmina_plugin_service->file_get_string(remminafile, "domain"));
1527 
1528  s = remmina_plugin_service->file_get_string(remminafile, "password");
1529  if (s) freerdp_settings_set_string(rfi->settings, FreeRDP_Password, s);
1530 
1531  freerdp_settings_set_bool(rfi->settings, FreeRDP_AutoLogonEnabled, TRUE);
1532 
1537  gchar *proxy_type = g_strdup(remmina_plugin_service->file_get_string(remminafile, "proxy_type"));
1538  gchar *proxy_username = g_strdup(remmina_plugin_service->file_get_string(remminafile, "proxy_username"));
1539  gchar *proxy_password = g_strdup(remmina_plugin_service->file_get_string(remminafile, "proxy_password"));
1540  gchar *proxy_hostname = g_strdup(remmina_plugin_service->file_get_string(remminafile, "proxy_hostname"));
1541  gint proxy_port = remmina_plugin_service->file_get_int(remminafile, "proxy_port", 80);
1542  REMMINA_PLUGIN_DEBUG("proxy_type: %s", proxy_type);
1543  REMMINA_PLUGIN_DEBUG("proxy_username: %s", proxy_username);
1544  REMMINA_PLUGIN_DEBUG("proxy_password: %s", proxy_password);
1545  REMMINA_PLUGIN_DEBUG("proxy_hostname: %s", proxy_hostname);
1546  REMMINA_PLUGIN_DEBUG("proxy_port: %d", proxy_port);
1547  if (proxy_type && proxy_hostname) {
1548  if (g_strcmp0(proxy_type, "no_proxy") == 0)
1549  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ProxyType, PROXY_TYPE_IGNORE);
1550  else if (g_strcmp0(proxy_type, "http") == 0)
1551  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP);
1552  else if (g_strcmp0(proxy_type, "socks5") == 0)
1553  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ProxyType, PROXY_TYPE_SOCKS);
1554  else
1555  g_warning("Invalid proxy protocol, at the moment only no_proxy, HTTP and SOCKS5 are supported");
1556  REMMINA_PLUGIN_DEBUG("ProxyType set to: %" PRIu32, freerdp_settings_get_uint32(rfi->settings, FreeRDP_ProxyType));
1557  freerdp_settings_set_string(rfi->settings, FreeRDP_ProxyHostname, proxy_hostname);
1558  if (proxy_username)
1559  freerdp_settings_set_string(rfi->settings, FreeRDP_ProxyUsername, proxy_username);
1560  if (proxy_password)
1561  freerdp_settings_set_string(rfi->settings, FreeRDP_ProxyPassword, proxy_password);
1562  if (proxy_port)
1563  freerdp_settings_set_uint16(rfi->settings, FreeRDP_ProxyPort, proxy_port);
1564  }
1565  g_free(proxy_hostname);
1566  g_free(proxy_username);
1567  g_free(proxy_password);
1568 
1569  if (remmina_plugin_service->file_get_int(remminafile, "base-cred-for-gw", FALSE)) {
1570  // Reset gateway credentials
1571  remmina_plugin_service->file_set_string(remminafile, "gateway_username", NULL);
1572  remmina_plugin_service->file_set_string(remminafile, "gateway_domain", NULL);
1573  remmina_plugin_service->file_set_string(remminafile, "gateway_password", NULL);
1574  }
1575 
1576  /* Remote Desktop Gateway server address */
1577  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayEnabled, FALSE);
1578  s = remmina_plugin_service->file_get_string(remminafile, "gateway_server");
1579  if (s) {
1580  cs = remmina_plugin_service->file_get_string(remminafile, "gwtransp");
1581 #if FREERDP_CHECK_VERSION(2, 3, 1)
1582  if (remmina_plugin_service->file_get_int(remminafile, "websockets", FALSE))
1583  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayHttpUseWebsockets, TRUE);
1584  else
1585  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayHttpUseWebsockets, FALSE);
1586 #endif
1587  if (g_strcmp0(cs, "http") == 0) {
1588  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayRpcTransport, FALSE);
1589  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayHttpTransport, TRUE);
1590  } else if (g_strcmp0(cs, "rpc") == 0) {
1591  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayRpcTransport, TRUE);
1592  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayHttpTransport, FALSE);
1593  } else if (g_strcmp0(cs, "auto") == 0) {
1594  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayRpcTransport, TRUE);
1595  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayHttpTransport, TRUE);
1596  }
1597  remmina_plugin_service->get_server_port(s, 443, &gateway_host, &gateway_port);
1598  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayHostname, gateway_host);
1599  freerdp_settings_set_uint32(rfi->settings, FreeRDP_GatewayPort, gateway_port);
1600  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayEnabled, TRUE);
1601  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayUseSameCredentials, TRUE);
1602  }
1603  /* Remote Desktop Gateway domain */
1604  if (remmina_plugin_service->file_get_string(remminafile, "gateway_domain")) {
1605  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayDomain, remmina_plugin_service->file_get_string(remminafile, "gateway_domain"));
1606  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayUseSameCredentials, FALSE);
1607  }
1608  /* Remote Desktop Gateway username */
1609  if (remmina_plugin_service->file_get_string(remminafile, "gateway_username")) {
1610  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayUsername, remmina_plugin_service->file_get_string(remminafile, "gateway_username"));
1611  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayUseSameCredentials, FALSE);
1612  }
1613  /* Remote Desktop Gateway password */
1614  s = remmina_plugin_service->file_get_string(remminafile, "gateway_password");
1615  if (s) {
1616  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayPassword, s);
1617  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayUseSameCredentials, FALSE);
1618  }
1619  /* If no different credentials were provided for the Remote Desktop Gateway
1620  * use the same authentication credentials for the host */
1621  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_GatewayEnabled) && freerdp_settings_get_bool(rfi->settings, FreeRDP_GatewayUseSameCredentials)) {
1622  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayDomain, freerdp_settings_get_string(rfi->settings, FreeRDP_Domain));
1623  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayUsername, freerdp_settings_get_string(rfi->settings, FreeRDP_Username));
1624  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayPassword, freerdp_settings_get_string(rfi->settings, FreeRDP_Password));
1625  }
1626  /* Remote Desktop Gateway usage */
1627  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_GatewayEnabled))
1628  freerdp_set_gateway_usage_method(rfi->settings,
1629  remmina_plugin_service->file_get_int(remminafile, "gateway_usage", FALSE) ? TSC_PROXY_MODE_DETECT : TSC_PROXY_MODE_DIRECT);
1630 
1631  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayAccessToken,
1632  remmina_plugin_service->file_get_string(remminafile, "gatewayaccesstoken"));
1633 
1634  freerdp_settings_set_uint32(rfi->settings, FreeRDP_AuthenticationLevel, remmina_plugin_service->file_get_int(
1635  remminafile, "authentication level", freerdp_settings_get_uint32(rfi->settings, FreeRDP_AuthenticationLevel)));
1636 
1637  /* Certificate ignore */
1638  freerdp_settings_set_bool(rfi->settings, FreeRDP_IgnoreCertificate, remmina_plugin_service->file_get_int(remminafile, "cert_ignore", 0));
1639  freerdp_settings_set_bool(rfi->settings, FreeRDP_OldLicenseBehaviour, remmina_plugin_service->file_get_int(remminafile, "old-license", 0));
1640  freerdp_settings_set_bool(rfi->settings, FreeRDP_AllowUnanouncedOrdersFromServer, remmina_plugin_service->file_get_int(remminafile, "relax-order-checks", 0));
1641  freerdp_settings_set_uint32(rfi->settings, FreeRDP_GlyphSupportLevel, (remmina_plugin_service->file_get_int(remminafile, "glyph-cache", 0) ? GLYPH_SUPPORT_FULL : GLYPH_SUPPORT_NONE));
1642 
1643  if ((cs = remmina_plugin_service->file_get_string(remminafile, "clientname")))
1644  freerdp_settings_set_string(rfi->settings, FreeRDP_ClientHostname, cs);
1645  else
1646  freerdp_settings_set_string(rfi->settings, FreeRDP_ClientHostname, g_get_host_name());
1647 
1648  /* Client Build number is optional, if not specified defaults to 0, allow for comments to appear after number */
1649  if ((cs = remmina_plugin_service->file_get_string(remminafile, "clientbuild"))) {
1650  if (*cs) {
1651  UINT32 val = strtoul(cs, NULL, 0);
1652  freerdp_settings_set_uint32(rfi->settings, FreeRDP_ClientBuild, val);
1653  }
1654  }
1655 
1656 
1657  if (remmina_plugin_service->file_get_string(remminafile, "loadbalanceinfo")) {
1658  char *tmp = strdup(remmina_plugin_service->file_get_string(remminafile, "loadbalanceinfo"));
1659  rfi->settings->LoadBalanceInfo = (BYTE *)tmp;
1660 
1661  freerdp_settings_set_uint32(rfi->settings, FreeRDP_LoadBalanceInfoLength, strlen(tmp));
1662  }
1663 
1664  if (remmina_plugin_service->file_get_string(remminafile, "exec"))
1665  freerdp_settings_set_string(rfi->settings, FreeRDP_AlternateShell, remmina_plugin_service->file_get_string(remminafile, "exec"));
1666 
1667  if (remmina_plugin_service->file_get_string(remminafile, "execpath"))
1668  freerdp_settings_set_string(rfi->settings, FreeRDP_ShellWorkingDirectory, remmina_plugin_service->file_get_string(remminafile, "execpath"));
1669 
1670  sm = g_strdup_printf("rdp_quality_%i", remmina_plugin_service->file_get_int(remminafile, "quality", DEFAULT_QUALITY_0));
1671  value = remmina_plugin_service->pref_get_value(sm);
1672  g_free(sm);
1673 
1674  if (value && value[0]) {
1675  freerdp_settings_set_uint32(rfi->settings, FreeRDP_PerformanceFlags, strtoul(value, NULL, 16));
1676  } else {
1677  switch (remmina_plugin_service->file_get_int(remminafile, "quality", DEFAULT_QUALITY_0)) {
1678  case 9:
1679  freerdp_settings_set_uint32(rfi->settings, FreeRDP_PerformanceFlags, DEFAULT_QUALITY_9);
1680  break;
1681 
1682  case 2:
1683  freerdp_settings_set_uint32(rfi->settings, FreeRDP_PerformanceFlags, DEFAULT_QUALITY_2);
1684  break;
1685 
1686  case 1:
1687  freerdp_settings_set_uint32(rfi->settings, FreeRDP_PerformanceFlags, DEFAULT_QUALITY_1);
1688  break;
1689 
1690  case 0:
1691  default:
1692  freerdp_settings_set_uint32(rfi->settings, FreeRDP_PerformanceFlags, DEFAULT_QUALITY_0);
1693  break;
1694  }
1695  }
1696  g_free(value);
1697 
1698  if ((cs = remmina_plugin_service->file_get_string(remminafile, "network"))) {
1699  guint32 type = 0;
1700 
1701  if (g_strcmp0(cs, "modem") == 0)
1702  type = CONNECTION_TYPE_MODEM;
1703  else if (g_strcmp0(cs, "broadband") == 0)
1704  type = CONNECTION_TYPE_BROADBAND_HIGH;
1705  else if (g_strcmp0(cs, "broadband-low") == 0)
1706  type = CONNECTION_TYPE_BROADBAND_LOW;
1707  else if (g_strcmp0(cs, "broadband-high") == 0)
1708  type = CONNECTION_TYPE_BROADBAND_HIGH;
1709  else if (g_strcmp0(cs, "wan") == 0)
1710  type = CONNECTION_TYPE_WAN;
1711  else if (g_strcmp0(cs, "lan") == 0)
1712  type = CONNECTION_TYPE_LAN;
1713  else if ((g_strcmp0(cs, "autodetect") == 0))
1714  type = CONNECTION_TYPE_AUTODETECT;
1715  else if ((g_strcmp0(cs, "none") == 0))
1716  type = REMMINA_CONNECTION_TYPE_NONE;
1717  else
1718  type = REMMINA_CONNECTION_TYPE_NONE;
1719 
1720  if (!remmina_rdp_set_connection_type(rfi->settings, type))
1721  REMMINA_PLUGIN_DEBUG("Network settings not set");
1722  }
1723 
1724  /* PerformanceFlags bitmask need also to be splitted into BOOL variables
1725  * like freerdp_settings_set_bool(rfi->settings, FreeRDP_DisableWallpaper, freerdp_settings_set_bool(rfi->settings, FreeRDP_AllowFontSmoothing…
1726  * or freerdp_get_param_bool() function will return the wrong value
1727  */
1728  freerdp_performance_flags_split(rfi->settings);
1729 
1730 #ifdef GDK_WINDOWING_X11
1731 #if FREERDP_CHECK_VERSION(2, 3, 0)
1732  rdp_kbd_remap = remmina_get_rdp_kbd_remap(remmina_plugin_service->file_get_string(remminafile, "keymap"));
1733  if (rdp_kbd_remap != NULL) {
1734  freerdp_settings_set_string(rfi->settings, FreeRDP_KeyboardRemappingList, rdp_kbd_remap);
1735  REMMINA_PLUGIN_DEBUG("rdp_keyboard_remapping_list: %s", rfi->settings->KeyboardRemappingList);
1736  g_free(rdp_kbd_remap);
1737  }
1738  else {
1739  freerdp_settings_set_string(rfi->settings, FreeRDP_KeyboardRemappingList, remmina_plugin_service->pref_get_value("rdp_kbd_remap"));
1740  REMMINA_PLUGIN_DEBUG("rdp_keyboard_remapping_list: %s", rfi->settings->KeyboardRemappingList);
1741  }
1742 #endif
1743 #endif
1744 
1745  freerdp_settings_set_uint32(rfi->settings, FreeRDP_KeyboardLayout, remmina_rdp_settings_get_keyboard_layout());
1746 
1747  if (remmina_plugin_service->file_get_int(remminafile, "console", FALSE))
1748  freerdp_settings_set_bool(rfi->settings, FreeRDP_ConsoleSession, TRUE);
1749 
1750  if (remmina_plugin_service->file_get_int(remminafile, "restricted-admin", FALSE)) {
1751  freerdp_settings_set_bool(rfi->settings, FreeRDP_ConsoleSession, TRUE);
1752  freerdp_settings_set_bool(rfi->settings, FreeRDP_RestrictedAdminModeRequired, TRUE);
1753  }
1754 
1755  if (remmina_plugin_service->file_get_string(remminafile, "pth")) {
1756  freerdp_settings_set_bool(rfi->settings, FreeRDP_ConsoleSession, TRUE);
1757  freerdp_settings_set_bool(rfi->settings, FreeRDP_RestrictedAdminModeRequired, TRUE);
1758  freerdp_settings_set_string(rfi->settings, FreeRDP_PasswordHash, remmina_plugin_service->file_get_string(remminafile, "pth"));
1759  remmina_plugin_service->file_set_int(remminafile, "restricted-admin", TRUE);
1760  }
1761 
1762  cs = remmina_plugin_service->file_get_string(remminafile, "security");
1763  if (g_strcmp0(cs, "rdp") == 0) {
1764  freerdp_settings_set_bool(rfi->settings, FreeRDP_RdpSecurity, TRUE);
1765  freerdp_settings_set_bool(rfi->settings, FreeRDP_TlsSecurity, FALSE);
1766  freerdp_settings_set_bool(rfi->settings, FreeRDP_NlaSecurity, FALSE);
1767  freerdp_settings_set_bool(rfi->settings, FreeRDP_ExtSecurity, FALSE);
1768  freerdp_settings_set_bool(rfi->settings, FreeRDP_UseRdpSecurityLayer, TRUE);
1769  } else if (g_strcmp0(cs, "tls") == 0) {
1770  freerdp_settings_set_bool(rfi->settings, FreeRDP_RdpSecurity, FALSE);
1771  freerdp_settings_set_bool(rfi->settings, FreeRDP_TlsSecurity, TRUE);
1772  freerdp_settings_set_bool(rfi->settings, FreeRDP_NlaSecurity, FALSE);
1773  freerdp_settings_set_bool(rfi->settings, FreeRDP_ExtSecurity, FALSE);
1774  } else if (g_strcmp0(cs, "nla") == 0) {
1775  freerdp_settings_set_bool(rfi->settings, FreeRDP_RdpSecurity, FALSE);
1776  freerdp_settings_set_bool(rfi->settings, FreeRDP_TlsSecurity, FALSE);
1777  freerdp_settings_set_bool(rfi->settings, FreeRDP_NlaSecurity, TRUE);
1778  freerdp_settings_set_bool(rfi->settings, FreeRDP_ExtSecurity, FALSE);
1779  } else if (g_strcmp0(cs, "ext") == 0) {
1780  freerdp_settings_set_bool(rfi->settings, FreeRDP_RdpSecurity, FALSE);
1781  freerdp_settings_set_bool(rfi->settings, FreeRDP_TlsSecurity, FALSE);
1782  freerdp_settings_set_bool(rfi->settings, FreeRDP_NlaSecurity, FALSE);
1783  freerdp_settings_set_bool(rfi->settings, FreeRDP_ExtSecurity, TRUE);
1784  } else {
1785  /* This is "-nego" switch of xfreerdp */
1786  freerdp_settings_set_bool(rfi->settings, FreeRDP_NegotiateSecurityLayer, TRUE);
1787  }
1788 
1789  cs = remmina_plugin_service->file_get_string(remminafile, "tls-seclevel");
1790  if (cs && g_strcmp0(cs,"")!=0) {
1791  i = atoi(cs);
1792  freerdp_settings_set_uint32(rfi->settings, FreeRDP_TlsSecLevel, i);
1793  }
1794 
1795  freerdp_settings_set_bool(rfi->settings, FreeRDP_CompressionEnabled, TRUE);
1796  if (remmina_plugin_service->file_get_int(remminafile, "disable_fastpath", FALSE)) {
1797  freerdp_settings_set_bool(rfi->settings, FreeRDP_FastPathInput, FALSE);
1798  freerdp_settings_set_bool(rfi->settings, FreeRDP_FastPathOutput, FALSE);
1799  } else {
1800  freerdp_settings_set_bool(rfi->settings, FreeRDP_FastPathInput, TRUE);
1801  freerdp_settings_set_bool(rfi->settings, FreeRDP_FastPathOutput, TRUE);
1802  }
1803 
1804  /* Orientation and scaling settings */
1805  remmina_rdp_settings_get_orientation_scale_prefs(&desktopOrientation, &desktopScaleFactor, &deviceScaleFactor);
1806 
1807  freerdp_settings_set_uint16(rfi->settings, FreeRDP_DesktopOrientation, desktopOrientation);
1808  if (desktopScaleFactor != 0 && deviceScaleFactor != 0) {
1809  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopScaleFactor, desktopScaleFactor);
1810  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DeviceScaleFactor, deviceScaleFactor);
1811  }
1812 
1813  /* Try to enable "Display Control Virtual Channel Extension", needed to
1814  * dynamically resize remote desktop. This will automatically open
1815  * the "disp" dynamic channel, if available */
1816  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportDisplayControl, TRUE);
1817 
1818  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_SupportDisplayControl)) {
1819  CLPARAM *d[1];
1820  int dcount;
1821 
1822  dcount = 1;
1823  d[0] = "disp";
1824  freerdp_client_add_dynamic_channel(rfi->settings, dcount, d);
1825  }
1826 
1827  if (freerdp_settings_get_bool(rfi->settings, FreeRDP_SupportGraphicsPipeline)) {
1828  CLPARAM *d[1];
1829 
1830  int dcount;
1831 
1832  dcount = 1;
1833  d[0] = "rdpgfx";
1834  freerdp_client_add_dynamic_channel(rfi->settings, dcount, d);
1835  }
1836 
1837  /* Sound settings */
1838  cs = remmina_plugin_service->file_get_string(remminafile, "sound");
1839  if (g_strcmp0(cs, "remote") == 0) {
1840  freerdp_settings_set_bool(rfi->settings, FreeRDP_RemoteConsoleAudio, TRUE);
1841  } else if (g_str_has_prefix(cs, "local")) {
1842  freerdp_settings_set_bool(rfi->settings, FreeRDP_AudioPlayback, TRUE);
1843  freerdp_settings_set_bool(rfi->settings, FreeRDP_AudioCapture, TRUE);
1844  } else {
1845  /* Disable sound */
1846  freerdp_settings_set_bool(rfi->settings, FreeRDP_AudioPlayback, FALSE);
1847  freerdp_settings_set_bool(rfi->settings, FreeRDP_RemoteConsoleAudio, FALSE);
1848  }
1849 
1850  cs = remmina_plugin_service->file_get_string(remminafile, "microphone");
1851  if (cs != NULL && cs[0] != '\0') {
1852  if (g_strcmp0(cs, "0") == 0) {
1853  REMMINA_PLUGIN_DEBUG("“microphone” was set to 0, setting to \"\"");
1854  remmina_plugin_service->file_set_string(remminafile, "microphone", "");
1855  } else {
1856  freerdp_settings_set_bool(rfi->settings, FreeRDP_AudioCapture, TRUE);
1857  REMMINA_PLUGIN_DEBUG("“microphone” set to “%s”", cs);
1858  CLPARAM **p;
1859  size_t count;
1860 
1861  p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("audin", g_strdup(cs), &count);
1862 
1863  freerdp_client_add_dynamic_channel(rfi->settings, count, p);
1864  g_free(p);
1865  }
1866  }
1867 
1868  cs = remmina_plugin_service->file_get_string(remminafile, "audio-output");
1869  if (cs != NULL && cs[0] != '\0') {
1870  REMMINA_PLUGIN_DEBUG("audio output set to %s", cs);
1871  CLPARAM **p;
1872  size_t count;
1873 
1874  p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("rdpsnd", g_strdup(cs), &count);
1875  status = freerdp_client_add_static_channel(rfi->settings, count, p);
1876  if (status)
1877  status = freerdp_client_add_dynamic_channel(rfi->settings, count, p);
1878  g_free(p);
1879  }
1880 
1881 
1882  cs = remmina_plugin_service->file_get_string(remminafile, "freerdp_log_level");
1883  if (cs != NULL && cs[0] != '\0')
1884  REMMINA_PLUGIN_DEBUG("Log level set to to %s", cs);
1885  else
1886  cs = g_strdup("INFO");
1887  wLog *root = WLog_GetRoot();
1888  WLog_SetStringLogLevel(root, cs);
1889 
1890  cs = remmina_plugin_service->file_get_string(remminafile, "freerdp_log_filters");
1891  if (cs != NULL && cs[0] != '\0') {
1892  REMMINA_PLUGIN_DEBUG("Log filters set to to %s", cs);
1893  WLog_AddStringLogFilters(cs);
1894  } else {
1895  WLog_AddStringLogFilters(NULL);
1896  }
1897 
1898 
1899  cs = remmina_plugin_service->file_get_string(remminafile, "usb");
1900  if (cs != NULL && cs[0] != '\0') {
1901  CLPARAM **p;
1902  size_t count;
1903  p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("urbdrc", g_strdup(cs), &count);
1904  freerdp_client_add_dynamic_channel(rfi->settings, count, p);
1905  g_free(p);
1906  }
1907 
1908  cs = remmina_plugin_service->file_get_string(remminafile, "vc");
1909  if (cs != NULL && cs[0] != '\0') {
1910  CLPARAM **p;
1911  size_t count;
1912  p = remmina_rdp_CommandLineParseCommaSeparatedValues(g_strdup(cs), &count);
1913  freerdp_client_add_static_channel(rfi->settings, count, p);
1914  g_free(p);
1915  }
1916 
1917  cs = remmina_plugin_service->file_get_string(remminafile, "dvc");
1918  if (cs != NULL && cs[0] != '\0') {
1919  CLPARAM **p;
1920  size_t count;
1921  p = remmina_rdp_CommandLineParseCommaSeparatedValues(g_strdup(cs), &count);
1922  freerdp_client_add_dynamic_channel(rfi->settings, count, p);
1923  g_free(p);
1924  }
1925 
1926  cs = remmina_plugin_service->file_get_string(remminafile, "rdp2tcp");
1927  if (cs != NULL && cs[0] != '\0') {
1928  g_free(rfi->settings->RDP2TCPArgs);
1929  rfi->settings->RDP2TCPArgs = g_strdup(cs);
1930  REMMINA_PLUGIN_DEBUG("rdp2tcp set to %s", rfi->settings->RDP2TCPArgs);
1931  remmina_rdp_load_static_channel_addin(channels, rfi->settings, "rdp2tcp", rfi->settings->RDP2TCPArgs);
1932  }
1933 
1934  int vermaj, vermin, verrev;
1935  freerdp_get_version(&vermaj, &vermin, &verrev);
1936 
1937 #if FREERDP_CHECK_VERSION(2, 1, 0)
1938  cs = remmina_plugin_service->file_get_string(remminafile, "timeout");
1939  if (cs != NULL && cs[0] != '\0') {
1940  const gchar *endptr = NULL;
1941  guint64 val = g_ascii_strtoull(cs, (gchar **)&endptr, 10);
1942  if (val > 600000 || val <= 0)
1943  val = 600000;
1944  freerdp_settings_set_uint32(rfi->settings, FreeRDP_TcpAckTimeout, (UINT32)val);
1945  }
1946 #endif
1947 
1948  if (remmina_plugin_service->file_get_int(remminafile, "preferipv6", FALSE) ? TRUE : FALSE)
1949  freerdp_settings_set_bool(rfi->settings, FreeRDP_PreferIPv6OverIPv4, TRUE);
1950 
1951  freerdp_settings_set_bool(rfi->settings, FreeRDP_RedirectClipboard, remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE) ? FALSE : TRUE);
1952 
1953  cs = remmina_plugin_service->file_get_string(remminafile, "sharefolder");
1954  if (cs != NULL && cs[0] != '\0') {
1955  gchar *ccs = g_strdup(cs);
1956  REMMINA_PLUGIN_DEBUG("[Deprecated->migrating] - Old sharefolder %s to \"drive \"", ccs);
1957  if (!remmina_plugin_service->file_get_string(remminafile, "drive")) {
1958  remmina_plugin_service->file_set_string(remminafile, "drive", g_strdup(ccs));
1959  remmina_plugin_service->file_set_string(remminafile, "sharefolder", NULL);
1960  REMMINA_PLUGIN_DEBUG("[Deprecated->migrated] - drive set to %s", g_strdup(ccs));
1961  }
1962  g_free(ccs);
1963  //CLPARAM **p;
1964  //size_t count;
1965  //p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("drive", g_strdup(cs), &count);
1966  //status = freerdp_client_add_device_channel(rfi->settings, count, p);
1967  //g_free(p);
1968  }
1969  cs = remmina_plugin_service->file_get_string(remminafile, "drive");
1970  if (cs != NULL && cs[0] != '\0') {
1971  REMMINA_PLUGIN_DEBUG("Redirect directory set to %s", cs);
1972  CLPARAM **p;
1973  size_t count;
1974 
1975  gchar **folders = g_strsplit(cs, ";", -1);
1976  for (i = 0; folders[i] != NULL; i++) {
1977  REMMINA_PLUGIN_DEBUG("Parsing folder %s", folders[i]);
1978  p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("drive", g_strdup(folders[i]), &count);
1979  status = freerdp_client_add_device_channel(rfi->settings, count, p);
1980  g_free(p);
1981  }
1982  g_strfreev(folders);
1983  }
1984 
1985  if (remmina_plugin_service->file_get_int(remminafile, "shareprinter", FALSE)) {
1986 #ifdef HAVE_CUPS
1987  REMMINA_PLUGIN_DEBUG("Sharing printers");
1988  const gchar *po = remmina_plugin_service->file_get_string(remminafile, "printer_overrides");
1989  if (po && po[0] != 0) {
1990  /* Fallback to remmina code to override print drivers */
1991  if (cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, 0, 0, remmina_rdp_set_printers, rfi))
1992  REMMINA_PLUGIN_DEBUG("All printers have been shared");
1993  else
1994  REMMINA_PLUGIN_DEBUG("Cannot share printers, are there any available?");
1995  } else {
1996  /* Use libfreerdp code to map all printers */
1997  CLPARAM *d[1];
1998  int dcount;
1999  dcount = 1;
2000  d[0] = "printer";
2001  freerdp_client_add_device_channel(rfi->settings, dcount, d);
2002  }
2003 #endif /* HAVE_CUPS */
2004  }
2005 
2006  if (remmina_plugin_service->file_get_int(remminafile, "span", FALSE)) {
2007  freerdp_settings_set_bool(rfi->settings, FreeRDP_SpanMonitors, TRUE);
2008  freerdp_settings_set_bool(rfi->settings, FreeRDP_UseMultimon, TRUE);
2009  freerdp_settings_set_bool(rfi->settings, FreeRDP_Fullscreen, TRUE);
2010  remmina_plugin_service->file_set_int(remminafile, "multimon", 1);
2011  }
2012 
2013  if (remmina_plugin_service->file_get_int(remminafile, "multimon", FALSE)) {
2014  guint32 maxwidth = 0;
2015  guint32 maxheight = 0;
2016  gchar *monitorids;
2017  guint32 i;
2018  freerdp_settings_set_bool(rfi->settings, FreeRDP_UseMultimon, TRUE);
2019  /* TODO Add an option for this */
2020  freerdp_settings_set_bool(rfi->settings, FreeRDP_ForceMultimon, TRUE);
2021  freerdp_settings_set_bool(rfi->settings, FreeRDP_Fullscreen, TRUE);
2022 
2023  gchar *monitorids_string = g_strdup(remmina_plugin_service->file_get_string(remminafile, "monitorids"));
2024  /* Otherwise we get all the attached monitors
2025  * monitorids may contains desktop orientation values.
2026  * But before we check if there are orientation attributes
2027  */
2028  if (monitorids_string != NULL && monitorids_string[0] != '\0') {
2029  if (g_strstr_len(monitorids_string, -1, ",") != NULL) {
2030  if (g_strstr_len(monitorids_string, -1, ":") != NULL) {
2031  rdpMonitor *base = (rdpMonitor *)freerdp_settings_get_pointer(rfi->settings, FreeRDP_MonitorDefArray);
2032  /* We have an ID and an orientation degree */
2033  gchar **temp_items;
2034  gchar **rot_items;
2035  temp_items = g_strsplit(monitorids_string, ",", -1);
2036  for (i = 0; i < g_strv_length(temp_items); i++) {
2037  rot_items = g_strsplit(temp_items[i], ":", -1);
2038  rdpMonitor *current = &base[atoi(rot_items[0])];
2039  if (i == 0)
2040  monitorids = g_strdup(rot_items[0]);
2041  else
2042  monitorids = g_strdup_printf("%s,%s", monitorids, rot_items[0]);
2043  current->attributes.orientation = atoi(rot_items[1]);
2044  REMMINA_PLUGIN_DEBUG("Monitor n %d orientation: %d", i, current->attributes.orientation);
2045  }
2046  } else {
2047  monitorids = g_strdup(monitorids_string);
2048  }
2049  } else {
2050  monitorids = g_strdup(monitorids_string);
2051  }
2052  } else {
2053  monitorids = g_strdup(monitorids_string);
2054  }
2055  remmina_rdp_monitor_get(rfi, &monitorids, &maxwidth, &maxheight);
2056  if (monitorids != NULL && monitorids[0] != '\0') {
2057  UINT32 *base = (UINT32 *)freerdp_settings_get_pointer(rfi->settings, FreeRDP_MonitorIds);
2058  gchar **items;
2059  items = g_strsplit(monitorids, ",", -1);
2060  freerdp_settings_set_uint32(rfi->settings, FreeRDP_NumMonitorIds, g_strv_length(items));
2061  REMMINA_PLUGIN_DEBUG("NumMonitorIds: %d", freerdp_settings_get_uint32(rfi->settings, FreeRDP_NumMonitorIds));
2062  for (i = 0; i < g_strv_length(items); i++) {
2063  UINT32 *current = &base[i];
2064  *current = atoi(items[i]);
2065  REMMINA_PLUGIN_DEBUG("Added monitor with ID %" PRIu32, *current);
2066  }
2067  g_free(monitorids);
2068  g_strfreev(items);
2069  }
2070  if (maxwidth && maxheight) {
2071  REMMINA_PLUGIN_DEBUG("Setting DesktopWidth and DesktopHeight to: %dx%d", maxwidth, maxheight);
2072  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopWidth, maxwidth);
2073  freerdp_settings_set_uint32(rfi->settings, FreeRDP_DesktopHeight, maxheight);
2074  REMMINA_PLUGIN_DEBUG("DesktopWidth and DesktopHeight set to: %dx%d", freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth), freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopHeight));
2075  } else {
2076  REMMINA_PLUGIN_DEBUG("Cannot set Desktop Size, we are using the previously set values: %dx%d", freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth), freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopHeight));
2077  }
2078  remmina_plugin_service->protocol_plugin_set_width(gp, freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopWidth));
2079  remmina_plugin_service->protocol_plugin_set_height(gp, freerdp_settings_get_uint32(rfi->settings, FreeRDP_DesktopHeight));
2080  }
2081 
2082  const gchar *sn = remmina_plugin_service->file_get_string(remminafile, "smartcardname");
2083  if (remmina_plugin_service->file_get_int(remminafile, "sharesmartcard", FALSE) ||
2084  (sn != NULL && sn[0] != '\0')) {
2085  RDPDR_SMARTCARD *smartcard;
2086  smartcard = (RDPDR_SMARTCARD *)calloc(1, sizeof(RDPDR_SMARTCARD));
2087 
2088 #ifdef WITH_FREERDP3
2089  RDPDR_DEVICE *sdev;
2090  sdev = &(smartcard->device);
2091 #else
2092  RDPDR_SMARTCARD *sdev;
2093  sdev = smartcard;
2094 #endif
2095 
2096  sdev->Type = RDPDR_DTYP_SMARTCARD;
2097 
2098  freerdp_settings_set_bool(rfi->settings, FreeRDP_DeviceRedirection, TRUE);
2099 
2100  if (sn != NULL && sn[0] != '\0')
2101  sdev->Name = _strdup(sn);
2102 
2103  freerdp_settings_set_bool(rfi->settings, FreeRDP_RedirectSmartCards, TRUE);
2104 
2105  freerdp_device_collection_add(rfi->settings, (RDPDR_DEVICE *)smartcard);
2106  }
2107 
2108  if (remmina_plugin_service->file_get_int(remminafile, "passwordispin", FALSE))
2109  /* Option works only combined with Username and Domain, because FreeRDP
2110  * doesn’t know anything about info on smart card */
2111  freerdp_settings_set_bool(rfi->settings, FreeRDP_PasswordIsSmartcardPin, TRUE);
2112 
2113  /* /serial[:<name>[,<path>[,<driver>[,permissive]]]] */
2114  if (remmina_plugin_service->file_get_int(remminafile, "shareserial", FALSE)) {
2115  RDPDR_SERIAL *serial;
2116  serial = (RDPDR_SERIAL *)calloc(1, sizeof(RDPDR_SERIAL));
2117 
2118 #ifdef WITH_FREERDP3
2119  RDPDR_DEVICE *sdev;
2120  sdev = &(serial->device);
2121 #else
2122  RDPDR_SERIAL *sdev;
2123  sdev = serial;
2124 #endif
2125 
2126  sdev->Type = RDPDR_DTYP_SERIAL;
2127 
2128  freerdp_settings_set_bool(rfi->settings, FreeRDP_DeviceRedirection, TRUE);
2129 
2130  const gchar *sn = remmina_plugin_service->file_get_string(remminafile, "serialname");
2131  if (sn != NULL && sn[0] != '\0')
2132  sdev->Name = _strdup(sn);
2133 
2134  const gchar *sd = remmina_plugin_service->file_get_string(remminafile, "serialdriver");
2135  if (sd != NULL && sd[0] != '\0')
2136  serial->Driver = _strdup(sd);
2137 
2138  const gchar *sp = remmina_plugin_service->file_get_string(remminafile, "serialpath");
2139  if (sp != NULL && sp[0] != '\0')
2140  serial->Path = _strdup(sp);
2141 
2142  if (remmina_plugin_service->file_get_int(remminafile, "serialpermissive", FALSE))
2143  serial->Permissive = _strdup("permissive");
2144 
2145  freerdp_settings_set_bool(rfi->settings, FreeRDP_RedirectSerialPorts, TRUE);
2146 
2147  freerdp_device_collection_add(rfi->settings, (RDPDR_DEVICE *)serial);
2148  }
2149 
2150  if (remmina_plugin_service->file_get_int(remminafile, "shareparallel", FALSE)) {
2151  RDPDR_PARALLEL *parallel;
2152  parallel = (RDPDR_PARALLEL *)calloc(1, sizeof(RDPDR_PARALLEL));
2153 
2154 #ifdef WITH_FREERDP3
2155  RDPDR_DEVICE *pdev;
2156  pdev = &(parallel->device);
2157 #else
2158  RDPDR_PARALLEL *pdev;
2159  pdev = parallel;
2160 #endif
2161 
2162  pdev->Type = RDPDR_DTYP_PARALLEL;
2163 
2164  freerdp_settings_set_bool(rfi->settings, FreeRDP_DeviceRedirection, TRUE);
2165 
2166  freerdp_settings_set_bool(rfi->settings, FreeRDP_RedirectParallelPorts, TRUE);
2167 
2168  const gchar *pn = remmina_plugin_service->file_get_string(remminafile, "parallelname");
2169  if (pn != NULL && pn[0] != '\0')
2170  pdev->Name = _strdup(pn);
2171  const gchar *dp = remmina_plugin_service->file_get_string(remminafile, "parallelpath");
2172  if (dp != NULL && dp[0] != '\0')
2173  parallel->Path = _strdup(dp);
2174 
2175  freerdp_device_collection_add(rfi->settings, (RDPDR_DEVICE *)parallel);
2176  }
2177 
2181  if (remmina_plugin_service->file_get_int(remminafile, "multitransport", FALSE)) {
2182  freerdp_settings_set_bool(rfi->settings, FreeRDP_DeviceRedirection, TRUE);
2183  freerdp_settings_set_bool(rfi->settings, FreeRDP_SupportMultitransport, TRUE);
2184  freerdp_settings_set_uint32(rfi->settings, FreeRDP_MultitransportFlags,
2185  (TRANSPORT_TYPE_UDP_FECR | TRANSPORT_TYPE_UDP_FECL | TRANSPORT_TYPE_UDP_PREFERRED));
2186  } else {
2187  freerdp_settings_set_uint32(rfi->settings, FreeRDP_MultitransportFlags, 0);
2188  }
2189 
2190  /* If needed, force interactive authentication by deleting all authentication fields,
2191  * forcing libfreerdp to call our callbacks for authentication.
2192  * This usually happens from a second attempt of connection, never on the 1st one. */
2194  freerdp_settings_set_string(rfi->settings, FreeRDP_Username, NULL);
2195  freerdp_settings_set_string(rfi->settings, FreeRDP_Password, NULL);
2196  freerdp_settings_set_string(rfi->settings, FreeRDP_Domain, NULL);
2197 
2198  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayDomain, NULL);
2199  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayUsername, NULL);
2200  freerdp_settings_set_string(rfi->settings, FreeRDP_GatewayPassword, NULL);
2201 
2202  freerdp_settings_set_bool(rfi->settings, FreeRDP_GatewayUseSameCredentials, FALSE);
2203  }
2204 
2205  gboolean orphaned;
2206 
2207  if (!freerdp_connect(rfi->instance)) {
2208  orphaned = (GET_PLUGIN_DATA(rfi->protocol_widget) == NULL);
2209  if (!orphaned) {
2210  UINT32 e;
2211 
2212  e = freerdp_get_last_error(rfi->instance->context);
2213 
2214  switch (e) {
2215  case FREERDP_ERROR_AUTHENTICATION_FAILED:
2216  case STATUS_LOGON_FAILURE: // wrong return code from FreeRDP introduced at the end of July 2016? (fixed with b86c0ba)
2217 #ifdef FREERDP_ERROR_CONNECT_LOGON_FAILURE
2218  case FREERDP_ERROR_CONNECT_LOGON_FAILURE:
2219 #endif
2220  /* Logon failure, will retry with interactive authentication */
2222  break;
2223  case STATUS_ACCOUNT_LOCKED_OUT:
2224 #ifdef FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT
2225  case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT:
2226 #endif
2227  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nAccount locked out."),
2228  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2229  break;
2230  case STATUS_ACCOUNT_EXPIRED:
2231 #ifdef FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED
2232  case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED:
2233 #endif
2234  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nAccount expired."),
2235  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2236  break;
2237  case STATUS_PASSWORD_EXPIRED:
2238 #ifdef FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED
2239  case FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED:
2240 #endif
2241  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nPassword expired."),
2242  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2243  break;
2244  case STATUS_ACCOUNT_DISABLED:
2245 #ifdef FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED
2246  case FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED:
2247 #endif
2248  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nAccount disabled."),
2249  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2250  break;
2251 #ifdef FREERDP_ERROR_SERVER_INSUFFICIENT_PRIVILEGES
2252  /* https://msdn.microsoft.com/en-us/library/ee392247.aspx */
2253  case FREERDP_ERROR_SERVER_INSUFFICIENT_PRIVILEGES:
2254  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nInsufficient user privileges."),
2255  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2256  break;
2257 #endif
2258  case STATUS_ACCOUNT_RESTRICTION:
2259 #ifdef FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION
2260  case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION:
2261 #endif
2262  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nAccount restricted."),
2263  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2264  break;
2265 
2266  case STATUS_PASSWORD_MUST_CHANGE:
2267 #ifdef FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE
2268  case FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE:
2269 #endif
2270  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nChange user password before connecting."),
2271  freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2272  break;
2273 
2274  case FREERDP_ERROR_CONNECT_FAILED:
2275  remmina_plugin_service->protocol_plugin_set_error(gp, _("Lost connection to the RDP server “%s”."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2276  break;
2277  case FREERDP_ERROR_DNS_NAME_NOT_FOUND:
2278  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not find the address for the RDP server “%s”."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2279  break;
2280  case FREERDP_ERROR_TLS_CONNECT_FAILED:
2281  remmina_plugin_service->protocol_plugin_set_error(gp,
2282  _("Could not connect to the RDP server “%s” via TLS. See the DEBUG traces from a terminal for more information."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2283  break;
2284  case FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED:
2285  // TRANSLATORS: the placeholder may be either an IP/FQDN or a server hostname
2286  remmina_plugin_service->protocol_plugin_set_error(gp, _("Unable to establish a connection to the RDP server “%s”. Check “Security protocol negotiation”."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2287  break;
2288 #ifdef FREERDP_ERROR_POST_CONNECT_FAILED
2289  case FREERDP_ERROR_POST_CONNECT_FAILED:
2290  /* remmina_rdp_post_connect() returned FALSE to libfreerdp. We saved the error on rfi->postconnect_error */
2291  switch (rfi->postconnect_error) {
2292  case REMMINA_POSTCONNECT_ERROR_OK:
2293  /* We should never come here */
2294  remmina_plugin_service->protocol_plugin_set_error(gp, _("Cannot connect to the RDP server “%s”."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2295  break;
2296  case REMMINA_POSTCONNECT_ERROR_GDI_INIT:
2297  remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not start libfreerdp-gdi."));
2298  break;
2299  case REMMINA_POSTCONNECT_ERROR_NO_H264:
2300  remmina_plugin_service->protocol_plugin_set_error(gp, _("You requested a H.264 GFX mode for the server “%s”, but your libfreerdp does not support H.264. Please use a non-AVC colour depth setting."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2301  break;
2302  }
2303  break;
2304 #endif
2305 #ifdef FREERDP_ERROR_SERVER_DENIED_CONNECTION
2306  case FREERDP_ERROR_SERVER_DENIED_CONNECTION:
2307  remmina_plugin_service->protocol_plugin_set_error(gp, _("The “%s” server refused the connection."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2308  break;
2309 #endif
2310  case 0x800759DB:
2311  // E_PROXY_NAP_ACCESSDENIED https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tsgu/84cd92e4-592c-4219-95d8-18021ac654b0
2312  remmina_plugin_service->protocol_plugin_set_error(gp, _("The Remote Desktop Gateway “%s” denied the user “%s\\%s” access due to policy."),
2313  freerdp_settings_get_string(rfi->settings, FreeRDP_GatewayHostname), freerdp_settings_get_string(rfi->settings, FreeRDP_GatewayDomain), freerdp_settings_get_string(rfi->settings, FreeRDP_GatewayUsername));
2314  break;
2315 
2316  case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS:
2317  rfi->user_cancelled = TRUE;
2318  break;
2319 
2320  default:
2321  g_printf("libfreerdp returned code is %08X\n", e);
2322  remmina_plugin_service->protocol_plugin_set_error(gp, _("Cannot connect to the “%s” RDP server."), freerdp_settings_get_string(rfi->settings, FreeRDP_ServerHostname));
2323  break;
2324  }
2325  }
2326 
2327  return FALSE;
2328  }
2329 
2330  if (GET_PLUGIN_DATA(rfi->protocol_widget) == NULL) orphaned = true; else orphaned = false;
2331  if (!orphaned && freerdp_get_last_error(rfi->instance->context) == FREERDP_ERROR_SUCCESS && !rfi->user_cancelled)
2333 
2334  return TRUE;
2335 }
2336 
2337 static void rfi_uninit(rfContext *rfi)
2338 {
2339  freerdp *instance;
2340 
2341  instance = rfi->instance;
2342 
2343  if (rfi->remmina_plugin_thread) {
2344  rfi->thread_cancelled = TRUE; // Avoid all rf_queue function to run
2345  pthread_cancel(rfi->remmina_plugin_thread);
2346  if (rfi->remmina_plugin_thread)
2347  pthread_join(rfi->remmina_plugin_thread, NULL);
2348  }
2349 
2350  if (instance) {
2351  if (rfi->connected) {
2352  freerdp_abort_connect(instance);
2353  rfi->connected = false;
2354  }
2355  }
2356 
2357  if (instance) {
2358  RDP_CLIENT_ENTRY_POINTS *pEntryPoints = instance->pClientEntryPoints;
2359  if (pEntryPoints)
2360  IFCALL(pEntryPoints->GlobalUninit);
2361  free(instance->pClientEntryPoints);
2362  freerdp_context_free(instance); /* context is rfContext* rfi */
2363  freerdp_free(instance); /* This implicitly frees instance->context and rfi is no longer valid */
2364  }
2365 }
2366 
2367 static gboolean complete_cleanup_on_main_thread(gpointer data)
2368 {
2369  TRACE_CALL(__func__);
2370 
2371  gboolean orphaned;
2372  rfContext *rfi = (rfContext *)data;
2374 
2376 
2377  gdi_free(rfi->instance);
2378 
2379  gp = rfi->protocol_widget;
2380  if (GET_PLUGIN_DATA(gp) == NULL) orphaned = true; else orphaned = false;
2381 
2383  if (!orphaned) remmina_rdp_event_uninit(gp);
2384 
2385  if (!orphaned) g_object_steal_data(G_OBJECT(gp), "plugin-data");
2386 
2387  rfi_uninit(rfi);
2388 
2389  /* Notify the RemminaProtocolWidget that we closed our connection, and the GUI interface
2390  * can be removed */
2391  if (!orphaned)
2392  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
2393 
2394  return G_SOURCE_REMOVE;
2395 }
2396 
2397 static gpointer remmina_rdp_main_thread(gpointer data)
2398 {
2399  TRACE_CALL(__func__);
2401  rfContext *rfi;
2402 
2403  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
2404  CANCEL_ASYNC
2405 
2406  gp = (RemminaProtocolWidget *)data;
2407 
2408  rfi = GET_PLUGIN_DATA(gp);
2409 
2411  do
2412  remmina_rdp_main(gp);
2413  while (!remmina_plugin_service->protocol_plugin_has_error(gp) && rfi->attempt_interactive_authentication == TRUE && !rfi->user_cancelled);
2414 
2415  rfi->remmina_plugin_thread = 0;
2416 
2417  /* cleanup */
2418  g_idle_add(complete_cleanup_on_main_thread, (gpointer)rfi);
2419 
2420 
2421  return NULL;
2422 }
2423 
2425 {
2426  TRACE_CALL(__func__);
2427  freerdp *instance;
2428  rfContext *rfi;
2429 
2430  instance = freerdp_new();
2431  instance->PreConnect = remmina_rdp_pre_connect;
2432  instance->PostConnect = remmina_rdp_post_connect;
2433  instance->PostDisconnect = remmina_rdp_post_disconnect;
2434  instance->Authenticate = remmina_rdp_authenticate;
2435  instance->GatewayAuthenticate = remmina_rdp_gw_authenticate;
2436  //instance->VerifyCertificate = remmina_rdp_verify_certificate;
2437  instance->VerifyCertificateEx = remmina_rdp_verify_certificate_ex;
2438  //instance->VerifyChangedCertificate = remmina_rdp_verify_changed_certificate;
2439  instance->VerifyChangedCertificateEx = remmina_rdp_verify_changed_certificate_ex;
2440 
2441  instance->ContextSize = sizeof(rfContext);
2442  freerdp_context_new(instance);
2443  rfi = (rfContext *)instance->context;
2444 
2445  g_object_set_data_full(G_OBJECT(gp), "plugin-data", rfi, free);
2446 
2447  rfi->protocol_widget = gp;
2448  rfi->instance = instance;
2449  rfi->settings = instance->settings;
2450  rfi->connected = false;
2451  rfi->is_reconnecting = false;
2452  rfi->stop_reconnecting_requested = false;
2453  rfi->user_cancelled = FALSE;
2454 
2455  freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0);
2456 
2458 }
2459 
2461 {
2462  TRACE_CALL(__func__);
2463  rfContext *rfi = GET_PLUGIN_DATA(gp);
2464  RemminaFile *remminafile;
2465  const gchar *profile_name, *p;
2466  gchar thname[16], c;
2467  gint nthname = 0;
2468 
2469  rfi->scale = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
2470 
2471  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
2472 
2473 
2474  if (pthread_create(&rfi->remmina_plugin_thread, NULL, remmina_rdp_main_thread, gp)) {
2475  remmina_plugin_service->protocol_plugin_set_error(gp, "%s",
2476  "Could not start pthread.");
2477 
2478  rfi->remmina_plugin_thread = 0;
2479 
2480  return FALSE;
2481  }
2482 
2483  /* Generate a thread name to be used with pthread_setname_np() for debugging */
2484  profile_name = remmina_plugin_service->file_get_string(remminafile, "name");
2485  p = profile_name;
2486  strcpy(thname, "RemmRDP:");
2487  if (p) {
2488  nthname = strlen(thname);
2489  while ((c = *p) != 0 && nthname < sizeof(thname) - 1) {
2490  if (isalnum(c))
2491  thname[nthname++] = c;
2492  p++;
2493  }
2494  } else {
2495  strcat(thname, "<NONAM>");
2496  nthname = strlen(thname);
2497  }
2498  thname[nthname] = 0;
2499 #if defined(__linux__)
2500  pthread_setname_np(rfi->remmina_plugin_thread, thname);
2501 #elif defined(__FreeBSD__)
2502  pthread_set_name_np(rfi->remmina_plugin_thread, thname);
2503 #endif
2504 
2505  return TRUE;
2506 }
2507 
2509 {
2510  TRACE_CALL(__func__);
2511 
2512  REMMINA_PLUGIN_DEBUG("Requesting to close the connection");
2513  RemminaPluginRdpEvent rdp_event = { 0 };
2514  rfContext *rfi = GET_PLUGIN_DATA(gp);
2515 
2516  if (!remmina_plugin_service->is_main_thread())
2517  g_warning("WARNING: %s called on a subthread, which may not work or crash Remmina.", __func__);
2518 
2519  if (rfi && !rfi->connected) {
2520  /* libfreerdp is attempting to connect, we cannot interrupt our main thread
2521  * in the connect phase.
2522  * So we remove "plugin-data" from gp, so our rfi remains "orphan"
2523  */
2525  g_object_steal_data(G_OBJECT(gp), "plugin-data");
2526  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
2527  return FALSE;
2528  }
2529 
2530 
2531  if (rfi && rfi->clipboard.srv_clip_data_wait == SCDW_BUSY_WAIT) {
2532  REMMINA_PLUGIN_DEBUG("[RDP] requesting clipboard transfer to abort");
2533  /* Allow clipboard transfer from server to terminate */
2534  rfi->clipboard.srv_clip_data_wait = SCDW_ABORTING;
2535  usleep(100000);
2536  }
2537 
2538  if (rfi->is_reconnecting) {
2539  /* Special case: window closed when attempting to reconnect */
2540  rfi->stop_reconnecting_requested = TRUE;
2541  return FALSE;
2542  }
2543 
2544  rdp_event.type = REMMINA_RDP_EVENT_DISCONNECT;
2545  remmina_rdp_event_event_push(gp, &rdp_event);
2546 
2547  return FALSE;
2548 }
2549 
2551 {
2552  TRACE_CALL(__func__);
2553  return TRUE;
2554 }
2555 
2557 {
2558  TRACE_CALL(__func__);
2559  rfContext *rfi = GET_PLUGIN_DATA(gp);
2560 
2561  switch (feature->id) {
2562  case REMMINA_RDP_FEATURE_UNFOCUS:
2564  break;
2565 
2566  case REMMINA_RDP_FEATURE_SCALE:
2567  if (rfi) {
2568  rfi->scale = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
2570  } else {
2571  REMMINA_PLUGIN_DEBUG("Remmina RDP plugin warning: Null value for rfi by REMMINA_RDP_FEATURE_SCALE");
2572  }
2573  break;
2574 
2575  case REMMINA_RDP_FEATURE_MULTIMON:
2576  if (rfi) {
2577  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
2578  if (remmina_plugin_service->file_get_int(remminafile, "multimon", FALSE)) {
2579  freerdp_settings_set_bool(rfi->settings, FreeRDP_UseMultimon, TRUE);
2580  /* TODO Add an option for this */
2581  freerdp_settings_set_bool(rfi->settings, FreeRDP_ForceMultimon, TRUE);
2582  freerdp_settings_set_bool(rfi->settings, FreeRDP_Fullscreen, TRUE);
2584  }
2585  } else {
2586  REMMINA_PLUGIN_DEBUG("Remmina RDP plugin warning: Null value for rfi by REMMINA_RDP_FEATURE_MULTIMON");
2587  }
2588  break;
2589 
2590  case REMMINA_RDP_FEATURE_DYNRESUPDATE:
2591  break;
2592 
2593  case REMMINA_RDP_FEATURE_TOOL_REFRESH:
2594  if (rfi)
2595  gtk_widget_queue_draw_area(rfi->drawing_area, 0, 0,
2596  remmina_plugin_service->protocol_plugin_get_width(gp),
2597  remmina_plugin_service->protocol_plugin_get_height(gp));
2598  else
2599  REMMINA_PLUGIN_DEBUG("Remmina RDP plugin warning: Null value for rfi by REMMINA_RDP_FEATURE_TOOL_REFRESH");
2600  break;
2601 
2602  case REMMINA_RDP_FEATURE_TOOL_SENDCTRLALTDEL:
2604  break;
2605 
2606  default:
2607  break;
2608  }
2609 }
2610 
2611 /* Send a keystroke to the plugin window */
2612 static void remmina_rdp_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
2613 {
2614  TRACE_CALL(__func__);
2615  rfContext *rfi = GET_PLUGIN_DATA(gp);
2616 
2617  remmina_plugin_service->protocol_plugin_send_keys_signals(rfi->drawing_area,
2618  keystrokes, keylen, GDK_KEY_PRESS | GDK_KEY_RELEASE);
2619  return;
2620 }
2621 
2623 {
2624  rfContext *rfi = GET_PLUGIN_DATA(gp);
2625  rdpGdi *gdi;
2626  size_t szmem;
2627 
2628  UINT32 bytesPerPixel;
2629  UINT32 bitsPerPixel;
2630 
2631  if (!rfi)
2632  return FALSE;
2633 
2634  gdi = ((rdpContext *)rfi)->gdi;
2635 
2636  bytesPerPixel = GetBytesPerPixel(gdi->hdc->format);
2637  bitsPerPixel = GetBitsPerPixel(gdi->hdc->format);
2638 
2642  szmem = gdi->width * gdi->height * bytesPerPixel;
2643 
2644  REMMINA_PLUGIN_DEBUG("allocating %zu bytes for a full screenshot", szmem);
2645  rpsd->buffer = malloc(szmem);
2646  if (!rpsd->buffer) {
2647  REMMINA_PLUGIN_DEBUG("could not set aside %zu bytes for a full screenshot", szmem);
2648  return FALSE;
2649  }
2650  rpsd->width = gdi->width;
2651  rpsd->height = gdi->height;
2652  rpsd->bitsPerPixel = bitsPerPixel;
2653  rpsd->bytesPerPixel = bytesPerPixel;
2654 
2655  memcpy(rpsd->buffer, gdi->primary_buffer, szmem);
2656 
2657  /* Returning TRUE instruct also the caller to deallocate rpsd->buffer */
2658  return TRUE;
2659 }
2660 
2661 /* Array of key/value pairs for colour depths */
2662 static gpointer colordepth_list[] =
2663 {
2664  /* 1st one is the default in a new install */
2665  "99", N_("Automatic (32 bpp) (Server chooses its best format)"),
2666  "66", N_("GFX AVC444 (32 bpp)"),
2667  "65", N_("GFX AVC420 (32 bpp)"),
2668  "64", N_("GFX RFX (32 bpp)"),
2669  "63", N_("GFX RFX Progressive (32 bpp)"),
2670  "0", N_("RemoteFX (32 bpp)"),
2671  "32", N_("True colour (32 bpp)"),
2672  "24", N_("True colour (24 bpp)"),
2673  "16", N_("High colour (16 bpp)"),
2674  "15", N_("High colour (15 bpp)"),
2675  "8", N_("256 colours (8 bpp)"),
2676  NULL
2677 };
2678 
2679 /* Array of key/value pairs for the FreeRDP logging level */
2680 static gpointer log_level[] =
2681 {
2682  "INFO", "INFO",
2683  "FATAL", "FATAL",
2684  "ERROR", "ERROR",
2685  "WARN", "WARN",
2686  "DEBUG", "DEBUG",
2687  "TRACE", "TRACE",
2688  "OFF", "OFF",
2689  NULL
2690 };
2691 
2692 
2693 /* Array of key/value pairs for quality selection */
2694 static gpointer quality_list[] =
2695 {
2696  "0", N_("Poor (fastest)"),
2697  "1", N_("Medium"),
2698  "2", N_("Good"),
2699  "9", N_("Best (slowest)"),
2700  NULL
2701 };
2702 
2703 /* Array of key/value pairs for quality selection */
2704 static gpointer network_list[] =
2705 {
2706  "none", N_("None"),
2707  "autodetect", N_("Auto-detect"),
2708  "modem", N_("Modem"),
2709  "broadband-low", N_("Low performance broadband"),
2710  "satellite", N_("Satellite"),
2711  "broadband-high", N_("High performance broadband"),
2712  "wan", N_("WAN"),
2713  "lan", N_("LAN"),
2714  NULL
2715 };
2716 
2717 /* Array of key/value pairs for sound options */
2718 static gpointer sound_list[] =
2719 {
2720  "off", N_("Off"),
2721  "local", N_("Local"),
2722  "remote", N_("Remote"),
2723  NULL
2724 };
2725 
2726 /* Array of key/value pairs for security */
2727 static gpointer security_list[] =
2728 {
2729  "", N_("Automatic negotiation"),
2730  "nla", N_("NLA protocol security"),
2731  "tls", N_("TLS protocol security"),
2732  "rdp", N_("RDP protocol security"),
2733  "ext", N_("NLA extended protocol security"),
2734  NULL
2735 };
2736 
2737 static gpointer gwtransp_list[] =
2738 {
2739  "http", "HTTP",
2740  "rpc", "RPC",
2741  "auto", "Auto",
2742  NULL
2743 };
2744 
2745 static gpointer tls_seclevel[] =
2746 {
2747  "", N_("Default"),
2748  "0", N_("0 — Windows 7 compatible"),
2749  "1", N_("1"),
2750  "2", N_("2"),
2751  "3", N_("3"),
2752  "4", N_("4"),
2753  "5", N_("5"),
2754  NULL
2755 };
2756 
2757 static gchar clientbuild_list[] =
2758  N_("2600 (Windows XP), 7601 (Windows Vista/7), 9600 (Windows 8 and newer)");
2759 
2760 static gchar clientbuild_tooltip[] =
2761  N_("Used i.a. by terminal services in a smart card channel to distinguish client capabilities:\n"
2762  " • < 4034: Windows XP base smart card functions\n"
2763  " • 4034-7064: Windows Vista/7: SCardReadCache(),\n"
2764  " SCardWriteCache(), SCardGetTransmitCount()\n"
2765  " • >= 7065: Windows 8 and newer: SCardGetReaderIcon(),\n"
2766  " SCardGetDeviceTypeId()");
2767 
2768 static gchar microphone_tooltip[] =
2769  N_("Options for redirection of audio input:\n"
2770  " • [sys:<sys>,][dev:<dev>,][format:<format>,][rate:<rate>,]\n"
2771  " [channel:<channel>] Audio input (microphone)\n"
2772  " • sys:pulse\n"
2773  " • format:1\n"
2774  " • sys:oss,dev:1,format:1\n"
2775  " • sys:alsa");
2776 
2777 static gchar audio_tooltip[] =
2778  N_("Options for redirection of audio output:\n"
2779  " • [sys:<sys>,][dev:<dev>,][format:<format>,][rate:<rate>,]\n"
2780  " [channel:<channel>] Audio output\n"
2781  " • sys:pulse\n"
2782  " • format:1\n"
2783  " • sys:oss,dev:1,format:1\n"
2784  " • sys:alsa");
2785 
2786 
2787 static gchar usb_tooltip[] =
2788  N_("Options for redirection of USB device:\n"
2789  " • [dbg,][id:<vid>:<pid>#…,][addr:<bus>:<addr>#…,][auto]\n"
2790  " • auto\n"
2791  " • id:054c:0268#4669:6e6b,addr:04:0c");
2792 
2793 static gchar timeout_tooltip[] =
2794  N_("Advanced setting for high latency links:\n"
2795  "Adjusts the connection timeout. Use if your connection times out.\n"
2796  "The highest possible value is 600000 ms (10 minutes).\n");
2797 
2798 static gchar network_tooltip[] =
2799  N_("Performance optimisations based on the network connection type:\n"
2800  "Using auto-detection is advised.\n"
2801  "If “Auto-detect” fails, choose the most appropriate option in the list.\n");
2802 
2803 static gchar monitorids_tooltip[] =
2804  N_("Comma-separated list of monitor IDs and desktop orientations:\n"
2805  " • [<id>:<orientation-in-degrees>,]\n"
2806  " • 0,1,2,3\n"
2807  " • 0:270,1:90\n"
2808  "Orientations are specified in degrees, valid values are:\n"
2809  " • 0 (landscape)\n"
2810  " • 90 (portrait)\n"
2811  " • 180 (landscape flipped)\n"
2812  " • 270 (portrait flipped)\n"
2813  "\n");
2814 
2815 static gchar drive_tooltip[] =
2816  N_("Redirect directory <path> as named share <name>.\n"
2817  " • <name>,<fullpath>[;<name>,<fullpath>[;…]]\n"
2818  " • MyHome,/home/remminer\n"
2819  " • /home/remminer\n"
2820  " • MyHome,/home/remminer;SomePath,/path/to/somepath\n"
2821  "Hotplug support is enabled with:\n"
2822  " • hotplug,*\n"
2823  "\n");
2824 
2825 /* Array of RemminaProtocolSetting for basic settings.
2826  * Each item is composed by:
2827  * a) RemminaProtocolSettingType for setting type
2828  * b) Setting name
2829  * c) Setting description
2830  * d) Compact disposition
2831  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
2832  * f) Setting tooltip
2833  * g) Validation data pointer, will be passed to the validation callback method.
2834  * h) Validation callback method (Can be NULL. Every entry will be valid then.)
2835  * use following prototype:
2836  * gboolean mysetting_validator_method(gpointer key, gpointer value,
2837  * gpointer validator_data);
2838  * gpointer key is a gchar* containing the setting's name,
2839  * gpointer value contains the value which should be validated,
2840  * gpointer validator_data contains your passed data.
2841  */
2843 {
2844  { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, NULL, NULL, NULL, NULL },
2845  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL, NULL, NULL },
2846  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("Password"), FALSE, NULL, NULL, NULL, NULL },
2847  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "domain", N_("Domain"), FALSE, NULL, NULL, NULL, NULL },
2848  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "drive", N_("Share folder"), FALSE, NULL, drive_tooltip, NULL, NULL },
2849  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "restricted-admin", N_("Restricted admin mode"), FALSE, NULL, NULL, NULL, NULL },
2850  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "pth", N_("Password hash"), FALSE, NULL, N_("Restricted admin mode password hash"), NULL, NULL },
2851  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "left-handed", N_("Left-handed mouse support"), TRUE, NULL, N_("Swap left and right mouse buttons for left-handed mouse support"), NULL, NULL },
2852  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disable-smooth-scrolling", N_("Disable smooth scrolling"), TRUE, NULL, NULL, NULL, NULL },
2853  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "multimon", N_("Enable multi monitor"), TRUE, NULL, NULL, NULL, NULL },
2854  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "span", N_("Span screen over multiple monitors"), TRUE, NULL, NULL, NULL, NULL },
2855  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "monitorids", N_("List monitor IDs"), FALSE, NULL, monitorids_tooltip, NULL, NULL },
2856  { REMMINA_PROTOCOL_SETTING_TYPE_RESOLUTION, "resolution", NULL, FALSE, NULL, NULL, NULL, NULL },
2857  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "colordepth", N_("Colour depth"), FALSE, colordepth_list, NULL, NULL, NULL },
2858  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "network", N_("Network connection type"), FALSE, network_list, network_tooltip, NULL, NULL },
2859  { REMMINA_PROTOCOL_SETTING_TYPE_KEYMAP, "keymap", NULL, FALSE, NULL, NULL, NULL, NULL },
2860  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL }
2861 };
2862 
2863 /* Array of RemminaProtocolSetting for advanced settings.
2864  * Each item is composed by:
2865  * a) RemminaProtocolSettingType for setting type
2866  * b) Setting name
2867  * c) Setting description
2868  * d) Compact disposition
2869  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
2870  * f) Setting Tooltip
2871  */
2873 {
2874  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "quality", N_("Quality"), FALSE, quality_list, NULL },
2875  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "security", N_("Security protocol negotiation"), FALSE, security_list, NULL },
2876  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "gwtransp", N_("Gateway transport type"), FALSE, gwtransp_list, NULL },
2877  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "tls-seclevel", N_("TLS Security Level"), FALSE, tls_seclevel, NULL },
2878  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "freerdp_log_level", N_("FreeRDP log level"), FALSE, log_level, NULL },
2879  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "freerdp_log_filters", N_("FreeRDP log filters"), FALSE, NULL, N_("tag:level[,tag:level[,…]]") },
2880  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "sound", N_("Audio output mode"), FALSE, sound_list, NULL },
2881  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "audio-output", N_("Redirect local audio output"), TRUE, NULL, audio_tooltip },
2882  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "microphone", N_("Redirect local microphone"), TRUE, NULL, microphone_tooltip },
2883  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "timeout", N_("Connection timeout in ms"), TRUE, NULL, timeout_tooltip },
2884  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "gateway_server", N_("Remote Desktop Gateway server"), FALSE, NULL, NULL },
2885  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "gateway_username", N_("Remote Desktop Gateway username"), FALSE, NULL, NULL },
2886  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "gateway_password", N_("Remote Desktop Gateway password"), FALSE, NULL, NULL },
2887  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "gateway_domain", N_("Remote Desktop Gateway domain"), FALSE, NULL, NULL },
2888  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "clientname", N_("Client name"), FALSE, NULL, NULL },
2889  { REMMINA_PROTOCOL_SETTING_TYPE_COMBO, "clientbuild", N_("Client build"), FALSE, clientbuild_list, clientbuild_tooltip },
2890  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "exec", N_("Start-up program"), FALSE, NULL, NULL },
2891  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "execpath", N_("Start-up path"), FALSE, NULL, NULL },
2892  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "loadbalanceinfo", N_("Load balance info"), FALSE, NULL, NULL },
2893  // TRANSLATORS: Do not use typographic quotation marks, these must stay as "double quote", also know as “Typewriter ("programmer's") quote, ambidextrous.”
2894  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "printer_overrides", N_("Override printer drivers"), FALSE, NULL, N_("\"Samsung_CLX-3300_Series\":\"Samsung CLX-3300 Series PS\";\"Canon MF410\":\"Canon MF410 Series UFR II\"") },
2895  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "usb", N_("USB device redirection"), TRUE, NULL, usb_tooltip },
2896  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "serialname", N_("Local serial name"), FALSE, NULL, N_("COM1, COM2, etc.") },
2897  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "serialdriver", N_("Local serial driver"), FALSE, NULL, N_("Serial") },
2898  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "serialpath", N_("Local serial path"), FALSE, NULL, N_("/dev/ttyS0, /dev/ttyS1, etc.") },
2899  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "parallelname", N_("Local parallel name"), FALSE, NULL, NULL },
2900  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "parallelpath", N_("Local parallel device"), FALSE, NULL, NULL },
2901  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "smartcardname", N_("Name of smart card"), FALSE, NULL, NULL },
2902  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "dvc", N_("Dynamic virtual channel"), FALSE, NULL, N_("<channel>[,<options>]") },
2903  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "vc", N_("Static virtual channel"), FALSE, NULL, N_("<channel>[,<options>]") },
2904  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "rdp2tcp", N_("TCP redirection"), FALSE, NULL, N_("/PATH/TO/rdp2tcp") },
2905  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "rdp_reconnect_attempts", N_("Reconnect attempts number"), FALSE, NULL, N_("The maximum number of reconnect attempts upon an RDP disconnect (default: 20)") },
2906  { REMMINA_PROTOCOL_SETTING_TYPE_ASSISTANCE, "assistance_mode", N_("Attempt to connect in assistance mode"), TRUE, NULL },
2907  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "preferipv6", N_("Prefer IPv6 AAAA record over IPv4 A record"), TRUE, NULL, NULL },
2908  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "shareprinter", N_("Share printers"), TRUE, NULL, NULL },
2909  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "shareserial", N_("Share serial ports"), TRUE, NULL, NULL },
2910  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "serialpermissive", N_("(SELinux) permissive mode for serial ports"), TRUE, NULL, NULL },
2911  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "shareparallel", N_("Share parallel ports"), TRUE, NULL, NULL },
2912  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "sharesmartcard", N_("Share a smart card"), TRUE, NULL, NULL },
2913  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", N_("Turn off clipboard sync"), TRUE, NULL, NULL },
2914  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "cert_ignore", N_("Ignore certificate"), TRUE, NULL, NULL },
2915  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "old-license", N_("Use the old license workflow"), TRUE, NULL, N_("It disables CAL and hwId is set to 0") },
2916  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"), TRUE, NULL, NULL },
2917  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "console", N_("Attach to console (2003/2003 R2)"), TRUE, NULL, NULL },
2918  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disable_fastpath", N_("Turn off fast-path"), TRUE, NULL, NULL },
2919  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "gateway_usage", N_("Server detection using Remote Desktop Gateway"), TRUE, NULL, NULL },
2920 #if defined(PROXY_TYPE_IGNORE)
2921  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "useproxyenv", N_("Use system proxy settings"), TRUE, NULL, NULL },
2922 #endif
2923  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableautoreconnect", N_("Turn off automatic reconnection"), TRUE, NULL, NULL },
2924  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "relax-order-checks", N_("Relax order checks"), TRUE, NULL, NULL },
2925  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "glyph-cache", N_("Glyph cache"), TRUE, NULL, NULL },
2926  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "multitransport", N_("Enable multitransport protocol (UDP)"), TRUE, NULL, N_("Using the UDP protocol may improve performance") },
2927  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "base-cred-for-gw", N_("Use base credentials for gateway too"), TRUE, NULL, NULL },
2928 #if FREERDP_CHECK_VERSION(2, 3, 1)
2929  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "websockets", N_("Enable Gateway websockets support"), TRUE, NULL, NULL },
2930 #endif
2931  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "no-suppress", N_("Update framebuffer even when not visible"), TRUE, NULL },
2932  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
2933 };
2934 
2935 /* Array for available features.
2936  * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
2938 {
2939  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_RDP_FEATURE_VIEWONLY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "viewonly",
2940  N_("View only") },
2941  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_RDP_FEATURE_TOOL_REFRESH, N_("Refresh"), NULL, NULL },
2942  { REMMINA_PROTOCOL_FEATURE_TYPE_SCALE, REMMINA_RDP_FEATURE_SCALE, NULL, NULL, NULL },
2943  { REMMINA_PROTOCOL_FEATURE_TYPE_DYNRESUPDATE, REMMINA_RDP_FEATURE_DYNRESUPDATE, NULL, NULL, NULL },
2944  { REMMINA_PROTOCOL_FEATURE_TYPE_MULTIMON, REMMINA_RDP_FEATURE_MULTIMON, NULL, NULL, NULL },
2945  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_RDP_FEATURE_TOOL_SENDCTRLALTDEL, N_("Send Ctrl+Alt+Delete"), NULL, NULL },
2946  { REMMINA_PROTOCOL_FEATURE_TYPE_UNFOCUS, REMMINA_RDP_FEATURE_UNFOCUS, NULL, NULL, NULL },
2947  { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
2948 };
2949 
2950 /* This will be filled with version info string */
2952 
2953 /* Protocol plugin definition and features */
2955 {
2957  "RDP", // Name
2958  N_("RDP - Remote Desktop Protocol"), // Description
2959  GETTEXT_PACKAGE, // Translation domain
2960  remmina_plugin_rdp_version, // Version number
2961  "org.remmina.Remmina-rdp-symbolic", // Icon for normal connection
2962  "org.remmina.Remmina-rdp-ssh-symbolic", // Icon for SSH connection
2963  remmina_rdp_basic_settings, // Array for basic settings
2964  remmina_rdp_advanced_settings, // Array for advanced settings
2965  REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type
2966  remmina_rdp_features, // Array for available features
2967  remmina_rdp_init, // Plugin initialization
2968  remmina_rdp_open_connection, // Plugin open connection
2969  remmina_rdp_close_connection, // Plugin close connection
2970  remmina_rdp_query_feature, // Query for available features
2971  remmina_rdp_call_feature, // Call a feature
2972  remmina_rdp_keystroke, // Send a keystroke
2973  remmina_rdp_get_screenshot, // Screenshot
2974  remmina_rdp_event_on_map, // RCW map event
2975  remmina_rdp_event_on_unmap // RCW unmap event
2976 };
2977 
2978 /* File plugin definition and features */
2980 {
2981  REMMINA_PLUGIN_TYPE_FILE, // Type
2982  "RDPF", // Name
2983  N_("RDP - RDP File Handler"), // Description
2984  GETTEXT_PACKAGE, // Translation domain
2985  remmina_plugin_rdp_version, // Version number
2986  remmina_rdp_file_import_test, // Test import function
2987  remmina_rdp_file_import, // Import function
2988  remmina_rdp_file_export_test, // Test export function
2989  remmina_rdp_file_export, // Export function
2990  NULL
2991 };
2992 
2993 /* Preferences plugin definition and features */
2995 {
2996  REMMINA_PLUGIN_TYPE_PREF, // Type
2997  "RDPS", // Name
2998  N_("RDP - Preferences"), // Description
2999  GETTEXT_PACKAGE, // Translation domain
3000  remmina_plugin_rdp_version, // Version number
3001  "RDP", // Label
3002  remmina_rdp_settings_new // Preferences body function
3003 };
3004 
3005 static char *buildconfig_strstr(const char *bc, const char *option)
3006 {
3007  TRACE_CALL(__func__);
3008 
3009  char *p, *n;
3010 
3011  p = strcasestr(bc, option);
3012  if (p == NULL)
3013  return NULL;
3014 
3015  if (p > bc && *(p - 1) > ' ')
3016  return NULL;
3017 
3018  n = p + strlen(option);
3019  if (*n > ' ')
3020  return NULL;
3021 
3022  return p;
3023 }
3024 
3025 G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
3026 {
3027  int vermaj, vermin, verrev;
3028 
3029  TRACE_CALL(__func__);
3030  remmina_plugin_service = service;
3031 
3032  /* Check that we are linked to the correct version of libfreerdp */
3033 
3034  freerdp_get_version(&vermaj, &vermin, &verrev);
3035  if (vermaj < FREERDP_REQUIRED_MAJOR ||
3036  (vermaj == FREERDP_REQUIRED_MAJOR && (vermin < FREERDP_REQUIRED_MINOR ||
3037  (vermin == FREERDP_REQUIRED_MINOR && verrev < FREERDP_REQUIRED_REVISION)))) {
3038  g_printf("Upgrade your FreeRDP library version from %d.%d.%d to at least libfreerdp %d.%d.%d "
3039  "to run the RDP plugin.\n",
3040  vermaj, vermin, verrev,
3041  FREERDP_REQUIRED_MAJOR, FREERDP_REQUIRED_MINOR, FREERDP_REQUIRED_REVISION);
3042  return FALSE;
3043  }
3044 
3045  bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
3046  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
3047 
3048  if (!service->register_plugin((RemminaPlugin *)&remmina_rdp))
3049  return FALSE;
3050 
3051  remmina_rdpf.export_hints = _("Export connection in Windows .rdp file format");
3052 
3053  if (!service->register_plugin((RemminaPlugin *)&remmina_rdpf))
3054  return FALSE;
3055 
3056  if (!service->register_plugin((RemminaPlugin *)&remmina_rdps))
3057  return FALSE;
3058 
3059  if (buildconfig_strstr(freerdp_get_build_config(), "WITH_GFX_H264=ON")) {
3060  gfx_h264_available = TRUE;
3061  REMMINA_PLUGIN_DEBUG("gfx_h264_available: %d", gfx_h264_available);
3062  } else {
3063  gfx_h264_available = FALSE;
3064  REMMINA_PLUGIN_DEBUG("gfx_h264_available: %d", gfx_h264_available);
3065  /* Remove values 65 and 66 from colordepth_list array by shifting it */
3066  gpointer *src, *dst;
3067  dst = src = colordepth_list;
3068  while (*src) {
3069  if (strcmp(*src, "66") != 0 && strcmp(*src, "65") != 0) {
3070  if (dst != src) {
3071  *dst = *src;
3072  *(dst + 1) = *(src + 1);
3073  }
3074  dst += 2;
3075  }
3076  src += 2;
3077  }
3078  *dst = NULL;
3079  }
3080 
3082  "RDP plugin: %s (Git %s), Compiled with libfreerdp %s (%s), Running with libfreerdp %s (rev %s), H.264 %s",
3083  VERSION, REMMINA_GIT_REVISION,
3084 #ifdef WITH_FREERDP3
3085  FREERDP_VERSION_FULL, FREERDP_GIT_REVISION,
3086 #else
3087  FREERDP_VERSION_FULL, GIT_REVISION,
3088 #endif
3089  freerdp_get_version_string(),
3090  freerdp_get_build_revision(),
3091  gfx_h264_available ? "Yes" : "No"
3092  );
3093 
3095 
3096  return TRUE;
3097 }
CliprdrClientContext * context
Definition: rdp_plugin.h:135
gint w
Definition: rdp_plugin.h:262
struct remmina_plugin_rdp_event::@42::@45 mouse_event
gboolean thread_cancelled
Definition: rdp_plugin.h:332
gboolean is_reconnecting
Definition: rdp_plugin.h:344
-
static gpointer tls_seclevel[]
Definition: rdp_plugin.c:2744
-
static gchar network_tooltip[]
Definition: rdp_plugin.c:2797
+
static gpointer tls_seclevel[]
Definition: rdp_plugin.c:2745
+
static gchar network_tooltip[]
Definition: rdp_plugin.c:2798
gint y
Definition: rdp_plugin.h:262
RemminaScaleMode(* remmina_protocol_widget_get_current_scale_mode)(RemminaProtocolWidget *gp)
Definition: plugin.h:172
static int remmina_rdp_load_static_channel_addin(rdpChannels *channels, rdpSettings *settings, char *name, void *data)
Definition: rdp_plugin.c:1049
-
static gboolean remmina_rdp_open_connection(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:2459
+
static gboolean remmina_rdp_open_connection(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:2460
gboolean user_cancelled
Definition: rdp_plugin.h:331
-
static gpointer quality_list[]
Definition: rdp_plugin.c:2693
+
static gpointer quality_list[]
Definition: rdp_plugin.c:2694
static void remmina_rdp_send_ctrlaltdel(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:1232
gboolean attempt_interactive_authentication
Definition: rdp_plugin.h:388
@@ -110,13 +110,13 @@ $(document).ready(function(){initNavTree('rdp__plugin_8c_source.html','');});
void(* file_set_int)(RemminaFile *remminafile, const gchar *setting, gint value)
Definition: plugin.h:221
-
static RemminaProtocolPlugin remmina_rdp
Definition: rdp_plugin.c:2953
+
static RemminaProtocolPlugin remmina_rdp
Definition: rdp_plugin.c:2954
gboolean remmina_rdp_event_on_unmap(RemminaProtocolWidget *gp)
Definition: rdp_event.c:74
gboolean remmina_rdp_file_export_test(RemminaFilePlugin *plugin, RemminaFile *remminafile)
Definition: rdp_file.c:205
-
static gpointer log_level[]
Definition: rdp_plugin.c:2679
+
static gpointer log_level[]
Definition: rdp_plugin.c:2680
gchar *(* file_get_user_datadir)(void)
Definition: plugin.h:214
static BOOL rf_auto_reconnect(rfContext *rfi)
Definition: rdp_plugin.c:398
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:44
@@ -136,13 +136,13 @@ $(document).ready(function(){initNavTree('rdp__plugin_8c_source.html','');});
void remmina_rdp_settings_get_orientation_scale_prefs(int *desktopOrientation, int *desktopScaleFactor, int *deviceScaleFactor)
Definition: rdp_settings.c:707
-
static gboolean remmina_rdp_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: rdp_plugin.c:2549
+
static gboolean remmina_rdp_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: rdp_plugin.c:2550
static DWORD remmina_rdp_verify_certificate_ex(freerdp *instance, const char *host, UINT16 port, const char *common_name, const char *subject, const char *issuer, const char *fingerprint, DWORD flags)
Definition: rdp_plugin.c:911
-
static gchar microphone_tooltip[]
Definition: rdp_plugin.c:2767
+
static gchar microphone_tooltip[]
Definition: rdp_plugin.c:2768
gint(* protocol_plugin_init_certificate)(RemminaProtocolWidget *gp, const gchar *subject, const gchar *issuer, const gchar *fingerprint)
Definition: plugin.h:192
static BOOL rf_desktop_resize(rdpContext *context)
Definition: rdp_plugin.c:577
static CLPARAM ** remmina_rdp_CommandLineParseCommaSeparatedValues(const char *list, size_t *count)
Definition: rdp_plugin.c:202
@@ -151,13 +151,13 @@ $(document).ready(function(){initNavTree('rdp__plugin_8c_source.html','');});
void remmina_rdp_settings_init(void)
Definition: rdp_settings.c:57
static BOOL remmina_rdp_authenticate(freerdp *instance, char **username, char **password, char **domain)
Definition: rdp_plugin.c:772
-
static gboolean complete_cleanup_on_main_thread(gpointer data)
Definition: rdp_plugin.c:2366
+
static gboolean complete_cleanup_on_main_thread(gpointer data)
Definition: rdp_plugin.c:2367
static gboolean remmina_rdp_main(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:1343
void remmina_rdp_clipboard_init(rfContext *rfi)
Definition: rdp_cliprdr.c:934
-
static const RemminaProtocolSetting remmina_rdp_advanced_settings[]
Definition: rdp_plugin.c:2871
+
static const RemminaProtocolSetting remmina_rdp_advanced_settings[]
Definition: rdp_plugin.c:2872
RemminaProtocolWidget * protocol_widget
Definition: rdp_plugin.h:323
int reconnect_nattempt
Definition: rdp_plugin.h:352
-
static gpointer network_list[]
Definition: rdp_plugin.c:2703
+
static gpointer network_list[]
Definition: rdp_plugin.c:2704
void remmina_rdp_OnChannelConnectedEventHandler(void *context, ChannelConnectedEventArgs *e)
Definition: rdp_channels.c:46
void(* protocol_plugin_send_keys_signals)(GtkWidget *widget, const guint *keyvals, int length, GdkEventType action)
Definition: plugin.h:212
@@ -165,7 +165,7 @@ $(document).ready(function(){initNavTree('rdp__plugin_8c_source.html','');});
gint(* protocol_plugin_get_width)(RemminaProtocolWidget *gp)
Definition: plugin.h:168
-
static void remmina_rdp_init(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:2423
+
static void remmina_rdp_init(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:2424
static gchar * remmina_get_rdp_kbd_remap(const gchar *keymap)
Definition: rdp_plugin.c:1315
void(* get_server_port)(const gchar *server, gint defaultport, gchar **host, gint *port)
Definition: plugin.h:249
gint(* file_get_int)(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: plugin.h:222
@@ -179,12 +179,12 @@ $(document).ready(function(){initNavTree('rdp__plugin_8c_source.html','');});
static CLPARAM ** remmina_rdp_CommandLineParseCommaSeparatedValuesEx(const char *name, const char *list, size_t *count)
Definition: rdp_plugin.c:123
-
static gchar clientbuild_tooltip[]
Definition: rdp_plugin.c:2759
+
static gchar clientbuild_tooltip[]
Definition: rdp_plugin.c:2760
RemminaPluginRdpEventType type
Definition: rdp_plugin.h:191
gboolean(* protocol_plugin_init_get_savepassword)(RemminaProtocolWidget *gp)
Definition: plugin.h:197
-
static void rfi_uninit(rfContext *rfi)
Definition: rdp_plugin.c:2336
+
static void rfi_uninit(rfContext *rfi)
Definition: rdp_plugin.c:2337
gboolean(* register_plugin)(RemminaPlugin *plugin)
Definition: plugin.h:166
@@ -194,7 +194,7 @@ $(document).ready(function(){initNavTree('rdp__plugin_8c_source.html','');});
static int remmina_rdp_set_printers(void *user_data, unsigned flags, cups_dest_t *dest)
Callback function used by cupsEnumDests.
Definition: rdp_plugin.c:1151
GtkWidget * drawing_area
Definition: rdp_plugin.h:355
-
static gpointer sound_list[]
Definition: rdp_plugin.c:2717
+
static gpointer sound_list[]
Definition: rdp_plugin.c:2718
cairo_format_t cairo_format
Definition: rdp_plugin.h:367
int remmina_rdp_event_queue_ui_sync_retint(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1439
@@ -204,23 +204,23 @@ $(document).ready(function(){initNavTree('rdp__plugin_8c_source.html','');}); -
static const RemminaProtocolFeature remmina_rdp_features[]
Definition: rdp_plugin.c:2936
-
static RemminaFilePlugin remmina_rdpf
Definition: rdp_plugin.c:2978
-
G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
Definition: rdp_plugin.c:3024
-
static gpointer security_list[]
Definition: rdp_plugin.c:2726
+
static const RemminaProtocolFeature remmina_rdp_features[]
Definition: rdp_plugin.c:2937
+
static RemminaFilePlugin remmina_rdpf
Definition: rdp_plugin.c:2979
+
G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
Definition: rdp_plugin.c:3025
+
static gpointer security_list[]
Definition: rdp_plugin.c:2727
static BOOL rf_process_event_queue(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:211
void remmina_rdp_event_queue_ui_async(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1433
-
static void remmina_rdp_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
Definition: rdp_plugin.c:2611
+
static void remmina_rdp_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
Definition: rdp_plugin.c:2612
gboolean stop_reconnecting_requested
Definition: rdp_plugin.h:345
struct remmina_plugin_rdp_event::@42::@44 key_event
gboolean remmina_rdp_file_import_test(RemminaFilePlugin *plugin, const gchar *from_file)
Definition: rdp_file.c:41
gboolean remmina_rdp_file_export(RemminaFilePlugin *plugin, RemminaFile *remminafile, const gchar *to_file)
Definition: rdp_file.c:282
-
static gpointer colordepth_list[]
Definition: rdp_plugin.c:2661
+
static gpointer colordepth_list[]
Definition: rdp_plugin.c:2662
struct remmina_plugin_rdp_event::@42::@46 clipboard_formatlist
-
static gchar audio_tooltip[]
Definition: rdp_plugin.c:2776
+
static gchar audio_tooltip[]
Definition: rdp_plugin.c:2777
gint(* protocol_plugin_get_height)(RemminaProtocolWidget *gp)
Definition: plugin.h:170
gint(* protocol_plugin_init_auth)(RemminaProtocolWidget *gp, RemminaMessagePanelFlags pflags, const gchar *title, const gchar *default_username, const gchar *default_password, const gchar *default_domain, const gchar *password_prompt)
Definition: plugin.h:191
-
static gchar monitorids_tooltip[]
Definition: rdp_plugin.c:2802
+
static gchar monitorids_tooltip[]
Definition: rdp_plugin.c:2803
void remmina_rdp_event_send_delayed_monitor_layout(RemminaProtocolWidget *gp)
Definition: rdp_event.c:483
GAsyncQueue * event_queue
Definition: rdp_plugin.h:380
@@ -240,16 +240,16 @@ $(document).ready(function(){initNavTree('rdp__plugin_8c_source.html','');});
guint *(* pref_keymap_get_table)(const gchar *keymap)
Definition: plugin.h:232
void(* protocol_plugin_desktop_resize)(RemminaProtocolWidget *gp)
Definition: plugin.h:190
static BOOL remmina_rdp_post_connect(freerdp *instance)
Definition: rdp_plugin.c:697
-
static gchar usb_tooltip[]
Definition: rdp_plugin.c:2786
+
static gchar usb_tooltip[]
Definition: rdp_plugin.c:2787
struct remmina_plugin_rdp_event::@42::@48 clipboard_formatdatarequest
GtkWidget * remmina_rdp_settings_new(RemminaPrefPlugin *plugin)
Definition: rdp_settings.c:696
-
static RemminaPrefPlugin remmina_rdps
Definition: rdp_plugin.c:2993
+
static RemminaPrefPlugin remmina_rdps
Definition: rdp_plugin.c:2994
gint srcBpp
Definition: rdp_plugin.h:363
gchar *(* protocol_plugin_init_get_username)(RemminaProtocolWidget *gp)
Definition: plugin.h:194
-
static gboolean remmina_rdp_close_connection(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:2507
+
static gboolean remmina_rdp_close_connection(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:2508
void remmina_rdp_clipboard_free(rfContext *rfi)
Definition: rdp_cliprdr.c:939
@@ -261,14 +261,14 @@ $(document).ready(function(){initNavTree('rdp__plugin_8c_source.html','');});
RemminaScaleMode scale
Definition: rdp_plugin.h:330
static BOOL rf_keyboard_set_ime_status(rdpContext *context, UINT16 imeId, UINT32 imeState, UINT32 imeConvMode)
Definition: rdp_plugin.c:660
void remmina_rdp_OnChannelDisconnectedEventHandler(void *context, ChannelDisconnectedEventArgs *e)
Definition: rdp_channels.c:86
-
static char remmina_plugin_rdp_version[256]
Definition: rdp_plugin.c:2950
+
static char remmina_plugin_rdp_version[256]
Definition: rdp_plugin.c:2951
freerdp * instance
Definition: rdp_plugin.h:327
static BOOL rf_keyboard_set_indicators(rdpContext *context, UINT16 led_flags)
Definition: rdp_plugin.c:631
static BOOL remmina_rdp_gw_authenticate(freerdp *instance, char **username, char **password, char **domain)
Definition: rdp_plugin.c:831
DispClientContext * dispcontext
Definition: rdp_plugin.h:335
-
static char * buildconfig_strstr(const char *bc, const char *option)
Definition: rdp_plugin.c:3004
-
static gchar clientbuild_list[]
Definition: rdp_plugin.c:2756
+
static char * buildconfig_strstr(const char *bc, const char *option)
Definition: rdp_plugin.c:3005
+
static gchar clientbuild_list[]
Definition: rdp_plugin.c:2757
static DWORD remmina_rdp_verify_certificate(freerdp *instance, const char *common_name, const char *subject, const char *issuer, const char *fingerprint, BOOL host_mismatch) __attribute__((unused))
Definition: rdp_plugin.c:934
void remmina_rdp_event_update_scale(RemminaProtocolWidget *gp)
Definition: rdp_event.c:1094
@@ -277,33 +277,33 @@ $(document).ready(function(){initNavTree('rdp__plugin_8c_source.html','');});
void(* file_set_string)(RemminaFile *remminafile, const gchar *setting, const gchar *value)
Definition: plugin.h:218
void(* protocol_plugin_set_height)(RemminaProtocolWidget *gp, gint height)
Definition: plugin.h:171
static BOOL rf_end_paint(rdpContext *context)
Definition: rdp_plugin.c:530
-
static gpointer remmina_rdp_main_thread(gpointer data)
Definition: rdp_plugin.c:2396
+
static gpointer remmina_rdp_main_thread(gpointer data)
Definition: rdp_plugin.c:2397
void remmina_rdp_event_unfocus(RemminaProtocolWidget *gp)
Definition: rdp_event.c:1288
static BOOL remmina_rdp_pre_connect(freerdp *instance)
Definition: rdp_plugin.c:672
static gchar * remmina_rdp_find_prdriver(char *smap, char *prn)
Definition: rdp_plugin.c:1076
-
static gchar timeout_tooltip[]
Definition: rdp_plugin.c:2792
+
static gchar timeout_tooltip[]
Definition: rdp_plugin.c:2793
static gboolean remmina_rdp_tunnel_init(RemminaProtocolWidget *gp)
Definition: rdp_plugin.c:339
struct remmina_plugin_rdp_ui_object::@50::@57 event
gchar *(* protocol_plugin_start_direct_tunnel)(RemminaProtocolWidget *gp, gint default_port, gboolean port_plus)
Definition: plugin.h:181
void remmina_rdp_monitor_get(rfContext *rfi, gchar **monitorids, guint32 *maxwidth, guint32 *maxheight)
Set the MonitorIDs, the maxwidth and maxheight.
Definition: rdp_monitor.c:68
-
static const RemminaProtocolSetting remmina_rdp_basic_settings[]
Definition: rdp_plugin.c:2841
-
static gboolean remmina_rdp_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
Definition: rdp_plugin.c:2621
+
static const RemminaProtocolSetting remmina_rdp_basic_settings[]
Definition: rdp_plugin.c:2842
+
static gboolean remmina_rdp_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
Definition: rdp_plugin.c:2622
void remmina_rdp_event_event_push(RemminaProtocolWidget *gp, const RemminaPluginRdpEvent *e)
Definition: rdp_event.c:150
const gchar *(* file_get_string)(RemminaFile *remminafile, const gchar *setting)
Definition: plugin.h:219
-
static gchar drive_tooltip[]
Definition: rdp_plugin.c:2814
+
static gchar drive_tooltip[]
Definition: rdp_plugin.c:2815
RemminaPluginService * remmina_plugin_service
Definition: rdp_plugin.c:112
unsigned char * buffer
Definition: types.h:84
gboolean remmina_rdp_event_on_map(RemminaProtocolWidget *gp)
Definition: rdp_event.c:52
struct timeval clientformatdatarequest_tv
Definition: rdp_plugin.h:151
-
static gpointer gwtransp_list[]
Definition: rdp_plugin.c:2736
+
static gpointer gwtransp_list[]
Definition: rdp_plugin.c:2737
rdpContext context
Definition: rdp_plugin.h:320
-
static void remmina_rdp_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: rdp_plugin.c:2555
+
static void remmina_rdp_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: rdp_plugin.c:2556
gchar *(* protocol_plugin_init_get_domain)(RemminaProtocolWidget *gp)
Definition: plugin.h:196
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
gboolean(* is_main_thread)(void)
Definition: plugin.h:250
diff --git a/public/remmina__exec_8c_source.html b/public/remmina__exec_8c_source.html index ddfdce9c1..5311bffea 100644 --- a/public/remmina__exec_8c_source.html +++ b/public/remmina__exec_8c_source.html @@ -104,7 +104,7 @@ $(document).ready(function(){initNavTree('remmina__exec_8c_source.html','');});
gboolean remmina_icon_is_available(void)
Determine whenever the Remmina icon is available.
Definition: remmina_icon.c:330
-
GtkWidget * remmina_file_editor_new_full(const gchar *server, const gchar *protocol)
+
GtkWidget * remmina_file_editor_new_full(const gchar *server, const gchar *protocol)
GtkWidget * remmina_main_new(void)
void remmina_main_save_before_destroy()
Save the Remmina Main Window size and the expanded group before to close Remmina. ...
Definition: remmina_main.c:180
const gchar * remmina_exec_get_build_config(void)
Definition: remmina_exec.c:78
@@ -129,7 +129,7 @@ $(document).ready(function(){initNavTree('remmina__exec_8c_source.html','');});
void remmina_exec_exitremmina()
Definition: remmina_exec.c:89
gboolean remmina_pref_get_boolean(const gchar *key)
-
GtkWidget * remmina_file_editor_new_from_filename(const gchar *filename)
+
GtkWidget * remmina_file_editor_new_from_filename(const gchar *filename)
void remmina_main_destroy()
Definition: remmina_main.c:193
RemminaFile * remmina_file_new(void)
Definition: remmina_file.c:93
diff --git a/public/remmina__file__editor_8c.html b/public/remmina__file__editor_8c.html index 23d8483b2..e6dd55da9 100644 --- a/public/remmina__file__editor_8c.html +++ b/public/remmina__file__editor_8c.html @@ -357,7 +357,7 @@ Variables
-

Definition at line 552 of file remmina_file_editor.c.

+

Definition at line 551 of file remmina_file_editor.c.

@@ -415,7 +415,7 @@ Variables
-

Definition at line 1963 of file remmina_file_editor.c.

+

Definition at line 1960 of file remmina_file_editor.c.

@@ -471,7 +471,7 @@ Variables
-

Definition at line 1380 of file remmina_file_editor.c.

+

Definition at line 1377 of file remmina_file_editor.c.

@@ -521,7 +521,7 @@ Variables
-

Definition at line 648 of file remmina_file_editor.c.

+

Definition at line 647 of file remmina_file_editor.c.

@@ -549,7 +549,7 @@ Variables
-

Definition at line 1157 of file remmina_file_editor.c.

+

Definition at line 1154 of file remmina_file_editor.c.

@@ -617,7 +617,7 @@ Variables
-

Definition at line 819 of file remmina_file_editor.c.

+

Definition at line 816 of file remmina_file_editor.c.

@@ -694,7 +694,7 @@ Variables

Create checkbox + gtk_file_chooser for open files and select folders.

The code is wrong, because if the checkbox is not active, the value should be set to NULL and remove it from the remmina file. The problem is that this function knows nothing about the remmina file. This should be rewritten in a more generic way Please use REMMINA_PROTOCOL_SETTING_TYPE_TEXT

-

Definition at line 847 of file remmina_file_editor.c.

+

Definition at line 844 of file remmina_file_editor.c.

@@ -768,7 +768,7 @@ Variables
-

Definition at line 796 of file remmina_file_editor.c.

+

Definition at line 793 of file remmina_file_editor.c.

@@ -848,7 +848,7 @@ Variables
-

Definition at line 952 of file remmina_file_editor.c.

+

Definition at line 949 of file remmina_file_editor.c.

@@ -928,7 +928,7 @@ Variables
-

Definition at line 910 of file remmina_file_editor.c.

+

Definition at line 907 of file remmina_file_editor.c.

@@ -1080,7 +1080,7 @@ Variables
-

Definition at line 503 of file remmina_file_editor.c.

+

Definition at line 502 of file remmina_file_editor.c.

@@ -1130,7 +1130,7 @@ Variables
-

Definition at line 568 of file remmina_file_editor.c.

+

Definition at line 567 of file remmina_file_editor.c.

@@ -1204,7 +1204,7 @@ Variables
-

Definition at line 774 of file remmina_file_editor.c.

+

Definition at line 771 of file remmina_file_editor.c.

@@ -1254,7 +1254,7 @@ Variables
-

Definition at line 452 of file remmina_file_editor.c.

+

Definition at line 451 of file remmina_file_editor.c.

@@ -1298,7 +1298,7 @@ Variables
-

Definition at line 997 of file remmina_file_editor.c.

+

Definition at line 994 of file remmina_file_editor.c.

@@ -1336,7 +1336,7 @@ Variables
-

Definition at line 1212 of file remmina_file_editor.c.

+

Definition at line 1209 of file remmina_file_editor.c.

@@ -1404,7 +1404,7 @@ Variables
-

Definition at line 732 of file remmina_file_editor.c.

+

Definition at line 729 of file remmina_file_editor.c.

@@ -1484,7 +1484,7 @@ Variables
-

Definition at line 698 of file remmina_file_editor.c.

+

Definition at line 695 of file remmina_file_editor.c.

@@ -1552,7 +1552,7 @@ Variables
-

Definition at line 741 of file remmina_file_editor.c.

+

Definition at line 738 of file remmina_file_editor.c.

@@ -1638,7 +1638,7 @@ Variables
-

Definition at line 899 of file remmina_file_editor.c.

+

Definition at line 896 of file remmina_file_editor.c.

@@ -1676,7 +1676,7 @@ Variables
-

Definition at line 1973 of file remmina_file_editor.c.

+

Definition at line 1970 of file remmina_file_editor.c.

@@ -1696,7 +1696,7 @@ Variables
-

Definition at line 1990 of file remmina_file_editor.c.

+

Definition at line 1987 of file remmina_file_editor.c.

@@ -1724,7 +1724,7 @@ Variables
-

Definition at line 1898 of file remmina_file_editor.c.

+

Definition at line 1895 of file remmina_file_editor.c.

@@ -1772,7 +1772,7 @@ Variables
-

Definition at line 885 of file remmina_file_editor.c.

+

Definition at line 882 of file remmina_file_editor.c.

@@ -1816,7 +1816,7 @@ Variables
-

Definition at line 1941 of file remmina_file_editor.c.

+

Definition at line 1938 of file remmina_file_editor.c.

@@ -1836,7 +1836,7 @@ Variables
-

Definition at line 2133 of file remmina_file_editor.c.

+

Definition at line 2130 of file remmina_file_editor.c.

@@ -1856,7 +1856,7 @@ Variables
-

Definition at line 2153 of file remmina_file_editor.c.

+

Definition at line 2150 of file remmina_file_editor.c.

@@ -1876,7 +1876,7 @@ Variables
-

Definition at line 2004 of file remmina_file_editor.c.

+

Definition at line 2001 of file remmina_file_editor.c.

@@ -1896,7 +1896,7 @@ Variables
-

Definition at line 2172 of file remmina_file_editor.c.

+

Definition at line 2169 of file remmina_file_editor.c.

@@ -1926,7 +1926,7 @@ Variables
-

Definition at line 2139 of file remmina_file_editor.c.

+

Definition at line 2136 of file remmina_file_editor.c.

@@ -1964,7 +1964,7 @@ Variables
-

Definition at line 1892 of file remmina_file_editor.c.

+

Definition at line 1889 of file remmina_file_editor.c.

@@ -2002,7 +2002,7 @@ Variables
-

Definition at line 1843 of file remmina_file_editor.c.

+

Definition at line 1840 of file remmina_file_editor.c.

@@ -2040,7 +2040,7 @@ Variables
-

Definition at line 1785 of file remmina_file_editor.c.

+

Definition at line 1782 of file remmina_file_editor.c.

@@ -2116,7 +2116,7 @@ Variables
-

Definition at line 1823 of file remmina_file_editor.c.

+

Definition at line 1820 of file remmina_file_editor.c.

@@ -2155,7 +2155,7 @@ Variables

: Call remmina_file_editor_on_save

-

Definition at line 1865 of file remmina_file_editor.c.

+

Definition at line 1862 of file remmina_file_editor.c.

@@ -2193,7 +2193,7 @@ Variables
-

Definition at line 1417 of file remmina_file_editor.c.

+

Definition at line 1414 of file remmina_file_editor.c.

@@ -2221,7 +2221,7 @@ Variables
-

Definition at line 1461 of file remmina_file_editor.c.

+

Definition at line 1458 of file remmina_file_editor.c.

@@ -2249,7 +2249,7 @@ Variables
-

Definition at line 1475 of file remmina_file_editor.c.

+

Definition at line 1472 of file remmina_file_editor.c.

@@ -2350,7 +2350,7 @@ TODO: Responsive text size and line wrap.

-

Definition at line 395 of file remmina_file_editor.c.

+

Definition at line 394 of file remmina_file_editor.c.

@@ -2426,7 +2426,7 @@ TODO: Responsive text size and line wrap.

-

Definition at line 1719 of file remmina_file_editor.c.

+

Definition at line 1716 of file remmina_file_editor.c.

@@ -2464,7 +2464,7 @@ TODO: Responsive text size and line wrap.

-

Definition at line 540 of file remmina_file_editor.c.

+

Definition at line 539 of file remmina_file_editor.c.

@@ -2502,7 +2502,7 @@ TODO: Responsive text size and line wrap.

-

Definition at line 1614 of file remmina_file_editor.c.

+

Definition at line 1611 of file remmina_file_editor.c.

@@ -2552,7 +2552,7 @@ TODO: Responsive text size and line wrap.

-

Definition at line 1536 of file remmina_file_editor.c.

+

Definition at line 1533 of file remmina_file_editor.c.

@@ -2578,7 +2578,7 @@ TODO: Responsive text size and line wrap.

Initial value:
=
{
"0", N_("Password"),
"1", N_("SSH identity file"),
"2", N_("SSH agent"),
"3", N_("Public key (automatic)"),
"4", N_("Kerberos (GSSAPI)"),
NULL
}
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 1201 of file remmina_file_editor.c.

+

Definition at line 1198 of file remmina_file_editor.c.

diff --git a/public/remmina__file__editor_8c_source.html b/public/remmina__file__editor_8c_source.html index ddf136034..ae1cff2ce 100644 --- a/public/remmina__file__editor_8c_source.html +++ b/public/remmina__file__editor_8c_source.html @@ -86,7 +86,7 @@ $(document).ready(function(){initNavTree('remmina__file__editor_8c_source.html',
remmina_file_editor.c
-Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2009-2011 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-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 * assistance_toggle;
115  GtkWidget * assistance_file;
116  GtkWidget * assistance_password;
117  GtkWidget * assistance_file_label;
118  GtkWidget * assistance_password_label;
119 
120  GtkWidget * behavior_autostart_check;
121  GtkWidget * behavior_precommand_entry;
122  GtkWidget * behavior_postcommand_entry;
123  GtkWidget * behavior_lock_check;
124 
125  GtkWidget * ssh_tunnel_enabled_check;
126  GtkWidget * ssh_tunnel_loopback_check;
127  GtkWidget * ssh_tunnel_server_default_radio;
128  GtkWidget * ssh_tunnel_server_custom_radio;
129  GtkWidget * ssh_tunnel_server_entry;
130  GtkWidget * ssh_tunnel_auth_agent_radio;
131  GtkWidget * ssh_tunnel_auth_password_radio;
132  GtkWidget * ssh_tunnel_auth_password;
133  GtkWidget * ssh_tunnel_passphrase;
134  GtkWidget * ssh_tunnel_auth_publickey_radio;
135  GtkWidget * ssh_tunnel_auth_auto_publickey_radio;
136  GtkWidget * ssh_tunnel_auth_combo;
137  GtkWidget * ssh_tunnel_username_entry;
138  GtkWidget * ssh_tunnel_privatekey_chooser;
139  GtkWidget * ssh_tunnel_certfile_chooser;
140 
141  GHashTable * setting_widgets;
142 };
143 
145 {
146  TRACE_CALL(__func__);
147 }
148 
165  GtkWidget * failed_widget,
166  GError * err)
167 {
168  if (!err) {
169  err = NULL; // g_set_error doesn't like overwriting errors.
170  g_set_error(&err, 1, 1, _("Input is invalid."));
171  }
172 
173  if (!gfe || !failed_widget) {
174  g_critical("(%s): Parameters RemminaFileEditor 'gfe' or "
175  "GtkWidget* 'failed_widget' are 'NULL'!",
176  __func__);
177  return;
178  }
179 
180  gint widget_width = gtk_widget_get_allocated_width(failed_widget);
181  gint widget_height = gtk_widget_get_allocated_height(failed_widget);
182 
183  GtkWidget *err_label = gtk_label_new("");
184  GtkWidget *alert_icon = NULL;
185  GtkWindow *err_window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
186  GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
187  GdkWindow *window = gtk_widget_get_window(failed_widget);
188 
189  GtkAllocation allocation;
190  gint failed_widget_x, failed_widget_y;
191 
192  gchar *markup = g_strdup_printf("<span size='large'>%s</span>", err->message);
193 
194  // Setup err_window
195  gtk_window_set_decorated(err_window, FALSE);
196  gtk_window_set_type_hint(err_window, GDK_WINDOW_TYPE_HINT_TOOLTIP);
197  gtk_window_set_default_size(err_window, widget_width, widget_height);
198  gtk_window_set_title(err_window, "Error");
199  gtk_window_set_resizable(err_window, TRUE);
200 
201  // Move err_window under failed_widget
202  gtk_window_set_attached_to(err_window, failed_widget);
203  gtk_window_set_transient_for(err_window, GTK_WINDOW(gfe));
204  gdk_window_get_origin(GDK_WINDOW(window), &failed_widget_x, &failed_widget_y);
205  gtk_widget_get_allocation(failed_widget, &allocation);
206  failed_widget_x += allocation.x;
207  failed_widget_y += allocation.y + allocation.height;
208  gtk_window_move(err_window, failed_widget_x, failed_widget_y);
209 
210  // Setup label
211  gtk_label_set_selectable(GTK_LABEL(err_label), FALSE);
212  gtk_label_set_max_width_chars(GTK_LABEL(err_label), 1);
213  gtk_widget_set_hexpand(GTK_WIDGET(err_label), TRUE);
214  gtk_widget_set_vexpand(GTK_WIDGET(err_label), TRUE);
215  gtk_label_set_ellipsize(GTK_LABEL(err_label), PANGO_ELLIPSIZE_END);
216  gtk_label_set_line_wrap(GTK_LABEL(err_label), TRUE);
217  gtk_label_set_line_wrap_mode(GTK_LABEL(err_label), PANGO_WRAP_WORD_CHAR);
218  gtk_label_set_markup(GTK_LABEL(err_label), markup);
219 
220  alert_icon = gtk_image_new_from_icon_name("dialog-warning-symbolic",
221  GTK_ICON_SIZE_DND);
222 
223  // Fill icon and label into a box.
224  gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(alert_icon), FALSE, FALSE, 0);
225  gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(err_label), TRUE, TRUE, 5);
226 
227  // Attach box to err_window
228  gtk_container_add(GTK_CONTAINER(err_window), GTK_WIDGET(box));
229 
230  // Display everything.
231  gtk_widget_show_all(GTK_WIDGET(err_window));
232 
233  // Mouse click and focus-loss will delete the err_window.
234  g_signal_connect(G_OBJECT(err_window), "focus-out-event",
235  G_CALLBACK(gtk_window_close), NULL);
236  g_signal_connect(G_OBJECT(err_window), "button-press-event",
237  G_CALLBACK(gtk_window_close), NULL);
238 }
239 
240 #ifdef HAVE_LIBAVAHI_UI
241 
242 static void remmina_file_editor_browse_avahi(GtkWidget *button, RemminaFileEditor *gfe)
243 {
244  TRACE_CALL(__func__);
245  GtkWidget *dialog;
246  gchar *host;
247 
248  dialog = aui_service_dialog_new(_("Choose a Remote Desktop Server"),
249  GTK_WINDOW(gfe),
250  _("_Cancel"), GTK_RESPONSE_CANCEL,
251  _("_OK"), GTK_RESPONSE_ACCEPT,
252  NULL);
253 
254  gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gfe));
255  aui_service_dialog_set_resolve_service(AUI_SERVICE_DIALOG(dialog), TRUE);
256  aui_service_dialog_set_resolve_host_name(AUI_SERVICE_DIALOG(dialog), TRUE);
257  aui_service_dialog_set_browse_service_types(AUI_SERVICE_DIALOG(dialog),
258  gfe->priv->avahi_service_type, NULL);
259 
260  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
261  host = g_strdup_printf("[%s]:%i",
262  aui_service_dialog_get_host_name(AUI_SERVICE_DIALOG(dialog)),
263  aui_service_dialog_get_port(AUI_SERVICE_DIALOG(dialog)));
264  } else {
265  host = NULL;
266  }
267  gtk_widget_destroy(dialog);
268 
269  if (host) {
270  gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(gfe->priv->server_combo))), host);
271  g_free(host);
272  }
273 }
274 #endif
275 
276 static void remmina_file_editor_on_realize(GtkWidget *widget, gpointer user_data)
277 {
278  TRACE_CALL(__func__);
279  RemminaFileEditor *gfe;
280  GtkWidget *defaultwidget;
281 
282  gfe = REMMINA_FILE_EDITOR(widget);
283 
284  defaultwidget = gfe->priv->server_combo;
285 
286  if (defaultwidget) {
287  if (GTK_IS_EDITABLE(defaultwidget))
288  gtk_editable_select_region(GTK_EDITABLE(defaultwidget), 0, -1);
289  gtk_widget_grab_focus(defaultwidget);
290  }
291 }
292 
293 static void remmina_file_editor_destroy(GtkWidget *widget, gpointer data)
294 {
295  TRACE_CALL(__func__);
296  remmina_file_free(REMMINA_FILE_EDITOR(widget)->priv->remmina_file);
297  g_hash_table_destroy(REMMINA_FILE_EDITOR(widget)->priv->setting_widgets);
298  g_free(REMMINA_FILE_EDITOR(widget)->priv);
299 }
300 
301 static void remmina_file_editor_button_on_toggled(GtkToggleButton *togglebutton, GtkWidget *widget)
302 {
303  TRACE_CALL(__func__);
304  gtk_widget_set_sensitive(widget, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)));
305 }
306 
308 {
309  TRACE_CALL(__func__);
310  /* Create the notebook */
311  gfe->priv->config_container = gtk_notebook_new();
312  gfe->priv->config_viewport = gtk_viewport_new(NULL, NULL);
313  gfe->priv->config_scrollable = gtk_scrolled_window_new(NULL, NULL);
314  gtk_container_set_border_width(GTK_CONTAINER(gfe->priv->config_scrollable), 2);
315  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gfe->priv->config_scrollable),
316  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
317  gtk_widget_show(gfe->priv->config_scrollable);
318 
319  gtk_container_add(GTK_CONTAINER(gfe->priv->config_viewport), gfe->priv->config_container);
320  gtk_container_set_border_width(GTK_CONTAINER(gfe->priv->config_viewport), 2);
321  gtk_widget_show(gfe->priv->config_viewport);
322  gtk_container_add(GTK_CONTAINER(gfe->priv->config_scrollable), gfe->priv->config_viewport);
323  gtk_container_set_border_width(GTK_CONTAINER(gfe->priv->config_container), 2);
324  gtk_widget_show(gfe->priv->config_container);
325 
326  gtk_container_add(GTK_CONTAINER(gfe->priv->config_box), gfe->priv->config_scrollable);
327 }
328 
330  const gchar *stock_id, const gchar *label, gint rows, gint cols)
331 {
332  TRACE_CALL(__func__);
333  GtkWidget *tablabel;
334  GtkWidget *tabbody;
335  GtkWidget *grid;
336  GtkWidget *widget;
337 
338  tablabel = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
339  gtk_widget_show(tablabel);
340 
341  widget = gtk_image_new_from_icon_name(stock_id, GTK_ICON_SIZE_BUTTON);
342  gtk_box_pack_start(GTK_BOX(tablabel), widget, FALSE, FALSE, 0);
343  gtk_widget_show(widget);
344 
345  widget = gtk_label_new(label);
346  gtk_box_pack_start(GTK_BOX(tablabel), widget, FALSE, FALSE, 0);
347  gtk_widget_show(widget);
348 
349  tabbody = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
350  gtk_widget_show(tabbody);
351  gtk_notebook_append_page(GTK_NOTEBOOK(gfe->priv->config_container), tabbody, tablabel);
352 
353  grid = gtk_grid_new();
354  gtk_widget_show(grid);
355  gtk_grid_set_row_spacing(GTK_GRID(grid), 8);
356  gtk_grid_set_column_spacing(GTK_GRID(grid), 8);
357  gtk_container_set_border_width(GTK_CONTAINER(grid), 15);
358  gtk_box_pack_start(GTK_BOX(tabbody), grid, FALSE, FALSE, 0);
359 
360  return grid;
361 }
362 
363 #ifdef HAVE_LIBSSH
364 
366 {
367  TRACE_CALL(__func__);
368  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_server_entry),
369  gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_enabled_check)) &&
370  (gfe->priv->ssh_tunnel_server_custom_radio == NULL ||
371  gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_server_custom_radio))));
372 }
373 
374 
375 static void remmina_file_editor_assistance_enabled_check_on_toggled(GtkToggleButton *togglebutton,
376  RemminaFileEditor *gfe)
377 {
378  TRACE_CALL(__func__);
379  RemminaFileEditorPriv *priv = gfe->priv;
380  gboolean enabled = TRUE;
381 
382  if (gfe->priv->assistance_toggle) {
383  enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->assistance_toggle));
384  if (gfe->priv->assistance_file)
385  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->assistance_file), enabled);
386  if (gfe->priv->assistance_password)
387  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->assistance_password), enabled);
388  if (gfe->priv->assistance_file_label)
389  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->assistance_file_label), enabled);
390  if (gfe->priv->assistance_password_label)
391  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->assistance_password_label), enabled);
392  }
393 }
394 
395 static void remmina_file_editor_ssh_tunnel_enabled_check_on_toggled(GtkToggleButton *togglebutton,
397 {
398  TRACE_CALL(__func__);
399  RemminaFileEditorPriv *priv = gfe->priv;
400  gboolean enabled = TRUE;
401  gchar *p;
402  const gchar *cp;
403  const gchar *s = NULL;
404 
405  if (gfe->priv->ssh_tunnel_enabled_check) {
406  enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_enabled_check));
407  if (gfe->priv->ssh_tunnel_loopback_check)
408  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_loopback_check), enabled);
409  if (gfe->priv->ssh_tunnel_server_default_radio)
410  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_server_default_radio), enabled);
411  if (gfe->priv->ssh_tunnel_server_custom_radio)
412  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_server_custom_radio), enabled);
414  p = remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->protocol_combo));
415  // if (!(g_strcmp0(p, "SFTP") == 0 || g_strcmp0(p, "SSH") == 0)) {
416  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_username_entry), enabled);
417  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_auth_password), enabled);
418  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_auth_combo), enabled);
419  //}
420  g_free(p);
421  }
422  // remmina_file_editor_ssh_tunnel_auth_publickey_radio_on_toggled(NULL, gfe);
423  s = remmina_file_get_string(gfe->priv->remmina_file, "ssh_tunnel_privatekey");
424  if (s)
425  gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(gfe->priv->ssh_tunnel_privatekey_chooser), s);
426  s = remmina_file_get_string(gfe->priv->remmina_file, "ssh_tunnel_certfile");
427  if (s)
428  gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(gfe->priv->ssh_tunnel_certfile_chooser), s);
429 
430  if (gfe->priv->ssh_tunnel_username_entry)
431  if (enabled && gtk_entry_get_text(GTK_ENTRY(gfe->priv->ssh_tunnel_username_entry))[0] == '\0') {
432  cp = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_username");
433  gtk_entry_set_text(GTK_ENTRY(gfe->priv->ssh_tunnel_username_entry), cp ? cp : "");
434  }
435 
436  if (gfe->priv->ssh_tunnel_auth_password) {
437  if (enabled && gtk_entry_get_text(GTK_ENTRY(gfe->priv->ssh_tunnel_auth_password))[0] == '\0') {
438  cp = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_password");
439  gtk_entry_set_text(GTK_ENTRY(gfe->priv->ssh_tunnel_auth_password), cp ? cp : "");
440  }
441  }
442  if (gfe->priv->ssh_tunnel_passphrase) {
443  if (enabled && gtk_entry_get_text(GTK_ENTRY(gfe->priv->ssh_tunnel_passphrase))[0] == '\0') {
444  cp = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_passphrase");
445  gtk_entry_set_text(GTK_ENTRY(gfe->priv->ssh_tunnel_passphrase), cp ? cp : "");
446  }
447  }
448 }
449 
450 #endif
451 
452 static void remmina_file_editor_create_server(RemminaFileEditor *gfe, const RemminaProtocolSetting *setting, GtkWidget *grid,
453  gint row)
454 {
455  TRACE_CALL(__func__);
456  RemminaProtocolPlugin *plugin = gfe->priv->plugin;
457  GtkWidget *widget;
458 #ifdef HAVE_LIBAVAHI_UI
459  GtkWidget *hbox;
460 #endif
461  gchar *s;
462 
463  widget = gtk_label_new(_("Server"));
464  gtk_widget_show(widget);
465  gtk_widget_set_valign(widget, GTK_ALIGN_START);
466  gtk_widget_set_halign(widget, GTK_ALIGN_START);
467  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, row + 1);
468 
469  s = remmina_pref_get_recent(plugin->name);
470  widget = remmina_public_create_combo_entry(s, remmina_file_get_string(gfe->priv->remmina_file, "server"), TRUE);
471  gtk_widget_set_hexpand(widget, TRUE);
472  gtk_widget_show(widget);
473  gtk_widget_set_tooltip_markup(widget, _(server_tips));
474  gtk_entry_set_activates_default(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(widget))), TRUE);
475  gfe->priv->server_combo = widget;
476  g_free(s);
477 
478 #ifdef HAVE_LIBAVAHI_UI
479  if (setting->opt1) {
480  gfe->priv->avahi_service_type = (const gchar *)setting->opt1;
481 
482  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
483  gtk_widget_show(hbox);
484  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
485 
486  widget = gtk_button_new_with_label("…");
487  s = g_strdup_printf(_("Browse the network to find a %s server"), plugin->name);
488  gtk_widget_set_tooltip_text(widget, s);
489  g_free(s);
490  gtk_widget_show(widget);
491  gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
492  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_browse_avahi), gfe);
493 
494  gtk_grid_attach(GTK_GRID(grid), hbox, 1, row, 1, 1);
495  } else
496 #endif
497  {
498  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1);
499  }
500 }
501 
502 
503 static GtkWidget *remmina_file_editor_create_password(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gchar *setting_name)
504 {
505  TRACE_CALL(__func__);
506  GtkWidget *widget;
507 
508  widget = gtk_label_new(label);
509  gtk_widget_show(widget);
510 #if GTK_CHECK_VERSION(3, 12, 0)
511  gtk_widget_set_margin_end(widget, 40);
512 #else
513  gtk_widget_set_margin_right(widget, 40);
514 #endif
515  gtk_widget_set_valign(widget, GTK_ALIGN_START);
516  gtk_widget_set_halign(widget, GTK_ALIGN_START);
517  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
518 
519  widget = gtk_entry_new();
520  gtk_widget_show(widget);
521  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1);
522  gtk_entry_set_max_length(GTK_ENTRY(widget), 0);
523  gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
524  gtk_widget_set_hexpand(widget, TRUE);
525  gtk_entry_set_activates_default(GTK_ENTRY(widget), TRUE);
526  if (setting_name)
527  gtk_widget_set_name(widget, setting_name);
528 
529  if (value)
530  gtk_entry_set_text(GTK_ENTRY(widget), value);
531  /* Password view Toogle*/
532  if (setting_name) {
533  gtk_entry_set_icon_from_icon_name(GTK_ENTRY(widget), GTK_ENTRY_ICON_SECONDARY, "org.remmina.Remmina-password-reveal-symbolic");
534  gtk_entry_set_icon_activatable(GTK_ENTRY(widget), GTK_ENTRY_ICON_SECONDARY, TRUE);
535  g_signal_connect(widget, "icon-press", G_CALLBACK(remmina_main_toggle_password_view), NULL);
536  }
537  return widget;
538 }
539 
540 static void remmina_file_editor_update_resolution(GtkWidget *widget, RemminaFileEditor *gfe)
541 {
542  TRACE_CALL(__func__);
543  gchar *res_str;
544  res_str = g_strdup_printf("%dx%d",
545  remmina_file_get_int(gfe->priv->remmina_file, "resolution_width", 0),
546  remmina_file_get_int(gfe->priv->remmina_file, "resolution_height", 0));
547  remmina_public_load_combo_text_d(gfe->priv->resolution_custom_combo, remmina_pref.resolutions,
548  res_str, NULL);
549  g_free(res_str);
550 }
551 
552 static void remmina_file_editor_browse_resolution(GtkWidget *button, RemminaFileEditor *gfe)
553 {
554  TRACE_CALL(__func__);
555 
556  GtkDialog *dialog = remmina_string_list_new(FALSE, NULL);
559  remmina_string_list_set_titles(_("Resolutions"), _("Configure the available resolutions"));
560  gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gfe));
561  gtk_dialog_run(dialog);
562  g_free(remmina_pref.resolutions);
564  g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(remmina_file_editor_update_resolution), gfe);
565  gtk_widget_destroy(GTK_WIDGET(dialog));
566 }
567 
569  GtkWidget *grid, gint row)
570 {
571  TRACE_CALL(__func__);
572  GtkWidget *widget;
573  GtkWidget *hbox;
574  int resolution_w, resolution_h;
575  gchar *res_str;
577 
578  res_mode = remmina_file_get_int(gfe->priv->remmina_file, "resolution_mode", RES_INVALID);
579  resolution_w = remmina_file_get_int(gfe->priv->remmina_file, "resolution_width", -1);
580  resolution_h = remmina_file_get_int(gfe->priv->remmina_file, "resolution_height", -1);
581 
582  /* If resolution_mode is non-existent (-1), then we try to calculate it
583  * as we did before having resolution_mode */
584  if (res_mode == RES_INVALID) {
585  if (resolution_w <= 0 || resolution_h <= 0)
586  res_mode = RES_USE_INITIAL_WINDOW_SIZE;
587  else
588  res_mode = RES_USE_CUSTOM;
589  }
590  if (res_mode == RES_USE_CUSTOM)
591  res_str = g_strdup_printf("%dx%d", resolution_w, resolution_h);
592  else
593  res_str = NULL;
594 
595  widget = gtk_label_new(_("Resolution"));
596  gtk_widget_show(widget);
597  gtk_widget_set_valign(widget, GTK_ALIGN_START);
598  gtk_widget_set_halign(widget, GTK_ALIGN_START);
599  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
600 
601  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
602  widget = gtk_radio_button_new_with_label(NULL, _("Use initial window size"));
603  gtk_widget_show(widget);
604  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
605  gfe->priv->resolution_iws_radio = widget;
606  widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(gfe->priv->resolution_iws_radio), _("Use client resolution"));
607  gtk_widget_show(widget);
608  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
609  gfe->priv->resolution_auto_radio = widget;
610  gtk_grid_attach(GTK_GRID(grid), hbox, 1, row, 1, 1);
611  gtk_widget_show(hbox);
612 
613  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
614  gtk_widget_show(hbox);
615  gtk_grid_attach(GTK_GRID(grid), hbox, 1, row + 1, 1, 1);
616 
617  widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(gfe->priv->resolution_iws_radio), _("Custom"));
618  gtk_widget_show(widget);
619  gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
620  gfe->priv->resolution_custom_radio = widget;
621 
623  gtk_widget_show(widget);
624  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
625  gfe->priv->resolution_custom_combo = widget;
626 
627  widget = gtk_button_new_with_label("…");
628  gtk_widget_show(widget);
629  gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
630  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_browse_resolution), gfe);
631 
632  g_signal_connect(G_OBJECT(gfe->priv->resolution_custom_radio), "toggled",
633  G_CALLBACK(remmina_file_editor_button_on_toggled), gfe->priv->resolution_custom_combo);
634 
635  if (res_mode == RES_USE_CUSTOM)
636  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_custom_radio), TRUE);
637  else if (res_mode == RES_USE_CLIENT)
638  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_auto_radio), TRUE);
639  else
640  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_iws_radio), TRUE);
641 
642  gtk_widget_set_sensitive(gfe->priv->resolution_custom_combo, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_custom_radio)));
643 
644  g_free(res_str);
645 }
646 
647 
649  GtkWidget *grid, gint row)
650 {
651  TRACE_CALL(__func__);
652  GtkWidget *widget;
653  int assistance_mode;
654 
655  assistance_mode = remmina_file_get_int(gfe->priv->remmina_file, "assistance_mode", 0);
656 
657 
658  widget = gtk_toggle_button_new_with_label(_("Assistance Mode"));
659  gtk_widget_set_halign(widget, GTK_ALIGN_START);
660  gtk_widget_show(widget);
661  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
662  gtk_toggle_button_set_active(widget, remmina_file_get_int(gfe->priv->remmina_file, "assistance_mode", 0));
663  gfe->priv->assistance_toggle = widget;
664  g_signal_connect(widget, "toggled", G_CALLBACK(remmina_file_editor_assistance_enabled_check_on_toggled), gfe);
665 
666 
667  widget = gtk_label_new("Assistance file");
668  gtk_widget_set_halign(widget, GTK_ALIGN_END);
669  gtk_widget_show(widget);
670  gtk_grid_attach(GTK_GRID(grid), widget, 0, row+1, 1, 1);
671  gfe->priv->assistance_file_label = widget;
672 
673  widget = gtk_entry_new();
674  gtk_widget_set_halign(widget, GTK_ALIGN_START);
675  gtk_widget_show(widget);
676  gtk_entry_set_text(GTK_ENTRY(widget), remmina_file_get_string(gfe->priv->remmina_file, "assistance_file"));
677  gtk_grid_attach(GTK_GRID(grid), widget, 1, row+1, 1, 1);
678  gfe->priv->assistance_file = widget;
679 
680  widget = gtk_label_new("Assistance Password");
681  gtk_widget_set_halign(widget, GTK_ALIGN_END);
682  gtk_widget_show(widget);
683  gtk_grid_attach(GTK_GRID(grid), widget, 0, row+2, 1, 1);
684  gfe->priv->assistance_password_label = widget;
685 
686  widget = gtk_entry_new();
687  gtk_widget_set_halign(widget, GTK_ALIGN_START);
688  gtk_widget_show(widget);
689  gtk_entry_set_text(GTK_ENTRY(widget), remmina_file_get_string(gfe->priv->remmina_file, "assistance_pass"));
690  gtk_grid_attach(GTK_GRID(grid), widget, 1, row+2, 1, 1);
691  gfe->priv->assistance_password = widget;
692 
694 
695 }
696 
697 
698 static GtkWidget *remmina_file_editor_create_text2(RemminaFileEditor *gfe, GtkWidget *grid,
699  gint row, gint col, const gchar *label, const gchar *value, gint left,
700  gint right, gchar *setting_name)
701 {
702  TRACE_CALL(__func__);
703  GtkWidget *widget;
704 
705  widget = gtk_label_new(label);
706  gtk_widget_show(widget);
707 #if GTK_CHECK_VERSION(3, 12, 0)
708  gtk_widget_set_margin_start(widget, left);
709  gtk_widget_set_margin_end(widget, right);
710 #else
711  gtk_widget_set_margin_left(widget, left);
712  gtk_widget_set_margin_right(widget, right);
713 #endif
714  gtk_widget_set_valign(widget, GTK_ALIGN_START);
715  gtk_widget_set_halign(widget, GTK_ALIGN_START);
716  gtk_grid_attach(GTK_GRID(grid), widget, col, row, 1, 1);
717 
718  widget = gtk_entry_new();
719  gtk_widget_show(widget);
720  gtk_grid_attach(GTK_GRID(grid), widget, col + 1, row, 1, 1);
721  gtk_entry_set_max_length(GTK_ENTRY(widget), 300);
722  gtk_widget_set_hexpand(widget, TRUE);
723  if (setting_name)
724  gtk_widget_set_name(widget, setting_name);
725 
726  if (value)
727  gtk_entry_set_text(GTK_ENTRY(widget), value);
728 
729  return widget;
730 }
731 
732 static GtkWidget *remmina_file_editor_create_text(RemminaFileEditor *gfe, GtkWidget *grid,
733  gint row, gint col, const gchar *label, const gchar *value,
734  gchar *setting_name)
735 {
736  TRACE_CALL(__func__);
737  return remmina_file_editor_create_text2(gfe, grid, row, col, label, value, 0, 40,
738  setting_name);
739 }
740 
741 static GtkWidget *remmina_file_editor_create_textarea(RemminaFileEditor *gfe, GtkWidget *grid,
742  gint row, gint col, const gchar *label, const gchar *value,
743  gchar *setting_name)
744 {
745  TRACE_CALL(__func__);
746  GtkWidget *widget;
747  GtkTextView *view;
748  GtkTextBuffer *buffer;
749  GtkTextIter start;
750 
751  widget = gtk_text_view_new();
752  view = GTK_TEXT_VIEW(widget);
753  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
754  gtk_text_view_set_top_margin(GTK_TEXT_VIEW(view), 20);
755  gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(view), 20);
756  gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 20);
757  gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 20);
758  gtk_text_view_set_monospace(view, TRUE);
759  if (setting_name)
760  gtk_widget_set_name(widget, setting_name);
761  if (value) {
762  buffer = gtk_text_view_get_buffer(view);
763  gtk_text_buffer_set_text(buffer, value, -1);
764  gtk_text_buffer_get_start_iter(buffer, &start);
765  gtk_text_buffer_place_cursor(buffer, &start);
766  }
767  gtk_widget_show(widget);
768  gtk_widget_set_hexpand(widget, TRUE);
769  gtk_widget_set_size_request(GTK_WIDGET(view), 320, 300);
770  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
771  return widget;
772 }
773 
774 static GtkWidget *remmina_file_editor_create_select(RemminaFileEditor *gfe, GtkWidget *grid,
775  gint row, gint col, const gchar *label, const gpointer *list,
776  const gchar *value, gchar *setting_name)
777 {
778  TRACE_CALL(__func__);
779  GtkWidget *widget;
780 
781  widget = gtk_label_new(label);
782  gtk_widget_show(widget);
783  gtk_widget_set_valign(widget, GTK_ALIGN_START);
784  gtk_widget_set_halign(widget, GTK_ALIGN_START);
785  if (setting_name)
786  gtk_widget_set_name(widget, setting_name);
787  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
788 
789  widget = remmina_public_create_combo_map(list, value, FALSE, gfe->priv->plugin->domain);
790  gtk_widget_show(widget);
791  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1);
792 
793  return widget;
794 }
795 
796 static GtkWidget *remmina_file_editor_create_combo(RemminaFileEditor *gfe, GtkWidget *grid,
797  gint row, gint col, const gchar *label, const gchar *list,
798  const gchar *value, gchar *setting_name)
799 {
800  TRACE_CALL(__func__);
801  GtkWidget *widget;
802 
803  widget = gtk_label_new(label);
804  gtk_widget_show(widget);
805  gtk_widget_set_valign(widget, GTK_ALIGN_START);
806  gtk_widget_set_halign(widget, GTK_ALIGN_START);
807  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
808 
809  widget = remmina_public_create_combo_entry(list, value, FALSE);
810  gtk_widget_show(widget);
811  gtk_widget_set_hexpand(widget, TRUE);
812  if (setting_name)
813  gtk_widget_set_name(widget, setting_name);
814  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1);
815 
816  return widget;
817 }
818 
819 static GtkWidget *remmina_file_editor_create_check(RemminaFileEditor *gfe, GtkWidget *grid,
820  gint row, gint top, const gchar *label, gboolean value,
821  gchar *setting_name)
822 {
823  TRACE_CALL(__func__);
824  GtkWidget *widget;
825  widget = gtk_check_button_new_with_label(label);
826  gtk_widget_show(widget);
827  if (setting_name)
828  gtk_widget_set_name(widget, setting_name);
829  gtk_grid_attach(GTK_GRID(grid), widget, top, row, 1, 1);
830 
831  if (value)
832  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
833 
834  return widget;
835 }
836 
846 static GtkWidget *
847 remmina_file_editor_create_chooser(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label,
848  const gchar *value, gint type, gchar *setting_name)
849 {
850  TRACE_CALL(__func__);
851  GtkWidget *check;
852  GtkWidget *widget;
853  GtkWidget *hbox;
854 
855  widget = gtk_label_new(label);
856  gtk_widget_show(widget);
857  gtk_widget_set_valign(widget, GTK_ALIGN_START);
858  gtk_widget_set_halign(widget, GTK_ALIGN_START);
859  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
860 
861  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
862  gtk_widget_show(hbox);
863  gtk_grid_attach(GTK_GRID(grid), hbox, 1, row, 1, 1);
864 
865  check = gtk_check_button_new();
866  gtk_widget_show(check);
867  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), (value && value[0] == '/'));
868  gtk_box_pack_start(GTK_BOX(hbox), check, FALSE, FALSE, 0);
869 
870  widget = gtk_file_chooser_button_new(label, type);
871  if (setting_name)
872  gtk_widget_set_name(widget, setting_name);
873  gtk_widget_show(widget);
874  if (value)
875  gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(widget), value);
876  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
877 
878  g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(remmina_file_editor_button_on_toggled), widget);
879  remmina_file_editor_button_on_toggled(GTK_TOGGLE_BUTTON(check), widget);
880 
881  return widget;
882 }
883 
884 // used to filter out invalid characters for REMMINA_PROTOCOL_SETTING_TYPE_INT
885 void remmina_file_editor_int_setting_filter(GtkEditable *editable, const gchar *text,
886  gint length, gint *position, gpointer data)
887 {
888  for (int i = 0; i < length; i++) {
889  if (!isdigit(text[i]) && text[i] != '-') {
890  g_signal_stop_emission_by_name(G_OBJECT(editable), "insert-text");
891  return;
892  }
893  }
894 }
895 
896 // used to filter out invalid characters for REMMINA_PROTOCOL_SETTING_TYPE_DOUBLE
897 // '.' and ',' can't be used interchangeably! It depends on the language setting
898 // of the user.
899 void remmina_file_editor_double_setting_filter(GtkEditable *editable, const gchar *text,
900  gint length, gint *position, gpointer data)
901 {
902  for (int i = 0; i < length; i++) {
903  if (!isdigit(text[i]) && text[i] != '-' && text[i] != '.' && text[i] != ',') {
904  g_signal_stop_emission_by_name(G_OBJECT(editable), "insert-text");
905  return;
906  }
907  }
908 }
909 
910 static GtkWidget *remmina_file_editor_create_int(RemminaFileEditor *gfe, GtkWidget *grid,
911  gint row, gint col, const gchar *label, const gint value,
912  gint left, gint right, gchar *setting_name)
913 {
914  TRACE_CALL(__func__);
915  GtkWidget *widget;
916 
917  widget = gtk_label_new(label);
918  gtk_widget_show(widget);
919 #if GTK_CHECK_VERSION(3, 12, 0)
920  gtk_widget_set_margin_start(widget, left);
921  gtk_widget_set_margin_end(widget, right);
922 #else
923  gtk_widget_set_margin_left(widget, left);
924  gtk_widget_set_margin_right(widget, right);
925 #endif
926  gtk_widget_set_valign(widget, GTK_ALIGN_START);
927  gtk_widget_set_halign(widget, GTK_ALIGN_START);
928  gtk_grid_attach(GTK_GRID(grid), widget, col, row, 1, 1);
929 
930  widget = gtk_entry_new();
931  gtk_widget_show(widget);
932  gtk_grid_attach(GTK_GRID(grid), widget, col + 1, row, 1, 1);
933  gtk_entry_set_max_length(GTK_ENTRY(widget), 300);
934  gtk_widget_set_hexpand(widget, TRUE);
935  if (setting_name)
936  gtk_widget_set_name(widget, setting_name);
937 
938  // Convert int to str.
939  int length = snprintf(NULL, 0, "%d", value) + 1; // +1 '\0' byte
940  char *str = malloc(length);
941  snprintf(str, length, "%d", value);
942 
943  gtk_entry_set_text(GTK_ENTRY(widget), str);
944  free(str);
945 
946  g_signal_connect(G_OBJECT(widget), "insert-text",
947  G_CALLBACK(remmina_file_editor_int_setting_filter), NULL);
948 
949  return widget;
950 }
951 
953  GtkWidget *grid, gint row, gint col,
954  const gchar *label, gdouble value, gint left,
955  gint right, gchar *setting_name)
956 {
957  TRACE_CALL(__func__);
958  GtkWidget *widget;
959 
960  widget = gtk_label_new(label);
961  gtk_widget_show(widget);
962 #if GTK_CHECK_VERSION(3, 12, 0)
963  gtk_widget_set_margin_start(widget, left);
964  gtk_widget_set_margin_end(widget, right);
965 #else
966  gtk_widget_set_margin_left(widget, left);
967  gtk_widget_set_margin_right(widget, right);
968 #endif
969  gtk_widget_set_valign(widget, GTK_ALIGN_START);
970  gtk_widget_set_halign(widget, GTK_ALIGN_START);
971  gtk_grid_attach(GTK_GRID(grid), widget, col, row, 1, 1);
972 
973  widget = gtk_entry_new();
974  gtk_widget_show(widget);
975  gtk_grid_attach(GTK_GRID(grid), widget, col + 1, row, 1, 1);
976  gtk_entry_set_max_length(GTK_ENTRY(widget), 300);
977  gtk_widget_set_hexpand(widget, TRUE);
978  if (setting_name)
979  gtk_widget_set_name(widget, setting_name);
980 
981  // Convert double to str.
982  int length = snprintf(NULL, 0, "%.8g", value) + 1; // +1 '\0' byte
983  char *str = malloc(length);
984  snprintf(str, length, "%f", value);
985 
986  gtk_entry_set_text(GTK_ENTRY(widget), str);
987  free(str);
988 
989  g_signal_connect(G_OBJECT(widget), "insert-text",
991 
992  return widget;
993 }
994 
995 
996 
997 static void remmina_file_editor_create_settings(RemminaFileEditor *gfe, GtkWidget *grid,
998  const RemminaProtocolSetting *settings)
999 {
1000  TRACE_CALL(__func__);
1001  RemminaFileEditorPriv *priv = gfe->priv;
1002  GtkWidget *widget;
1003  gint grid_row = 0;
1004  gint grid_column = 0;
1005  gchar **strarr;
1006  gchar *setting_name;
1007  const gchar *escaped;
1008 
1009  while (settings->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
1010  setting_name = (gchar *)(remmina_plugin_manager_get_canonical_setting_name(settings));
1011  switch (settings->type) {
1013  remmina_file_editor_create_server(gfe, settings, grid, grid_row);
1014  break;
1015 
1017  widget = remmina_file_editor_create_password(gfe, grid, grid_row, 0,
1018  g_dgettext(priv->plugin->domain, settings->label),
1019  remmina_file_get_string(priv->remmina_file, setting_name),
1020  setting_name);
1021  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1022  grid_row++;
1023  break;
1024 
1026  remmina_file_editor_create_resolution(gfe, settings, grid, grid_row);
1027  grid_row ++;
1028  break;
1029 
1031  remmina_file_editor_create_assistance(gfe, settings, grid, grid_row);
1032  grid_row += 3;
1033  break;
1034 
1036  strarr = remmina_pref_keymap_groups();
1037  priv->keymap_combo = remmina_file_editor_create_select(gfe, grid,
1038  grid_row + 1, 0,
1039  _("Keyboard mapping"), (const gpointer *)strarr,
1040  remmina_file_get_string(priv->remmina_file, "keymap"),
1041  setting_name);
1042  g_strfreev(strarr);
1043  grid_row++;
1044  break;
1045 
1047  widget = remmina_file_editor_create_text(gfe, grid, grid_row, 0,
1048  g_dgettext(priv->plugin->domain, settings->label),
1049  remmina_file_get_string(priv->remmina_file, setting_name),
1050  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 
1058  escaped = remmina_file_get_string(priv->remmina_file, setting_name);
1059  escaped = g_uri_unescape_string(escaped, NULL);
1060  widget = remmina_file_editor_create_textarea(gfe, grid, grid_row, 0,
1061  g_dgettext(priv->plugin->domain, settings->label), escaped,
1062  setting_name);
1063  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1064  grid_row++;
1065  break;
1066 
1068  widget = remmina_file_editor_create_select(gfe, grid, grid_row, 0,
1069  g_dgettext(priv->plugin->domain, settings->label),
1070  (const gpointer *)settings->opt1,
1071  remmina_file_get_string(priv->remmina_file, setting_name),
1072  setting_name);
1073  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1074  if (settings->opt2)
1075  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1076  break;
1077 
1079  widget = remmina_file_editor_create_combo(gfe, grid, grid_row, 0,
1080  g_dgettext(priv->plugin->domain, settings->label),
1081  (const gchar *)settings->opt1,
1082  remmina_file_get_string(priv->remmina_file, setting_name),
1083  setting_name);
1084  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1085  if (settings->opt2)
1086  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1087  break;
1088 
1090  widget = remmina_file_editor_create_check(gfe, grid, grid_row, grid_column,
1091  g_dgettext(priv->plugin->domain, settings->label),
1092  remmina_file_get_int(priv->remmina_file, setting_name, FALSE),
1093  setting_name);
1094  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1095  if (settings->opt2)
1096  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1097  break;
1098 
1100  widget = remmina_file_editor_create_chooser(gfe, grid, grid_row, 0,
1101  g_dgettext(priv->plugin->domain, settings->label),
1102  remmina_file_get_string(priv->remmina_file, setting_name),
1103  GTK_FILE_CHOOSER_ACTION_OPEN, setting_name);
1104  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1105  if (settings->opt2)
1106  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1107  break;
1108 
1110  widget = remmina_file_editor_create_chooser(gfe, grid, grid_row, 0,
1111  g_dgettext(priv->plugin->domain, settings->label),
1112  remmina_file_get_string(priv->remmina_file, setting_name),
1113  GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1114  setting_name);
1115  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1116  if (settings->opt2)
1117  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1118  break;
1120  widget = remmina_file_editor_create_int(gfe, grid, grid_row, 0,
1121  g_dgettext(priv->plugin->domain, settings->label),
1122  remmina_file_get_int(priv->remmina_file, setting_name, 0),
1123  0, 40, setting_name);
1124  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1125  if (settings->opt2)
1126  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1127  grid_row++;
1128  break;
1130  widget = remmina_file_editor_create_double(gfe, grid, grid_row, 0,
1131  g_dgettext(priv->plugin->domain, settings->label),
1132  remmina_file_get_double(priv->remmina_file, setting_name, 0.0f),
1133  0, 40, setting_name);
1134  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1135  if (settings->opt2)
1136  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1137  grid_row++;
1138  break;
1139 
1140  default:
1141  break;
1142  }
1143  /* If the setting wants compactness, move to the next column */
1144  if (settings->compact)
1145  grid_column++;
1146  /* Add a new settings row and move to the first column
1147  * if the setting doesn’t want the compactness
1148  * or we already have two columns */
1149  if (!settings->compact || grid_column > 1) {
1150  grid_row++;
1151  grid_column = 0;
1152  }
1153  settings++;
1154  }
1155 }
1156 
1158 {
1159  TRACE_CALL(__func__);
1160  RemminaFileEditorPriv *priv = gfe->priv;
1161  GtkWidget *grid;
1162  GtkWidget *widget;
1163  const gchar *cs;
1164 
1165  /* The Behavior tab (implementation) */
1166  grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Behavior"), 20, 2);
1167 
1168  /* Execute Command frame */
1169  remmina_public_create_group(GTK_GRID(grid), _("Execute a Command"), 0, 1, 2);
1170 
1171  /* PRE connection command */
1172  cs = remmina_file_get_string(priv->remmina_file, "precommand");
1173  widget = remmina_file_editor_create_text2(gfe, grid, 2, 0, _("Before connecting"), cs, 24, 26, "precommand");
1174  priv->behavior_precommand_entry = widget;
1175  gtk_entry_set_placeholder_text(GTK_ENTRY(widget), _("command %h %u %t %U %p %g --option"));
1176  gtk_widget_set_tooltip_markup(widget, _(cmd_tips));
1177 
1178  /* POST connection command */
1179  cs = remmina_file_get_string(priv->remmina_file, "postcommand");
1180  widget = remmina_file_editor_create_text2(gfe, grid, 3, 0, _("After connecting"), cs, 24, 16, "postcommand");
1181  priv->behavior_postcommand_entry = widget;
1182  gtk_entry_set_placeholder_text(GTK_ENTRY(widget), _("/path/to/command -opt1 arg %h %u %t -opt2 %U %p %g"));
1183  gtk_widget_set_tooltip_markup(widget, _(cmd_tips));
1184 
1185  /* Startup frame */
1186  remmina_public_create_group(GTK_GRID(grid), _("Start-up"), 4, 1, 2);
1187 
1188  /* Autostart profile option */
1189  priv->behavior_autostart_check = remmina_file_editor_create_check(gfe, grid, 6, 1, _("Auto-start this profile"),
1190  remmina_file_get_int(priv->remmina_file, "enable-autostart", FALSE), "enable-autostart");
1191 
1192  /* Startup frame */
1193  remmina_public_create_group(GTK_GRID(grid), _("Connection profile security"), 8, 1, 2);
1194 
1195  /* Autostart profile option */
1196  priv->behavior_lock_check = remmina_file_editor_create_check(gfe, grid, 10, 1, _("Require password to connect or edit the profile"),
1197  remmina_file_get_int(priv->remmina_file, "profile-lock", FALSE), "profile-lock");
1198 }
1199 
1200 #ifdef HAVE_LIBSSH
1201 static gpointer ssh_tunnel_auth_list[] =
1202 {
1203  "0", N_("Password"),
1204  "1", N_("SSH identity file"),
1205  "2", N_("SSH agent"),
1206  "3", N_("Public key (automatic)"),
1207  "4", N_("Kerberos (GSSAPI)"),
1208  NULL
1209 };
1210 #endif
1211 
1213 {
1214  TRACE_CALL(__func__);
1215 #ifdef HAVE_LIBSSH
1216  RemminaFileEditorPriv *priv = gfe->priv;
1217  GtkWidget *grid;
1218  GtkWidget *widget;
1219  const gchar *cs;
1220  gchar *s;
1221  gchar *p;
1222  gint row = 0;
1223 
1224  if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_NONE)
1225  return;
1226 
1227  /* The SSH tab (implementation) */
1228  grid = remmina_file_editor_create_notebook_tab(gfe, NULL,
1229  _("SSH Tunnel"), 9, 3);
1230  widget = gtk_toggle_button_new_with_label(_("Enable SSH tunnel"));
1231  gtk_widget_set_halign(widget, GTK_ALIGN_START);
1232  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
1233  g_signal_connect(G_OBJECT(widget), "toggled",
1235  priv->ssh_tunnel_enabled_check = widget;
1236 
1237  widget = gtk_check_button_new_with_label(_("Tunnel via loopback address"));
1238  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 2, 1);
1239  priv->ssh_tunnel_loopback_check = widget;
1240 
1241  // 1
1242  row++;
1243  /* SSH Server group */
1244 
1245  switch (ssh_setting) {
1247  s = g_strdup_printf(_("Same server at port %i"), DEFAULT_SSH_PORT);
1248  widget = gtk_radio_button_new_with_label(NULL, s);
1249  g_free(s);
1250  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 3, 1);
1251  priv->ssh_tunnel_server_default_radio = widget;
1252  // 2
1253  row++;
1254 
1255  widget = gtk_radio_button_new_with_label_from_widget(
1256  GTK_RADIO_BUTTON(priv->ssh_tunnel_server_default_radio), _("Custom"));
1257  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
1258  g_signal_connect(G_OBJECT(widget), "toggled",
1260  priv->ssh_tunnel_server_custom_radio = widget;
1261 
1262  widget = gtk_entry_new();
1263  gtk_entry_set_max_length(GTK_ENTRY(widget), 100);
1264  gtk_widget_set_tooltip_markup(widget, _(server_tips2));
1265  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 2, 1);
1266  priv->ssh_tunnel_server_entry = widget;
1267  // 3
1268  row++;
1269  break;
1270 
1272  priv->ssh_tunnel_server_default_radio = NULL;
1273  priv->ssh_tunnel_server_custom_radio = NULL;
1274 
1275  priv->ssh_tunnel_server_entry = remmina_file_editor_create_text(gfe, grid, 1, 0,
1276  _("Server"), NULL, "ssh_reverse_tunnel_server");
1277  gtk_widget_set_tooltip_markup(priv->ssh_tunnel_server_entry, _(server_tips));
1278  // 2
1279  row++;
1280  break;
1283  priv->ssh_tunnel_server_default_radio = NULL;
1284  priv->ssh_tunnel_server_custom_radio = NULL;
1285  priv->ssh_tunnel_server_entry = NULL;
1286 
1287  break;
1288 
1289  default:
1290  break;
1291  }
1292 
1293  /* This is not used? */
1294  p = remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->protocol_combo));
1295  if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_SFTP) {
1296  widget = remmina_file_editor_create_text(gfe, grid, row, 1,
1297  _("Start-up path"), NULL, "start-up-path");
1298  cs = remmina_file_get_string(priv->remmina_file, "execpath");
1299  gtk_entry_set_text(GTK_ENTRY(widget), cs ? cs : "");
1300  g_hash_table_insert(priv->setting_widgets, "execpath", widget);
1301  // 2
1302  row++;
1303  }
1304 
1305  /* SSH Authentication frame */
1306  remmina_public_create_group(GTK_GRID(grid), _("SSH Authentication"), row, 6, 1);
1307  // 5
1308  row += 2;
1309 
1310  priv->ssh_tunnel_auth_combo = remmina_file_editor_create_select(gfe, grid, row, 0,
1311  _("Authentication type"),
1312  (const gpointer *)ssh_tunnel_auth_list,
1313  remmina_file_get_string(priv->remmina_file, "ssh_tunnel_auth"), "ssh_tunnel_auth");
1314  row++;
1315 
1316  if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_TUNNEL ||
1318  priv->ssh_tunnel_username_entry =
1319  remmina_file_editor_create_text(gfe, grid, row, 0,
1320  _("Username"), NULL, "ssh_tunnel_username");
1321  // 5
1322  row++;
1323  }
1324 
1325  widget = remmina_file_editor_create_password(gfe, grid, row, 0,
1326  _("Password"),
1327  remmina_file_get_string(priv->remmina_file, "ssh_tunnel_password"),
1328  "ssh_tunnel_password");
1329  priv->ssh_tunnel_auth_password = widget;
1330  row++;
1331 
1332  priv->ssh_tunnel_privatekey_chooser = remmina_file_editor_create_chooser(gfe, grid, row, 0,
1333  _("SSH private key file"),
1334  remmina_file_get_string(priv->remmina_file, "ssh_tunnel_privatekey"),
1335  GTK_FILE_CHOOSER_ACTION_OPEN, "ssh_tunnel_privatekey");
1336  row++;
1337 
1338  priv->ssh_tunnel_certfile_chooser = remmina_file_editor_create_chooser(gfe, grid, row, 0,
1339  _("SSH certificate file"),
1340  remmina_file_get_string(priv->remmina_file, "ssh_tunnel_certfile"),
1341  GTK_FILE_CHOOSER_ACTION_OPEN, "ssh_tunnel_certfile");
1342  row++;
1343 
1344  widget = gtk_label_new(_("Password to unlock private key"));
1345  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
1346  widget = gtk_entry_new();
1347  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 2, 1);
1348  gtk_entry_set_max_length(GTK_ENTRY(widget), 300);
1349  gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
1350  gtk_widget_set_hexpand(widget, TRUE);
1351  priv->ssh_tunnel_passphrase = widget;
1352  row++;
1353 
1354  /* Set the values */
1355  cs = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_server");
1356  if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_TUNNEL) {
1357  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_enabled_check),
1358  remmina_file_get_int(priv->remmina_file, "ssh_tunnel_enabled", FALSE));
1359  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_loopback_check),
1360  remmina_file_get_int(priv->remmina_file, "ssh_tunnel_loopback", FALSE));
1361 
1362  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cs ? priv->ssh_tunnel_server_custom_radio : priv->ssh_tunnel_server_default_radio), TRUE);
1363  gtk_entry_set_text(GTK_ENTRY(priv->ssh_tunnel_server_entry),
1364  cs ? cs : "");
1365  } else if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_REVERSE_TUNNEL) {
1366  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_enabled_check),
1367  remmina_file_get_int(priv->remmina_file, "ssh_tunnel_enabled", FALSE));
1368  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_loopback_check),
1369  remmina_file_get_int(priv->remmina_file, "ssh_tunnel_loopback", FALSE));
1370  gtk_entry_set_text(GTK_ENTRY(priv->ssh_tunnel_server_entry),
1371  cs ? cs : "");
1372  }
1373 
1375  gtk_widget_show_all(grid);
1376  g_free(p);
1377 #endif
1378 }
1379 
1381 {
1382  TRACE_CALL(__func__);
1383  RemminaFileEditorPriv *priv = gfe->priv;
1384  GtkWidget *grid;
1385 
1386  static const RemminaProtocolSetting notes_settings[] =
1387  {
1388  { REMMINA_PROTOCOL_SETTING_TYPE_TEXTAREA, "notes_text", NULL, FALSE, NULL, NULL },
1389  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
1390  };
1391 
1393 
1394  /* The Basic tab */
1395  if (priv->plugin->basic_settings) {
1396  grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Basic"), 20, 2);
1397  remmina_file_editor_create_settings(gfe, grid, priv->plugin->basic_settings);
1398  }
1399 
1400  /* The Advanced tab */
1401  if (priv->plugin->advanced_settings) {
1402  grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Advanced"), 20, 2);
1403  remmina_file_editor_create_settings(gfe, grid, priv->plugin->advanced_settings);
1404  }
1405 
1406  /* The Behavior tab */
1408 
1409  /* The SSH tab */
1410  remmina_file_editor_create_ssh_tunnel_tab(gfe, priv->plugin->ssh_setting);
1411 
1412  /* Notes tab */
1413  grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Notes"), 1, 1);
1414  remmina_file_editor_create_settings(gfe, grid, notes_settings);
1415 }
1416 
1418 {
1419  TRACE_CALL(__func__);
1420  RemminaFileEditorPriv *priv = gfe->priv;
1421  gchar *protocol;
1422 
1423  if (priv->config_container) {
1424  gtk_widget_destroy(priv->config_container);
1425  priv->config_container = NULL;
1426  gtk_widget_destroy(priv->config_viewport);
1427  priv->config_viewport = NULL;
1428  gtk_widget_destroy(priv->config_scrollable);
1429  priv->config_scrollable = NULL;
1430  }
1431 
1432  priv->server_combo = NULL;
1433  priv->resolution_iws_radio = NULL;
1434  priv->resolution_auto_radio = NULL;
1435  priv->resolution_custom_radio = NULL;
1436  priv->resolution_custom_combo = NULL;
1437  priv->keymap_combo = NULL;
1438 
1439  priv->ssh_tunnel_enabled_check = NULL;
1440  priv->ssh_tunnel_loopback_check = NULL;
1441  priv->ssh_tunnel_server_default_radio = NULL;
1442  priv->ssh_tunnel_server_custom_radio = NULL;
1443  priv->ssh_tunnel_server_entry = NULL;
1444  priv->ssh_tunnel_username_entry = NULL;
1445  priv->ssh_tunnel_auth_combo = NULL;
1446  priv->ssh_tunnel_auth_password = NULL;
1447  priv->ssh_tunnel_privatekey_chooser = NULL;
1448  priv->ssh_tunnel_certfile_chooser = NULL;
1449 
1450  g_hash_table_remove_all(priv->setting_widgets);
1451 
1452  protocol = remmina_public_combo_get_active_text(combo);
1453  if (protocol) {
1455  protocol);
1456  g_free(protocol);
1458  }
1459 }
1460 
1462 {
1463  TRACE_CALL(__func__);
1464  RemminaFileEditorPriv *priv = gfe->priv;
1465 
1466  remmina_file_set_string(priv->remmina_file, "precommand", gtk_entry_get_text(GTK_ENTRY(priv->behavior_precommand_entry)));
1467  remmina_file_set_string(priv->remmina_file, "postcommand", gtk_entry_get_text(GTK_ENTRY(priv->behavior_postcommand_entry)));
1468 
1469  gboolean autostart_enabled = (priv->behavior_autostart_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->behavior_autostart_check)) : FALSE);
1470  remmina_file_set_int(priv->remmina_file, "enable-autostart", autostart_enabled);
1471  gboolean lock_enabled = (priv->behavior_lock_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->behavior_lock_check)) : FALSE);
1472  remmina_file_set_int(priv->remmina_file, "profile-lock", lock_enabled);
1473 }
1474 
1476 {
1477  TRACE_CALL(__func__);
1478  RemminaFileEditorPriv *priv = gfe->priv;
1479  gboolean ssh_tunnel_enabled;
1480  int ssh_tunnel_auth;
1481 
1482  ssh_tunnel_enabled = (priv->ssh_tunnel_enabled_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_enabled_check)) : FALSE);
1483  remmina_file_set_int(priv->remmina_file,
1484  "ssh_tunnel_loopback",
1485  (priv->ssh_tunnel_loopback_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_loopback_check)) : FALSE));
1486  remmina_file_set_int(priv->remmina_file, "ssh_tunnel_enabled", ssh_tunnel_enabled);
1487  remmina_file_set_string(priv->remmina_file, "ssh_tunnel_auth",
1488  remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->ssh_tunnel_auth_combo)));
1489  remmina_file_set_string(priv->remmina_file, "ssh_tunnel_username",
1490  (ssh_tunnel_enabled ? gtk_entry_get_text(GTK_ENTRY(priv->ssh_tunnel_username_entry)) : NULL));
1492  priv->remmina_file,
1493  "ssh_tunnel_server",
1494  (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));
1495 
1496  ssh_tunnel_auth = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->ssh_tunnel_auth_combo));
1497 
1499  priv->remmina_file,
1500  "ssh_tunnel_auth",
1501  ssh_tunnel_auth);
1502 
1503  // If box is unchecked for private key and certfile file choosers,
1504  // set the string to NULL in the remmina file
1505  if (gtk_widget_get_sensitive(priv->ssh_tunnel_privatekey_chooser)) {
1507  priv->remmina_file,
1508  "ssh_tunnel_privatekey",
1509  (priv->ssh_tunnel_privatekey_chooser ? gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->ssh_tunnel_privatekey_chooser)) : NULL));
1510  }
1511  else {
1512  remmina_file_set_string(priv->remmina_file, "ssh_tunnel_privatekey", NULL);
1513  }
1514 
1515  if (gtk_widget_get_sensitive(priv->ssh_tunnel_certfile_chooser)) {
1517  priv->remmina_file,
1518  "ssh_tunnel_certfile",
1519  (priv->ssh_tunnel_certfile_chooser ? gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->ssh_tunnel_certfile_chooser)) : NULL));
1520  }
1521  else {
1522  remmina_file_set_string(priv->remmina_file, "ssh_tunnel_certfile", NULL);
1523  }
1524 
1526  priv->remmina_file,
1527  "ssh_tunnel_password",
1528  (ssh_tunnel_enabled && (ssh_tunnel_auth == SSH_AUTH_PASSWORD)) ? gtk_entry_get_text(GTK_ENTRY(priv->ssh_tunnel_auth_password)) : NULL);
1529 
1531  priv->remmina_file,
1532  "ssh_tunnel_passphrase",
1533  (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);
1534 }
1535 
1537  gchar * setting_name_to_validate,
1538  gconstpointer value,
1539  GError ** err)
1540 {
1541  if (!setting_name_to_validate || !value || !gfe) {
1542  if (!setting_name_to_validate) {
1543  g_critical(_("(%s: %i): Can't validate setting '%s' since 'value' or 'gfe' "
1544  "are NULL!"),
1545  __func__, __LINE__, setting_name_to_validate);
1546  } else {
1547  g_critical(_("(%s: %i): Can't validate user input since "
1548  "'setting_name_to_validate', 'value' or 'gfe' are NULL!"),
1549  __func__, __LINE__);
1550  }
1551  g_set_error(err, 1, 1, _("Internal error."));
1552  return FALSE;
1553  }
1554 
1555  if (strcmp(setting_name_to_validate, "notes_text") == 0) {
1556  // Not a plugin setting. Bail out early.
1557  return TRUE;
1558  }
1559 
1560  const RemminaProtocolSetting *setting_iter;
1561  RemminaProtocolPlugin *protocol_plugin;
1562  RemminaFileEditorPriv *priv = gfe->priv;
1563  protocol_plugin = priv->plugin;
1564 
1565  setting_iter = protocol_plugin->basic_settings;
1566  if (setting_iter) {
1567  // gboolean found = FALSE;
1568  while (setting_iter->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
1569  if (setting_iter->name == NULL) {
1570  g_error("Internal error: a setting name in protocol plugin %s is "
1571  "null. Please fix RemminaProtocolSetting struct content.",
1572  protocol_plugin->name);
1573  } else if ((gchar *)setting_name_to_validate) {
1574  if (strcmp((gchar *)setting_name_to_validate, setting_iter->name) == 0) {
1575  // found = TRUE;
1576 
1577  gpointer validator_data = setting_iter->validator_data;
1578  GCallback validator = setting_iter->validator;
1579 
1580  // Default behaviour is that everything is valid,
1581  // except a validator is given and its returned GError is not NULL.
1582  GError *err_ret = NULL;
1583 
1584  g_debug("Checking setting '%s' for validation.", setting_iter->name);
1585  if (validator != NULL) {
1586  // Looks weird but it calls the setting's validator
1587  // function using setting_name_to_validate, value and
1588  // validator_data as parameters and it returns a GError*.
1589  err_ret = ((GError * (*)(gpointer, gconstpointer, gpointer)) validator)(setting_name_to_validate, value, validator_data);
1590  }
1591 
1592  if (err_ret) {
1593  g_debug("it has a validator function and it had an error!");
1594  // pass err (returned value) to function caller.
1595  *err = err_ret;
1596  return FALSE;
1597  }
1598 
1599  break;
1600  }
1601  }
1602  setting_iter++;
1603  }
1604 
1605  // if (!found) {
1606  // TOO VERBOSE:
1607  // g_warning("%s is not a plugin setting!", setting_name_to_validate);
1608  // }
1609  }
1610 
1611  return TRUE;
1612 }
1613 
1615  GtkWidget ** failed_widget)
1616 {
1617  TRACE_CALL(__func__);
1618  RemminaFileEditorPriv *priv = gfe->priv;
1619  GHashTableIter iter;
1620  gpointer key;
1621  gpointer widget;
1622  GtkTextBuffer *buffer;
1623  gchar *escaped, *unescaped;
1624  GtkTextIter start, end;
1625 
1626  GError *err = NULL;
1627  *failed_widget = NULL;
1628 
1629  g_hash_table_iter_init(&iter, priv->setting_widgets);
1630  while (g_hash_table_iter_next(&iter, &key, &widget)) {
1631 
1632  // We don't want to save or validate grayed-out settings.
1633  // If widget is a file chooser, it was made not sensitive because
1634  // the box was unchecked. In that case, don't continue. The
1635  // relevant file strings will be set to NULL in the remmina file.
1636  if (!gtk_widget_get_sensitive(GTK_WIDGET(widget)) && !GTK_IS_FILE_CHOOSER(widget)) {
1637  g_debug("Grayed-out setting-widget '%s' will not be saved.",
1638  gtk_widget_get_name(widget));
1639  continue;
1640  }
1641 
1642  if (GTK_IS_ENTRY(widget)) {
1643  const gchar *value = gtk_entry_get_text(GTK_ENTRY(widget));
1644 
1645  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, value, &err)) {
1646  // Error while validating!
1647  // err should be set now.
1648  *failed_widget = widget;
1649  break;
1650  }
1651 
1652  remmina_file_set_string(priv->remmina_file, (gchar *)key, value);
1653  } else if (GTK_IS_TEXT_VIEW(widget)) {
1654  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
1655  gtk_text_buffer_get_start_iter(buffer, &start);
1656  gtk_text_buffer_get_end_iter(buffer, &end);
1657  unescaped = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1658  escaped = g_uri_escape_string(unescaped, NULL, TRUE);
1659 
1660  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, escaped, &err)) {
1661  // Error while validating!
1662  // err should be set now.
1663  *failed_widget = widget;
1664  break;
1665  }
1666 
1667  remmina_file_set_string(priv->remmina_file, (gchar *)key, escaped);
1668  g_free(escaped);
1669  } else if (GTK_IS_COMBO_BOX(widget)) {
1670  gchar *value = remmina_public_combo_get_active_text(GTK_COMBO_BOX(widget));
1671 
1672  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, value, &err)) {
1673  // Error while validating!
1674  // err should be set now.
1675  *failed_widget = widget;
1676  break;
1677  }
1678 
1679  remmina_file_set_string(priv->remmina_file, (gchar *)key, value);
1680  } else if (GTK_IS_FILE_CHOOSER(widget)) {
1681  gchar *value = gtk_widget_get_sensitive(GTK_WIDGET(widget)) ? gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)) : NULL;
1682 
1683  if (!gtk_widget_get_sensitive(GTK_WIDGET(widget))) {
1684  remmina_file_set_string(priv->remmina_file, (gchar *)key, value);
1685  continue;
1686  }
1687 
1688  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, value, &err)) {
1689  // Error while validating!
1690  // err should be set now.
1691  g_free(value);
1692  *failed_widget = widget;
1693  break;
1694  }
1695 
1696  remmina_file_set_string(priv->remmina_file, (gchar *)key, value);
1697  g_free(value);
1698  } else if (GTK_IS_TOGGLE_BUTTON(widget)) {
1699  gboolean value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
1700 
1701  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, &value, &err)) {
1702  // Error while validating!
1703  // err should be set now.
1704  *failed_widget = widget;
1705  break;
1706  }
1707 
1708  remmina_file_set_int(priv->remmina_file, (gchar *)key, value);
1709  }
1710  }
1711 
1712  if (err) {
1713  return err;
1714  }
1715 
1716  return NULL;
1717 }
1718 
1720  GtkWidget ** failed_widget)
1721 {
1722  TRACE_CALL(__func__);
1723  int res_w, res_h;
1724  gchar *custom_resolution;
1726 
1727  RemminaFileEditorPriv *priv = gfe->priv;
1728 
1729  remmina_file_set_string(priv->remmina_file, "name", gtk_entry_get_text(GTK_ENTRY(priv->name_entry)));
1730 
1731  remmina_file_set_string(priv->remmina_file, "labels", gtk_entry_get_text(GTK_ENTRY(priv->labels_entry)));
1732 
1733  remmina_file_set_string(priv->remmina_file, "group",
1734  (priv->group_combo ? remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->group_combo)) : NULL));
1735 
1736  remmina_file_set_string(priv->remmina_file, "protocol",
1737  remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->protocol_combo)));
1738 
1739  remmina_file_set_string(priv->remmina_file, "server",
1740  (priv->server_combo ? remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->server_combo)) : NULL));
1741 
1742  if (priv->resolution_auto_radio) {
1743  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->resolution_auto_radio))) {
1744  /* Resolution is set to auto (which means: Use client fullscreen resolution, aka use client resolution) */
1745  res_w = res_h = 0;
1746  res_mode = RES_USE_CLIENT;
1747  } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->resolution_iws_radio))) {
1748  /* Resolution is set to initial window size */
1749  res_w = res_h = 0;
1750  res_mode = RES_USE_INITIAL_WINDOW_SIZE;
1751  } else {
1752  /* Resolution is set to a value from the list */
1753  custom_resolution = remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->resolution_custom_combo));
1754  if (remmina_public_split_resolution_string(custom_resolution, &res_w, &res_h))
1755  res_mode = RES_USE_CUSTOM;
1756  else
1757  res_mode = RES_USE_INITIAL_WINDOW_SIZE;
1758  g_free(custom_resolution);
1759  }
1760  remmina_file_set_int(priv->remmina_file, "resolution_mode", res_mode);
1761  remmina_file_set_int(priv->remmina_file, "resolution_width", res_w);
1762  remmina_file_set_int(priv->remmina_file, "resolution_height", res_h);
1763  }
1764 
1765  if (priv->assistance_toggle){
1766  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->assistance_toggle))) {
1767  remmina_file_set_string(priv->remmina_file, "assistance_file", gtk_entry_get_text(priv->assistance_file));
1768  remmina_file_set_string(priv->remmina_file, "assistance_pass", gtk_entry_get_text(priv->assistance_password));
1769  remmina_file_set_int(priv->remmina_file, "assistance_mode", 1);
1770  }else{
1771  remmina_file_set_int(priv->remmina_file, "assistance_mode", 0);
1772  }
1773 
1774  }
1775 
1776  if (priv->keymap_combo)
1777  remmina_file_set_string(priv->remmina_file, "keymap",
1778  remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->keymap_combo)));
1779 
1782  return remmina_file_editor_update_settings(gfe, failed_widget);
1783 }
1784 
1785 static void remmina_file_editor_on_default(GtkWidget *button, RemminaFileEditor *gfe)
1786 {
1787  TRACE_CALL(__func__);
1788  RemminaFile *gf;
1789  GtkWidget *dialog;
1790 
1791  GtkWidget *failed_widget = NULL;
1792  GError *err = remmina_file_editor_update(gfe, &failed_widget);
1793  if (err) {
1794  g_warning(_("Couldn't validate user input. %s"), err->message);
1795  remmina_file_editor_show_validation_error_popup(gfe, failed_widget, err);
1796  return;
1797  }
1798 
1799  gf = remmina_file_dup(gfe->priv->remmina_file);
1800 
1802 
1803  /* Clear properties that should never be default */
1804  remmina_file_set_string(gf, "name", NULL);
1805  remmina_file_set_string(gf, "server", NULL);
1806  remmina_file_set_string(gf, "password", NULL);
1807  remmina_file_set_string(gf, "precommand", NULL);
1808  remmina_file_set_string(gf, "postcommand", NULL);
1809 
1810  remmina_file_set_string(gf, "ssh_tunnel_server", NULL);
1811  remmina_file_set_string(gf, "ssh_tunnel_password", NULL);
1812  remmina_file_set_string(gf, "ssh_tunnel_passphrase", NULL);
1813 
1814  remmina_file_save(gf);
1815  remmina_file_free(gf);
1816 
1817  dialog = gtk_message_dialog_new(GTK_WINDOW(gfe), GTK_DIALOG_MODAL, GTK_MESSAGE_INFO,
1818  GTK_BUTTONS_OK, _("Default settings saved."));
1819  gtk_dialog_run(GTK_DIALOG(dialog));
1820  gtk_widget_destroy(dialog);
1821 }
1822 
1823 static void remmina_file_editor_on_save(GtkWidget *button, RemminaFileEditor *gfe)
1824 {
1825  TRACE_CALL(__func__);
1826 
1827  GtkWidget *failed_widget = NULL;
1828  GError *err = remmina_file_editor_update(gfe, &failed_widget);
1829  if (err) {
1830  g_warning(_("Couldn't validate user input. %s"), err->message);
1831  remmina_file_editor_show_validation_error_popup(gfe, failed_widget, err);
1832  return;
1833  }
1834 
1836 
1837  remmina_file_save(gfe->priv->remmina_file);
1839 
1840  gtk_widget_destroy(GTK_WIDGET(gfe));
1841 }
1842 
1843 static void remmina_file_editor_on_connect(GtkWidget *button, RemminaFileEditor *gfe)
1844 {
1845  TRACE_CALL(__func__);
1846  RemminaFile *gf;
1847 
1848  GtkWidget *failed_widget = NULL;
1849  GError *err = remmina_file_editor_update(gfe, &failed_widget);
1850  if (err) {
1851  g_warning(_("Couldn't validate user input. %s"), err->message);
1852  remmina_file_editor_show_validation_error_popup(gfe, failed_widget, err);
1853  return;
1854  }
1855 
1856  gf = remmina_file_dup(gfe->priv->remmina_file);
1857  /* Put server into name for "Quick Connect" */
1858  if (remmina_file_get_filename(gf) == NULL)
1859  remmina_file_set_string(gf, "name", remmina_file_get_string(gf, "server"));
1860  gtk_widget_destroy(GTK_WIDGET(gfe));
1861  gf->prevent_saving = TRUE;
1862  rcw_open_from_file(gf);
1863 }
1864 
1865 static void remmina_file_editor_on_save_connect(GtkWidget *button, RemminaFileEditor *gfe)
1866 {
1867  TRACE_CALL(__func__);
1869  RemminaFile *gf;
1870 
1871  GtkWidget *failed_widget = NULL;
1872  GError *err = remmina_file_editor_update(gfe, &failed_widget);
1873  if (err) {
1874  g_warning(_("Couldn't validate user input. %s"), err->message);
1875  remmina_file_editor_show_validation_error_popup(gfe, failed_widget, err);
1876  return;
1877  }
1878 
1880 
1881  remmina_file_save(gfe->priv->remmina_file);
1883 
1884  gf = remmina_file_dup(gfe->priv->remmina_file);
1885  /* Put server into name for Quick Connect */
1886  if (remmina_file_get_filename(gf) == NULL)
1887  remmina_file_set_string(gf, "name", remmina_file_get_string(gf, "server"));
1888  gtk_widget_destroy(GTK_WIDGET(gfe));
1889  rcw_open_from_file(gf);
1890 }
1891 
1892 static void remmina_file_editor_on_cancel(GtkWidget *button, RemminaFileEditor *gfe)
1893 {
1894  TRACE_CALL(__func__);
1895  gtk_widget_destroy(GTK_WIDGET(gfe));
1896 }
1897 
1899 {
1900  TRACE_CALL(__func__);
1901  RemminaFileEditorPriv *priv;
1902  GtkWidget *widget;
1903 
1904  priv = g_new0(RemminaFileEditorPriv, 1);
1905  gfe->priv = priv;
1906 
1907  /* Create the editor dialog */
1908  gtk_window_set_title(GTK_WINDOW(gfe), _("Remote Connection Profile"));
1909 
1910  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("_Cancel")), GTK_RESPONSE_CANCEL);
1911  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_cancel), gfe);
1912 
1913  /* Default button */
1914  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("Save as Default")), GTK_RESPONSE_OK);
1915  gtk_widget_set_tooltip_text(GTK_WIDGET(widget), _("Use the current settings as the default for all new connection profiles"));
1916  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_default), gfe);
1917 
1918  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("_Save")), GTK_RESPONSE_APPLY);
1919  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_save), gfe);
1920  gtk_widget_set_sensitive(widget, FALSE);
1921  priv->save_button = widget;
1922 
1923  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("Connect")), GTK_RESPONSE_ACCEPT);
1924  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_connect), gfe);
1925 
1926  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("_Save and Connect")), GTK_RESPONSE_OK);
1927  gtk_widget_set_can_default(widget, TRUE);
1928  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_save_connect), gfe);
1929 
1930  gtk_dialog_set_default_response(GTK_DIALOG(gfe), GTK_RESPONSE_OK);
1931  gtk_window_set_default_size(GTK_WINDOW(gfe), 800, 600);
1932 
1933  g_signal_connect(G_OBJECT(gfe), "destroy", G_CALLBACK(remmina_file_editor_destroy), NULL);
1934  g_signal_connect(G_OBJECT(gfe), "realize", G_CALLBACK(remmina_file_editor_on_realize), NULL);
1935 
1936  priv->setting_widgets = g_hash_table_new(g_str_hash, g_str_equal);
1937 
1938  remmina_widget_pool_register(GTK_WIDGET(gfe));
1939 }
1940 
1941 static gboolean remmina_file_editor_iterate_protocol(gchar *protocol, RemminaPlugin *plugin, gpointer data)
1942 {
1943  TRACE_CALL(__func__);
1944  RemminaFileEditor *gfe = REMMINA_FILE_EDITOR(data);
1945  GtkListStore *store;
1946  GtkTreeIter iter;
1947  gboolean first;
1948 
1949  store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(gfe->priv->protocol_combo)));
1950 
1951  first = !gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1952 
1953  gtk_list_store_append(store, &iter);
1954  gtk_list_store_set(store, &iter, 0, protocol, 1, g_dgettext(plugin->domain, plugin->description), 2,
1955  ((RemminaProtocolPlugin *)plugin)->icon_name, -1);
1956 
1957  if (first || g_strcmp0(protocol, remmina_file_get_string(gfe->priv->remmina_file, "protocol")) == 0)
1958  gtk_combo_box_set_active_iter(GTK_COMBO_BOX(gfe->priv->protocol_combo), &iter);
1959 
1960  return FALSE;
1961 }
1962 
1964 {
1965  TRACE_CALL(__func__);
1966  RemminaFileEditorPriv *priv;
1967 
1968  priv = gfe->priv;
1969  gtk_widget_set_sensitive(priv->group_combo, TRUE);
1970  gtk_widget_set_sensitive(priv->save_button, TRUE);
1971 }
1972 
1973 static void remmina_file_editor_entry_on_changed(GtkEditable *editable, RemminaFileEditor *gfe)
1974 {
1975  TRACE_CALL(__func__);
1976  RemminaFileEditorPriv *priv;
1977 
1978  priv = gfe->priv;
1979  if (remmina_file_get_filename(priv->remmina_file) == NULL) {
1980  remmina_file_generate_filename(priv->remmina_file);
1981  /* TODO: Probably to be removed */
1983  } else {
1984  remmina_file_delete(remmina_file_get_filename(priv->remmina_file));
1985  remmina_file_generate_filename(priv->remmina_file);
1987  }
1988 }
1989 
1991 {
1992  TRACE_CALL(__func__);
1993  RemminaFileEditorPriv *priv;
1994 
1995  priv = gfe->priv;
1996  if (remmina_file_get_filename(priv->remmina_file) == NULL) {
1997  remmina_file_generate_filename(priv->remmina_file);
1998  } else {
1999  remmina_file_delete(remmina_file_get_filename(priv->remmina_file));
2000  remmina_file_generate_filename(priv->remmina_file);
2001  }
2002 }
2003 
2005 {
2006  TRACE_CALL(__func__);
2007  RemminaFileEditor *gfe;
2008  RemminaFileEditorPriv *priv;
2009  GtkWidget *grid;
2010  GtkWidget *widget;
2011  gchar *groups;
2012  gchar *s;
2013  const gchar *cs;
2014 
2015  gfe = REMMINA_FILE_EDITOR(g_object_new(REMMINA_TYPE_FILE_EDITOR, NULL));
2016  priv = gfe->priv;
2017  priv->remmina_file = remminafile;
2018 
2019  if (remmina_file_get_filename(remminafile) == NULL)
2020  gtk_dialog_set_response_sensitive(GTK_DIALOG(gfe), GTK_RESPONSE_APPLY, FALSE);
2021 
2022  /* Create the "Profile" group on the top (for name and protocol) */
2023  grid = gtk_grid_new();
2024  gtk_widget_show(grid);
2025  gtk_grid_set_row_spacing(GTK_GRID(grid), 4);
2026  gtk_grid_set_column_spacing(GTK_GRID(grid), 8);
2027  gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE);
2028  gtk_container_set_border_width(GTK_CONTAINER(grid), 8);
2029  gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(gfe))), grid, FALSE, FALSE, 2);
2030 
2031  // remmina_public_create_group(GTK_GRID(grid), _("Profile"), 0, 4, 3);
2032 
2033  gboolean profile_file_exists = (remmina_file_get_filename(remminafile) != NULL);
2034 
2035  /* Profile: Name */
2036  widget = gtk_label_new(_("Name"));
2037  gtk_widget_show(widget);
2038  gtk_widget_set_valign(widget, GTK_ALIGN_START);
2039  gtk_widget_set_halign(widget, GTK_ALIGN_START);
2040  gtk_grid_attach(GTK_GRID(grid), widget, 0, 3, 2, 1);
2041  gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
2042 
2043  widget = gtk_entry_new();
2044  gtk_widget_show(widget);
2045  gtk_grid_attach(GTK_GRID(grid), widget, 1, 3, 3, 1);
2046  gtk_entry_set_max_length(GTK_ENTRY(widget), 100);
2047  priv->name_entry = widget;
2048 
2049  if (!profile_file_exists) {
2050  gtk_entry_set_text(GTK_ENTRY(widget), _("Quick Connect"));
2051 #if GTK_CHECK_VERSION(3, 16, 0)
2052  gtk_entry_grab_focus_without_selecting(GTK_ENTRY(widget));
2053 #endif
2054  g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(remmina_file_editor_entry_on_changed), gfe);
2055  } else {
2056  cs = remmina_file_get_string(remminafile, "name");
2057  gtk_entry_set_text(GTK_ENTRY(widget), cs ? cs : "");
2058  }
2059 
2060  /* Profile: Group */
2061  widget = gtk_label_new(_("Group"));
2062  gtk_widget_show(widget);
2063  gtk_widget_set_valign(widget, GTK_ALIGN_START);
2064  gtk_widget_set_halign(widget, GTK_ALIGN_START);
2065  gtk_grid_attach(GTK_GRID(grid), widget, 0, 6, 2, 1);
2066 
2068  priv->group_combo = remmina_public_create_combo_entry(groups, remmina_file_get_string(remminafile, "group"), FALSE);
2069  g_free(groups);
2070  gtk_widget_show(priv->group_combo);
2071  gtk_grid_attach(GTK_GRID(grid), priv->group_combo, 1, 6, 3, 1);
2072  gtk_widget_set_sensitive(priv->group_combo, FALSE);
2073 
2074  s = g_strdup_printf(_("Use '%s' as subgroup delimiter"), "/");
2075  gtk_widget_set_tooltip_text(priv->group_combo, s);
2076  g_free(s);
2077 
2078  /* Profile: Labels */
2079  widget = gtk_label_new(_("Labels"));
2080  gtk_widget_show(widget);
2081  gtk_widget_set_valign(widget, GTK_ALIGN_START);
2082  gtk_widget_set_halign(widget, GTK_ALIGN_START);
2083  gtk_grid_attach(GTK_GRID(grid), widget, 0, 9, 2, 1);
2084  gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
2085 
2086  widget = gtk_entry_new();
2087  gtk_widget_show(widget);
2088  gtk_grid_attach(GTK_GRID(grid), widget, 1, 9, 3, 1);
2089  gtk_entry_set_max_length(GTK_ENTRY(widget), 255);
2090  priv->labels_entry = widget;
2091 
2092  if (!profile_file_exists) {
2093  gtk_widget_set_tooltip_text(widget, _("Label1,Label2"));
2094 #if GTK_CHECK_VERSION(3, 16, 0)
2095  gtk_entry_grab_focus_without_selecting(GTK_ENTRY(widget));
2096 #endif
2097  g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(remmina_file_editor_entry_on_changed), gfe);
2098  } else {
2099  cs = remmina_file_get_string(remminafile, "labels");
2100  gtk_entry_set_text(GTK_ENTRY(widget), cs ? cs : "");
2101  }
2102 
2103  /* Profile: Protocol */
2104  widget = gtk_label_new(_("Protocol"));
2105  gtk_widget_show(widget);
2106  gtk_widget_set_valign(widget, GTK_ALIGN_START);
2107  gtk_widget_set_halign(widget, GTK_ALIGN_START);
2108  gtk_grid_attach(GTK_GRID(grid), widget, 0, 12, 2, 1);
2109 
2110  widget = remmina_public_create_combo(TRUE);
2111  gtk_widget_show(widget);
2112  gtk_grid_attach(GTK_GRID(grid), widget, 1, 12, 3, 1);
2113  priv->protocol_combo = widget;
2115  g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(remmina_file_editor_protocol_combo_on_changed), gfe);
2116 
2117  /* Create the "Preference" frame */
2118  widget = gtk_event_box_new();
2119  gtk_widget_show(widget);
2120  gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(gfe))), widget, TRUE, TRUE, 2);
2121  priv->config_box = widget;
2122 
2123  priv->config_container = NULL;
2124  priv->config_scrollable = NULL;
2125 
2126  remmina_file_editor_protocol_combo_on_changed(GTK_COMBO_BOX(priv->protocol_combo), gfe);
2127 
2129 
2130  return GTK_WIDGET(gfe);
2131 }
2132 
2133 GtkWidget *remmina_file_editor_new(void)
2134 {
2135  TRACE_CALL(__func__);
2136  return remmina_file_editor_new_full(NULL, NULL);
2137 }
2138 
2139 GtkWidget *remmina_file_editor_new_full(const gchar *server, const gchar *protocol)
2140 {
2141  TRACE_CALL(__func__);
2142  RemminaFile *remminafile;
2143 
2144  remminafile = remmina_file_new();
2145  if (server)
2146  remmina_file_set_string(remminafile, "server", server);
2147  if (protocol)
2148  remmina_file_set_string(remminafile, "protocol", protocol);
2149 
2150  return remmina_file_editor_new_from_file(remminafile);
2151 }
2152 
2153 GtkWidget *remmina_file_editor_new_copy(const gchar *filename)
2154 {
2155  TRACE_CALL(__func__);
2156  RemminaFile *remminafile;
2157  GtkWidget *dialog;
2158 
2159  remminafile = remmina_file_copy(filename);
2160 
2161  if (remminafile) {
2162  return remmina_file_editor_new_from_file(remminafile);
2163  } else {
2164  dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
2165  _("Could not find the file “%s”."), filename);
2166  gtk_dialog_run(GTK_DIALOG(dialog));
2167  gtk_widget_destroy(dialog);
2168  return NULL;
2169  }
2170 }
2171 
2172 GtkWidget *remmina_file_editor_new_from_filename(const gchar *filename)
2173 {
2174  TRACE_CALL(__func__);
2175  RemminaFile *remminafile;
2176 
2177  remminafile = remmina_file_manager_load_file(filename);
2178  if (remminafile) {
2179  if (remmina_file_get_int(remminafile, "profile-lock", FALSE) && remmina_unlock_new(remmina_main_get_window()) == 0)
2180  return NULL;
2181  return remmina_file_editor_new_from_file(remminafile);
2182  } else {
2183  GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
2184  _("Could not find the file “%s”."), filename);
2185  gtk_dialog_run(GTK_DIALOG(dialog));
2186  gtk_widget_destroy(dialog);
2187  return NULL;
2188  }
2189 }
+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 * assistance_toggle;
115  GtkWidget * assistance_file;
116  GtkWidget * assistance_password;
117  GtkWidget * assistance_file_label;
118  GtkWidget * assistance_password_label;
119 
120  GtkWidget * behavior_autostart_check;
121  GtkWidget * behavior_precommand_entry;
122  GtkWidget * behavior_postcommand_entry;
123  GtkWidget * behavior_lock_check;
124 
125  GtkWidget * ssh_tunnel_enabled_check;
126  GtkWidget * ssh_tunnel_loopback_check;
127  GtkWidget * ssh_tunnel_server_default_radio;
128  GtkWidget * ssh_tunnel_server_custom_radio;
129  GtkWidget * ssh_tunnel_server_entry;
130  GtkWidget * ssh_tunnel_auth_agent_radio;
131  GtkWidget * ssh_tunnel_auth_password_radio;
132  GtkWidget * ssh_tunnel_auth_password;
133  GtkWidget * ssh_tunnel_passphrase;
134  GtkWidget * ssh_tunnel_auth_publickey_radio;
135  GtkWidget * ssh_tunnel_auth_auto_publickey_radio;
136  GtkWidget * ssh_tunnel_auth_combo;
137  GtkWidget * ssh_tunnel_username_entry;
138  GtkWidget * ssh_tunnel_privatekey_chooser;
139  GtkWidget * ssh_tunnel_certfile_chooser;
140 
141  GHashTable * setting_widgets;
142 };
143 
145 {
146  TRACE_CALL(__func__);
147 }
148 
165  GtkWidget * failed_widget,
166  GError * err)
167 {
168  if (!err) {
169  err = NULL; // g_set_error doesn't like overwriting errors.
170  g_set_error(&err, 1, 1, _("Input is invalid."));
171  }
172 
173  if (!gfe || !failed_widget) {
174  g_critical("(%s): Parameters RemminaFileEditor 'gfe' or "
175  "GtkWidget* 'failed_widget' are 'NULL'!",
176  __func__);
177  return;
178  }
179 
180  gint widget_width = gtk_widget_get_allocated_width(failed_widget);
181  gint widget_height = gtk_widget_get_allocated_height(failed_widget);
182 
183  GtkWidget *err_label = gtk_label_new("");
184  GtkWidget *alert_icon = NULL;
185  GtkWindow *err_window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
186  GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
187  GdkWindow *window = gtk_widget_get_window(failed_widget);
188 
189  GtkAllocation allocation;
190  gint failed_widget_x, failed_widget_y;
191 
192  gchar *markup = g_strdup_printf("<span size='large'>%s</span>", err->message);
193 
194  // Setup err_window
195  gtk_window_set_decorated(err_window, FALSE);
196  gtk_window_set_type_hint(err_window, GDK_WINDOW_TYPE_HINT_TOOLTIP);
197  gtk_window_set_default_size(err_window, widget_width, widget_height);
198  gtk_window_set_title(err_window, "Error");
199  gtk_window_set_resizable(err_window, TRUE);
200 
201  // Move err_window under failed_widget
202  gtk_window_set_attached_to(err_window, failed_widget);
203  gtk_window_set_transient_for(err_window, GTK_WINDOW(gfe));
204  gdk_window_get_origin(GDK_WINDOW(window), &failed_widget_x, &failed_widget_y);
205  gtk_widget_get_allocation(failed_widget, &allocation);
206  failed_widget_x += allocation.x;
207  failed_widget_y += allocation.y + allocation.height;
208  gtk_window_move(err_window, failed_widget_x, failed_widget_y);
209 
210  // Setup label
211  gtk_label_set_selectable(GTK_LABEL(err_label), FALSE);
212  gtk_label_set_max_width_chars(GTK_LABEL(err_label), 1);
213  gtk_widget_set_hexpand(GTK_WIDGET(err_label), TRUE);
214  gtk_widget_set_vexpand(GTK_WIDGET(err_label), TRUE);
215  gtk_label_set_ellipsize(GTK_LABEL(err_label), PANGO_ELLIPSIZE_END);
216  gtk_label_set_line_wrap(GTK_LABEL(err_label), TRUE);
217  gtk_label_set_line_wrap_mode(GTK_LABEL(err_label), PANGO_WRAP_WORD_CHAR);
218  gtk_label_set_markup(GTK_LABEL(err_label), markup);
219 
220  alert_icon = gtk_image_new_from_icon_name("dialog-warning-symbolic",
221  GTK_ICON_SIZE_DND);
222 
223  // Fill icon and label into a box.
224  gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(alert_icon), FALSE, FALSE, 0);
225  gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(err_label), TRUE, TRUE, 5);
226 
227  // Attach box to err_window
228  gtk_container_add(GTK_CONTAINER(err_window), GTK_WIDGET(box));
229 
230  // Display everything.
231  gtk_widget_show_all(GTK_WIDGET(err_window));
232 
233  // Mouse click and focus-loss will delete the err_window.
234  g_signal_connect(G_OBJECT(err_window), "focus-out-event",
235  G_CALLBACK(gtk_window_close), NULL);
236  g_signal_connect(G_OBJECT(err_window), "button-press-event",
237  G_CALLBACK(gtk_window_close), NULL);
238 }
239 
240 #ifdef HAVE_LIBAVAHI_UI
241 
242 static void remmina_file_editor_browse_avahi(GtkWidget *button, RemminaFileEditor *gfe)
243 {
244  TRACE_CALL(__func__);
245  GtkWidget *dialog;
246  gchar *host;
247 
248  dialog = aui_service_dialog_new(_("Choose a Remote Desktop Server"),
249  GTK_WINDOW(gfe),
250  _("_Cancel"), GTK_RESPONSE_CANCEL,
251  _("_OK"), GTK_RESPONSE_ACCEPT,
252  NULL);
253 
254  gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gfe));
255  aui_service_dialog_set_resolve_service(AUI_SERVICE_DIALOG(dialog), TRUE);
256  aui_service_dialog_set_resolve_host_name(AUI_SERVICE_DIALOG(dialog), TRUE);
257  aui_service_dialog_set_browse_service_types(AUI_SERVICE_DIALOG(dialog),
258  gfe->priv->avahi_service_type, NULL);
259 
260  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
261  host = g_strdup_printf("[%s]:%i",
262  aui_service_dialog_get_host_name(AUI_SERVICE_DIALOG(dialog)),
263  aui_service_dialog_get_port(AUI_SERVICE_DIALOG(dialog)));
264  } else {
265  host = NULL;
266  }
267  gtk_widget_destroy(dialog);
268 
269  if (host) {
270  gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(gfe->priv->server_combo))), host);
271  g_free(host);
272  }
273 }
274 #endif
275 
276 static void remmina_file_editor_on_realize(GtkWidget *widget, gpointer user_data)
277 {
278  TRACE_CALL(__func__);
279  RemminaFileEditor *gfe;
280  GtkWidget *defaultwidget;
281 
282  gfe = REMMINA_FILE_EDITOR(widget);
283 
284  defaultwidget = gfe->priv->server_combo;
285 
286  if (defaultwidget) {
287  if (GTK_IS_EDITABLE(defaultwidget))
288  gtk_editable_select_region(GTK_EDITABLE(defaultwidget), 0, -1);
289  gtk_widget_grab_focus(defaultwidget);
290  }
291 }
292 
293 static void remmina_file_editor_destroy(GtkWidget *widget, gpointer data)
294 {
295  TRACE_CALL(__func__);
296  remmina_file_free(REMMINA_FILE_EDITOR(widget)->priv->remmina_file);
297  g_hash_table_destroy(REMMINA_FILE_EDITOR(widget)->priv->setting_widgets);
298  g_free(REMMINA_FILE_EDITOR(widget)->priv);
299 }
300 
301 static void remmina_file_editor_button_on_toggled(GtkToggleButton *togglebutton, GtkWidget *widget)
302 {
303  TRACE_CALL(__func__);
304  gtk_widget_set_sensitive(widget, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)));
305 }
306 
308 {
309  TRACE_CALL(__func__);
310  /* Create the notebook */
311  gfe->priv->config_container = gtk_notebook_new();
312  gfe->priv->config_viewport = gtk_viewport_new(NULL, NULL);
313  gfe->priv->config_scrollable = gtk_scrolled_window_new(NULL, NULL);
314  gtk_container_set_border_width(GTK_CONTAINER(gfe->priv->config_scrollable), 2);
315  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gfe->priv->config_scrollable),
316  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
317  gtk_widget_show(gfe->priv->config_scrollable);
318 
319  gtk_container_add(GTK_CONTAINER(gfe->priv->config_viewport), gfe->priv->config_container);
320  gtk_container_set_border_width(GTK_CONTAINER(gfe->priv->config_viewport), 2);
321  gtk_widget_show(gfe->priv->config_viewport);
322  gtk_container_add(GTK_CONTAINER(gfe->priv->config_scrollable), gfe->priv->config_viewport);
323  gtk_container_set_border_width(GTK_CONTAINER(gfe->priv->config_container), 2);
324  gtk_widget_show(gfe->priv->config_container);
325 
326  gtk_container_add(GTK_CONTAINER(gfe->priv->config_box), gfe->priv->config_scrollable);
327 }
328 
330  const gchar *stock_id, const gchar *label, gint rows, gint cols)
331 {
332  TRACE_CALL(__func__);
333  GtkWidget *tablabel;
334  GtkWidget *tabbody;
335  GtkWidget *grid;
336  GtkWidget *widget;
337 
338  tablabel = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
339  gtk_widget_show(tablabel);
340 
341  widget = gtk_image_new_from_icon_name(stock_id, GTK_ICON_SIZE_BUTTON);
342  gtk_box_pack_start(GTK_BOX(tablabel), widget, FALSE, FALSE, 0);
343  gtk_widget_show(widget);
344 
345  widget = gtk_label_new(label);
346  gtk_box_pack_start(GTK_BOX(tablabel), widget, FALSE, FALSE, 0);
347  gtk_widget_show(widget);
348 
349  tabbody = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
350  gtk_widget_show(tabbody);
351  gtk_notebook_append_page(GTK_NOTEBOOK(gfe->priv->config_container), tabbody, tablabel);
352 
353  grid = gtk_grid_new();
354  gtk_widget_show(grid);
355  gtk_grid_set_row_spacing(GTK_GRID(grid), 8);
356  gtk_grid_set_column_spacing(GTK_GRID(grid), 8);
357  gtk_container_set_border_width(GTK_CONTAINER(grid), 15);
358  gtk_box_pack_start(GTK_BOX(tabbody), grid, FALSE, FALSE, 0);
359 
360  return grid;
361 }
362 
363 #ifdef HAVE_LIBSSH
364 
366 {
367  TRACE_CALL(__func__);
368  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_server_entry),
369  gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_enabled_check)) &&
370  (gfe->priv->ssh_tunnel_server_custom_radio == NULL ||
371  gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_server_custom_radio))));
372 }
373 
374 
375 static void remmina_file_editor_assistance_enabled_check_on_toggled(GtkToggleButton *togglebutton,
376  RemminaFileEditor *gfe)
377 {
378  TRACE_CALL(__func__);
379  gboolean enabled = TRUE;
380 
381  if (gfe->priv->assistance_toggle) {
382  enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->assistance_toggle));
383  if (gfe->priv->assistance_file)
384  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->assistance_file), enabled);
385  if (gfe->priv->assistance_password)
386  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->assistance_password), enabled);
387  if (gfe->priv->assistance_file_label)
388  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->assistance_file_label), enabled);
389  if (gfe->priv->assistance_password_label)
390  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->assistance_password_label), enabled);
391  }
392 }
393 
394 static void remmina_file_editor_ssh_tunnel_enabled_check_on_toggled(GtkToggleButton *togglebutton,
396 {
397  TRACE_CALL(__func__);
398  RemminaFileEditorPriv *priv = gfe->priv;
399  gboolean enabled = TRUE;
400  gchar *p;
401  const gchar *cp;
402  const gchar *s = NULL;
403 
404  if (gfe->priv->ssh_tunnel_enabled_check) {
405  enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->ssh_tunnel_enabled_check));
406  if (gfe->priv->ssh_tunnel_loopback_check)
407  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_loopback_check), enabled);
408  if (gfe->priv->ssh_tunnel_server_default_radio)
409  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_server_default_radio), enabled);
410  if (gfe->priv->ssh_tunnel_server_custom_radio)
411  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_server_custom_radio), enabled);
413  p = remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->protocol_combo));
414  // if (!(g_strcmp0(p, "SFTP") == 0 || g_strcmp0(p, "SSH") == 0)) {
415  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_username_entry), enabled);
416  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_auth_password), enabled);
417  gtk_widget_set_sensitive(GTK_WIDGET(gfe->priv->ssh_tunnel_auth_combo), enabled);
418  //}
419  g_free(p);
420  }
421  // remmina_file_editor_ssh_tunnel_auth_publickey_radio_on_toggled(NULL, gfe);
422  s = remmina_file_get_string(gfe->priv->remmina_file, "ssh_tunnel_privatekey");
423  if (s)
424  gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(gfe->priv->ssh_tunnel_privatekey_chooser), s);
425  s = remmina_file_get_string(gfe->priv->remmina_file, "ssh_tunnel_certfile");
426  if (s)
427  gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(gfe->priv->ssh_tunnel_certfile_chooser), s);
428 
429  if (gfe->priv->ssh_tunnel_username_entry)
430  if (enabled && gtk_entry_get_text(GTK_ENTRY(gfe->priv->ssh_tunnel_username_entry))[0] == '\0') {
431  cp = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_username");
432  gtk_entry_set_text(GTK_ENTRY(gfe->priv->ssh_tunnel_username_entry), cp ? cp : "");
433  }
434 
435  if (gfe->priv->ssh_tunnel_auth_password) {
436  if (enabled && gtk_entry_get_text(GTK_ENTRY(gfe->priv->ssh_tunnel_auth_password))[0] == '\0') {
437  cp = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_password");
438  gtk_entry_set_text(GTK_ENTRY(gfe->priv->ssh_tunnel_auth_password), cp ? cp : "");
439  }
440  }
441  if (gfe->priv->ssh_tunnel_passphrase) {
442  if (enabled && gtk_entry_get_text(GTK_ENTRY(gfe->priv->ssh_tunnel_passphrase))[0] == '\0') {
443  cp = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_passphrase");
444  gtk_entry_set_text(GTK_ENTRY(gfe->priv->ssh_tunnel_passphrase), cp ? cp : "");
445  }
446  }
447 }
448 
449 #endif
450 
451 static void remmina_file_editor_create_server(RemminaFileEditor *gfe, const RemminaProtocolSetting *setting, GtkWidget *grid,
452  gint row)
453 {
454  TRACE_CALL(__func__);
455  RemminaProtocolPlugin *plugin = gfe->priv->plugin;
456  GtkWidget *widget;
457 #ifdef HAVE_LIBAVAHI_UI
458  GtkWidget *hbox;
459 #endif
460  gchar *s;
461 
462  widget = gtk_label_new(_("Server"));
463  gtk_widget_show(widget);
464  gtk_widget_set_valign(widget, GTK_ALIGN_START);
465  gtk_widget_set_halign(widget, GTK_ALIGN_START);
466  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, row + 1);
467 
468  s = remmina_pref_get_recent(plugin->name);
469  widget = remmina_public_create_combo_entry(s, remmina_file_get_string(gfe->priv->remmina_file, "server"), TRUE);
470  gtk_widget_set_hexpand(widget, TRUE);
471  gtk_widget_show(widget);
472  gtk_widget_set_tooltip_markup(widget, _(server_tips));
473  gtk_entry_set_activates_default(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(widget))), TRUE);
474  gfe->priv->server_combo = widget;
475  g_free(s);
476 
477 #ifdef HAVE_LIBAVAHI_UI
478  if (setting->opt1) {
479  gfe->priv->avahi_service_type = (const gchar *)setting->opt1;
480 
481  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
482  gtk_widget_show(hbox);
483  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
484 
485  widget = gtk_button_new_with_label("…");
486  s = g_strdup_printf(_("Browse the network to find a %s server"), plugin->name);
487  gtk_widget_set_tooltip_text(widget, s);
488  g_free(s);
489  gtk_widget_show(widget);
490  gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
491  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_browse_avahi), gfe);
492 
493  gtk_grid_attach(GTK_GRID(grid), hbox, 1, row, 1, 1);
494  } else
495 #endif
496  {
497  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1);
498  }
499 }
500 
501 
502 static GtkWidget *remmina_file_editor_create_password(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gchar *setting_name)
503 {
504  TRACE_CALL(__func__);
505  GtkWidget *widget;
506 
507  widget = gtk_label_new(label);
508  gtk_widget_show(widget);
509 #if GTK_CHECK_VERSION(3, 12, 0)
510  gtk_widget_set_margin_end(widget, 40);
511 #else
512  gtk_widget_set_margin_right(widget, 40);
513 #endif
514  gtk_widget_set_valign(widget, GTK_ALIGN_START);
515  gtk_widget_set_halign(widget, GTK_ALIGN_START);
516  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
517 
518  widget = gtk_entry_new();
519  gtk_widget_show(widget);
520  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1);
521  gtk_entry_set_max_length(GTK_ENTRY(widget), 0);
522  gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
523  gtk_widget_set_hexpand(widget, TRUE);
524  gtk_entry_set_activates_default(GTK_ENTRY(widget), TRUE);
525  if (setting_name)
526  gtk_widget_set_name(widget, setting_name);
527 
528  if (value)
529  gtk_entry_set_text(GTK_ENTRY(widget), value);
530  /* Password view Toogle*/
531  if (setting_name) {
532  gtk_entry_set_icon_from_icon_name(GTK_ENTRY(widget), GTK_ENTRY_ICON_SECONDARY, "org.remmina.Remmina-password-reveal-symbolic");
533  gtk_entry_set_icon_activatable(GTK_ENTRY(widget), GTK_ENTRY_ICON_SECONDARY, TRUE);
534  g_signal_connect(widget, "icon-press", G_CALLBACK(remmina_main_toggle_password_view), NULL);
535  }
536  return widget;
537 }
538 
539 static void remmina_file_editor_update_resolution(GtkWidget *widget, RemminaFileEditor *gfe)
540 {
541  TRACE_CALL(__func__);
542  gchar *res_str;
543  res_str = g_strdup_printf("%dx%d",
544  remmina_file_get_int(gfe->priv->remmina_file, "resolution_width", 0),
545  remmina_file_get_int(gfe->priv->remmina_file, "resolution_height", 0));
546  remmina_public_load_combo_text_d(gfe->priv->resolution_custom_combo, remmina_pref.resolutions,
547  res_str, NULL);
548  g_free(res_str);
549 }
550 
551 static void remmina_file_editor_browse_resolution(GtkWidget *button, RemminaFileEditor *gfe)
552 {
553  TRACE_CALL(__func__);
554 
555  GtkDialog *dialog = remmina_string_list_new(FALSE, NULL);
558  remmina_string_list_set_titles(_("Resolutions"), _("Configure the available resolutions"));
559  gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gfe));
560  gtk_dialog_run(dialog);
561  g_free(remmina_pref.resolutions);
563  g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(remmina_file_editor_update_resolution), gfe);
564  gtk_widget_destroy(GTK_WIDGET(dialog));
565 }
566 
568  GtkWidget *grid, gint row)
569 {
570  TRACE_CALL(__func__);
571  GtkWidget *widget;
572  GtkWidget *hbox;
573  int resolution_w, resolution_h;
574  gchar *res_str;
576 
577  res_mode = remmina_file_get_int(gfe->priv->remmina_file, "resolution_mode", RES_INVALID);
578  resolution_w = remmina_file_get_int(gfe->priv->remmina_file, "resolution_width", -1);
579  resolution_h = remmina_file_get_int(gfe->priv->remmina_file, "resolution_height", -1);
580 
581  /* If resolution_mode is non-existent (-1), then we try to calculate it
582  * as we did before having resolution_mode */
583  if (res_mode == RES_INVALID) {
584  if (resolution_w <= 0 || resolution_h <= 0)
585  res_mode = RES_USE_INITIAL_WINDOW_SIZE;
586  else
587  res_mode = RES_USE_CUSTOM;
588  }
589  if (res_mode == RES_USE_CUSTOM)
590  res_str = g_strdup_printf("%dx%d", resolution_w, resolution_h);
591  else
592  res_str = NULL;
593 
594  widget = gtk_label_new(_("Resolution"));
595  gtk_widget_show(widget);
596  gtk_widget_set_valign(widget, GTK_ALIGN_START);
597  gtk_widget_set_halign(widget, GTK_ALIGN_START);
598  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
599 
600  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
601  widget = gtk_radio_button_new_with_label(NULL, _("Use initial window size"));
602  gtk_widget_show(widget);
603  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
604  gfe->priv->resolution_iws_radio = widget;
605  widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(gfe->priv->resolution_iws_radio), _("Use client resolution"));
606  gtk_widget_show(widget);
607  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
608  gfe->priv->resolution_auto_radio = widget;
609  gtk_grid_attach(GTK_GRID(grid), hbox, 1, row, 1, 1);
610  gtk_widget_show(hbox);
611 
612  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
613  gtk_widget_show(hbox);
614  gtk_grid_attach(GTK_GRID(grid), hbox, 1, row + 1, 1, 1);
615 
616  widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(gfe->priv->resolution_iws_radio), _("Custom"));
617  gtk_widget_show(widget);
618  gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
619  gfe->priv->resolution_custom_radio = widget;
620 
622  gtk_widget_show(widget);
623  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
624  gfe->priv->resolution_custom_combo = widget;
625 
626  widget = gtk_button_new_with_label("…");
627  gtk_widget_show(widget);
628  gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
629  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_browse_resolution), gfe);
630 
631  g_signal_connect(G_OBJECT(gfe->priv->resolution_custom_radio), "toggled",
632  G_CALLBACK(remmina_file_editor_button_on_toggled), gfe->priv->resolution_custom_combo);
633 
634  if (res_mode == RES_USE_CUSTOM)
635  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_custom_radio), TRUE);
636  else if (res_mode == RES_USE_CLIENT)
637  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_auto_radio), TRUE);
638  else
639  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_iws_radio), TRUE);
640 
641  gtk_widget_set_sensitive(gfe->priv->resolution_custom_combo, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gfe->priv->resolution_custom_radio)));
642 
643  g_free(res_str);
644 }
645 
646 
648  GtkWidget *grid, gint row)
649 {
650  TRACE_CALL(__func__);
651  GtkWidget *widget;
652 
653 
654 
655  widget = gtk_toggle_button_new_with_label(_("Assistance Mode"));
656  gtk_widget_set_halign(widget, GTK_ALIGN_START);
657  gtk_widget_show(widget);
658  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
659  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), remmina_file_get_int(gfe->priv->remmina_file, "assistance_mode", 0));
660  gfe->priv->assistance_toggle = widget;
661  g_signal_connect(widget, "toggled", G_CALLBACK(remmina_file_editor_assistance_enabled_check_on_toggled), gfe);
662 
663 
664  widget = gtk_label_new("Assistance file");
665  gtk_widget_set_halign(widget, GTK_ALIGN_END);
666  gtk_widget_show(widget);
667  gtk_grid_attach(GTK_GRID(grid), widget, 0, row+1, 1, 1);
668  gfe->priv->assistance_file_label = widget;
669 
670  widget = gtk_entry_new();
671  gtk_widget_set_halign(widget, GTK_ALIGN_START);
672  gtk_widget_show(widget);
673  gtk_entry_set_text(GTK_ENTRY(widget), remmina_file_get_string(gfe->priv->remmina_file, "assistance_file"));
674  gtk_grid_attach(GTK_GRID(grid), widget, 1, row+1, 1, 1);
675  gfe->priv->assistance_file = widget;
676 
677  widget = gtk_label_new("Assistance Password");
678  gtk_widget_set_halign(widget, GTK_ALIGN_END);
679  gtk_widget_show(widget);
680  gtk_grid_attach(GTK_GRID(grid), widget, 0, row+2, 1, 1);
681  gfe->priv->assistance_password_label = widget;
682 
683  widget = gtk_entry_new();
684  gtk_widget_set_halign(widget, GTK_ALIGN_START);
685  gtk_widget_show(widget);
686  gtk_entry_set_text(GTK_ENTRY(widget), remmina_file_get_string(gfe->priv->remmina_file, "assistance_pass"));
687  gtk_grid_attach(GTK_GRID(grid), widget, 1, row+2, 1, 1);
688  gfe->priv->assistance_password = widget;
689 
691 
692 }
693 
694 
695 static GtkWidget *remmina_file_editor_create_text2(RemminaFileEditor *gfe, GtkWidget *grid,
696  gint row, gint col, const gchar *label, const gchar *value, gint left,
697  gint right, gchar *setting_name)
698 {
699  TRACE_CALL(__func__);
700  GtkWidget *widget;
701 
702  widget = gtk_label_new(label);
703  gtk_widget_show(widget);
704 #if GTK_CHECK_VERSION(3, 12, 0)
705  gtk_widget_set_margin_start(widget, left);
706  gtk_widget_set_margin_end(widget, right);
707 #else
708  gtk_widget_set_margin_left(widget, left);
709  gtk_widget_set_margin_right(widget, right);
710 #endif
711  gtk_widget_set_valign(widget, GTK_ALIGN_START);
712  gtk_widget_set_halign(widget, GTK_ALIGN_START);
713  gtk_grid_attach(GTK_GRID(grid), widget, col, row, 1, 1);
714 
715  widget = gtk_entry_new();
716  gtk_widget_show(widget);
717  gtk_grid_attach(GTK_GRID(grid), widget, col + 1, row, 1, 1);
718  gtk_entry_set_max_length(GTK_ENTRY(widget), 300);
719  gtk_widget_set_hexpand(widget, TRUE);
720  if (setting_name)
721  gtk_widget_set_name(widget, setting_name);
722 
723  if (value)
724  gtk_entry_set_text(GTK_ENTRY(widget), value);
725 
726  return widget;
727 }
728 
729 static GtkWidget *remmina_file_editor_create_text(RemminaFileEditor *gfe, GtkWidget *grid,
730  gint row, gint col, const gchar *label, const gchar *value,
731  gchar *setting_name)
732 {
733  TRACE_CALL(__func__);
734  return remmina_file_editor_create_text2(gfe, grid, row, col, label, value, 0, 40,
735  setting_name);
736 }
737 
738 static GtkWidget *remmina_file_editor_create_textarea(RemminaFileEditor *gfe, GtkWidget *grid,
739  gint row, gint col, const gchar *label, const gchar *value,
740  gchar *setting_name)
741 {
742  TRACE_CALL(__func__);
743  GtkWidget *widget;
744  GtkTextView *view;
745  GtkTextBuffer *buffer;
746  GtkTextIter start;
747 
748  widget = gtk_text_view_new();
749  view = GTK_TEXT_VIEW(widget);
750  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
751  gtk_text_view_set_top_margin(GTK_TEXT_VIEW(view), 20);
752  gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(view), 20);
753  gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 20);
754  gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 20);
755  gtk_text_view_set_monospace(view, TRUE);
756  if (setting_name)
757  gtk_widget_set_name(widget, setting_name);
758  if (value) {
759  buffer = gtk_text_view_get_buffer(view);
760  gtk_text_buffer_set_text(buffer, value, -1);
761  gtk_text_buffer_get_start_iter(buffer, &start);
762  gtk_text_buffer_place_cursor(buffer, &start);
763  }
764  gtk_widget_show(widget);
765  gtk_widget_set_hexpand(widget, TRUE);
766  gtk_widget_set_size_request(GTK_WIDGET(view), 320, 300);
767  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
768  return widget;
769 }
770 
771 static GtkWidget *remmina_file_editor_create_select(RemminaFileEditor *gfe, GtkWidget *grid,
772  gint row, gint col, const gchar *label, const gpointer *list,
773  const gchar *value, gchar *setting_name)
774 {
775  TRACE_CALL(__func__);
776  GtkWidget *widget;
777 
778  widget = gtk_label_new(label);
779  gtk_widget_show(widget);
780  gtk_widget_set_valign(widget, GTK_ALIGN_START);
781  gtk_widget_set_halign(widget, GTK_ALIGN_START);
782  if (setting_name)
783  gtk_widget_set_name(widget, setting_name);
784  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
785 
786  widget = remmina_public_create_combo_map(list, value, FALSE, gfe->priv->plugin->domain);
787  gtk_widget_show(widget);
788  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1);
789 
790  return widget;
791 }
792 
793 static GtkWidget *remmina_file_editor_create_combo(RemminaFileEditor *gfe, GtkWidget *grid,
794  gint row, gint col, const gchar *label, const gchar *list,
795  const gchar *value, gchar *setting_name)
796 {
797  TRACE_CALL(__func__);
798  GtkWidget *widget;
799 
800  widget = gtk_label_new(label);
801  gtk_widget_show(widget);
802  gtk_widget_set_valign(widget, GTK_ALIGN_START);
803  gtk_widget_set_halign(widget, GTK_ALIGN_START);
804  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
805 
806  widget = remmina_public_create_combo_entry(list, value, FALSE);
807  gtk_widget_show(widget);
808  gtk_widget_set_hexpand(widget, TRUE);
809  if (setting_name)
810  gtk_widget_set_name(widget, setting_name);
811  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 1, 1);
812 
813  return widget;
814 }
815 
816 static GtkWidget *remmina_file_editor_create_check(RemminaFileEditor *gfe, GtkWidget *grid,
817  gint row, gint top, const gchar *label, gboolean value,
818  gchar *setting_name)
819 {
820  TRACE_CALL(__func__);
821  GtkWidget *widget;
822  widget = gtk_check_button_new_with_label(label);
823  gtk_widget_show(widget);
824  if (setting_name)
825  gtk_widget_set_name(widget, setting_name);
826  gtk_grid_attach(GTK_GRID(grid), widget, top, row, 1, 1);
827 
828  if (value)
829  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
830 
831  return widget;
832 }
833 
843 static GtkWidget *
844 remmina_file_editor_create_chooser(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label,
845  const gchar *value, gint type, gchar *setting_name)
846 {
847  TRACE_CALL(__func__);
848  GtkWidget *check;
849  GtkWidget *widget;
850  GtkWidget *hbox;
851 
852  widget = gtk_label_new(label);
853  gtk_widget_show(widget);
854  gtk_widget_set_valign(widget, GTK_ALIGN_START);
855  gtk_widget_set_halign(widget, GTK_ALIGN_START);
856  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
857 
858  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
859  gtk_widget_show(hbox);
860  gtk_grid_attach(GTK_GRID(grid), hbox, 1, row, 1, 1);
861 
862  check = gtk_check_button_new();
863  gtk_widget_show(check);
864  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), (value && value[0] == '/'));
865  gtk_box_pack_start(GTK_BOX(hbox), check, FALSE, FALSE, 0);
866 
867  widget = gtk_file_chooser_button_new(label, type);
868  if (setting_name)
869  gtk_widget_set_name(widget, setting_name);
870  gtk_widget_show(widget);
871  if (value)
872  gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(widget), value);
873  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
874 
875  g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(remmina_file_editor_button_on_toggled), widget);
876  remmina_file_editor_button_on_toggled(GTK_TOGGLE_BUTTON(check), widget);
877 
878  return widget;
879 }
880 
881 // used to filter out invalid characters for REMMINA_PROTOCOL_SETTING_TYPE_INT
882 void remmina_file_editor_int_setting_filter(GtkEditable *editable, const gchar *text,
883  gint length, gint *position, gpointer data)
884 {
885  for (int i = 0; i < length; i++) {
886  if (!isdigit(text[i]) && text[i] != '-') {
887  g_signal_stop_emission_by_name(G_OBJECT(editable), "insert-text");
888  return;
889  }
890  }
891 }
892 
893 // used to filter out invalid characters for REMMINA_PROTOCOL_SETTING_TYPE_DOUBLE
894 // '.' and ',' can't be used interchangeably! It depends on the language setting
895 // of the user.
896 void remmina_file_editor_double_setting_filter(GtkEditable *editable, const gchar *text,
897  gint length, gint *position, gpointer data)
898 {
899  for (int i = 0; i < length; i++) {
900  if (!isdigit(text[i]) && text[i] != '-' && text[i] != '.' && text[i] != ',') {
901  g_signal_stop_emission_by_name(G_OBJECT(editable), "insert-text");
902  return;
903  }
904  }
905 }
906 
907 static GtkWidget *remmina_file_editor_create_int(RemminaFileEditor *gfe, GtkWidget *grid,
908  gint row, gint col, const gchar *label, const gint value,
909  gint left, gint right, gchar *setting_name)
910 {
911  TRACE_CALL(__func__);
912  GtkWidget *widget;
913 
914  widget = gtk_label_new(label);
915  gtk_widget_show(widget);
916 #if GTK_CHECK_VERSION(3, 12, 0)
917  gtk_widget_set_margin_start(widget, left);
918  gtk_widget_set_margin_end(widget, right);
919 #else
920  gtk_widget_set_margin_left(widget, left);
921  gtk_widget_set_margin_right(widget, right);
922 #endif
923  gtk_widget_set_valign(widget, GTK_ALIGN_START);
924  gtk_widget_set_halign(widget, GTK_ALIGN_START);
925  gtk_grid_attach(GTK_GRID(grid), widget, col, row, 1, 1);
926 
927  widget = gtk_entry_new();
928  gtk_widget_show(widget);
929  gtk_grid_attach(GTK_GRID(grid), widget, col + 1, row, 1, 1);
930  gtk_entry_set_max_length(GTK_ENTRY(widget), 300);
931  gtk_widget_set_hexpand(widget, TRUE);
932  if (setting_name)
933  gtk_widget_set_name(widget, setting_name);
934 
935  // Convert int to str.
936  int length = snprintf(NULL, 0, "%d", value) + 1; // +1 '\0' byte
937  char *str = malloc(length);
938  snprintf(str, length, "%d", value);
939 
940  gtk_entry_set_text(GTK_ENTRY(widget), str);
941  free(str);
942 
943  g_signal_connect(G_OBJECT(widget), "insert-text",
944  G_CALLBACK(remmina_file_editor_int_setting_filter), NULL);
945 
946  return widget;
947 }
948 
950  GtkWidget *grid, gint row, gint col,
951  const gchar *label, gdouble value, gint left,
952  gint right, gchar *setting_name)
953 {
954  TRACE_CALL(__func__);
955  GtkWidget *widget;
956 
957  widget = gtk_label_new(label);
958  gtk_widget_show(widget);
959 #if GTK_CHECK_VERSION(3, 12, 0)
960  gtk_widget_set_margin_start(widget, left);
961  gtk_widget_set_margin_end(widget, right);
962 #else
963  gtk_widget_set_margin_left(widget, left);
964  gtk_widget_set_margin_right(widget, right);
965 #endif
966  gtk_widget_set_valign(widget, GTK_ALIGN_START);
967  gtk_widget_set_halign(widget, GTK_ALIGN_START);
968  gtk_grid_attach(GTK_GRID(grid), widget, col, row, 1, 1);
969 
970  widget = gtk_entry_new();
971  gtk_widget_show(widget);
972  gtk_grid_attach(GTK_GRID(grid), widget, col + 1, row, 1, 1);
973  gtk_entry_set_max_length(GTK_ENTRY(widget), 300);
974  gtk_widget_set_hexpand(widget, TRUE);
975  if (setting_name)
976  gtk_widget_set_name(widget, setting_name);
977 
978  // Convert double to str.
979  int length = snprintf(NULL, 0, "%.8g", value) + 1; // +1 '\0' byte
980  char *str = malloc(length);
981  snprintf(str, length, "%f", value);
982 
983  gtk_entry_set_text(GTK_ENTRY(widget), str);
984  free(str);
985 
986  g_signal_connect(G_OBJECT(widget), "insert-text",
988 
989  return widget;
990 }
991 
992 
993 
994 static void remmina_file_editor_create_settings(RemminaFileEditor *gfe, GtkWidget *grid,
995  const RemminaProtocolSetting *settings)
996 {
997  TRACE_CALL(__func__);
998  RemminaFileEditorPriv *priv = gfe->priv;
999  GtkWidget *widget;
1000  gint grid_row = 0;
1001  gint grid_column = 0;
1002  gchar **strarr;
1003  gchar *setting_name;
1004  const gchar *escaped;
1005 
1006  while (settings->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
1007  setting_name = (gchar *)(remmina_plugin_manager_get_canonical_setting_name(settings));
1008  switch (settings->type) {
1010  remmina_file_editor_create_server(gfe, settings, grid, grid_row);
1011  break;
1012 
1014  widget = remmina_file_editor_create_password(gfe, grid, grid_row, 0,
1015  g_dgettext(priv->plugin->domain, settings->label),
1016  remmina_file_get_string(priv->remmina_file, setting_name),
1017  setting_name);
1018  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1019  grid_row++;
1020  break;
1021 
1023  remmina_file_editor_create_resolution(gfe, settings, grid, grid_row);
1024  grid_row ++;
1025  break;
1026 
1028  remmina_file_editor_create_assistance(gfe, settings, grid, grid_row);
1029  grid_row += 3;
1030  break;
1031 
1033  strarr = remmina_pref_keymap_groups();
1034  priv->keymap_combo = remmina_file_editor_create_select(gfe, grid,
1035  grid_row + 1, 0,
1036  _("Keyboard mapping"), (const gpointer *)strarr,
1037  remmina_file_get_string(priv->remmina_file, "keymap"),
1038  setting_name);
1039  g_strfreev(strarr);
1040  grid_row++;
1041  break;
1042 
1044  widget = remmina_file_editor_create_text(gfe, grid, grid_row, 0,
1045  g_dgettext(priv->plugin->domain, settings->label),
1046  remmina_file_get_string(priv->remmina_file, setting_name),
1047  setting_name);
1048  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1049  if (settings->opt2)
1050  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1051  grid_row++;
1052  break;
1053 
1055  escaped = remmina_file_get_string(priv->remmina_file, setting_name);
1056  escaped = g_uri_unescape_string(escaped, NULL);
1057  widget = remmina_file_editor_create_textarea(gfe, grid, grid_row, 0,
1058  g_dgettext(priv->plugin->domain, settings->label), escaped,
1059  setting_name);
1060  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1061  grid_row++;
1062  break;
1063 
1065  widget = remmina_file_editor_create_select(gfe, grid, grid_row, 0,
1066  g_dgettext(priv->plugin->domain, settings->label),
1067  (const gpointer *)settings->opt1,
1068  remmina_file_get_string(priv->remmina_file, setting_name),
1069  setting_name);
1070  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1071  if (settings->opt2)
1072  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1073  break;
1074 
1076  widget = remmina_file_editor_create_combo(gfe, grid, grid_row, 0,
1077  g_dgettext(priv->plugin->domain, settings->label),
1078  (const gchar *)settings->opt1,
1079  remmina_file_get_string(priv->remmina_file, setting_name),
1080  setting_name);
1081  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1082  if (settings->opt2)
1083  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1084  break;
1085 
1087  widget = remmina_file_editor_create_check(gfe, grid, grid_row, grid_column,
1088  g_dgettext(priv->plugin->domain, settings->label),
1089  remmina_file_get_int(priv->remmina_file, setting_name, FALSE),
1090  setting_name);
1091  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1092  if (settings->opt2)
1093  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1094  break;
1095 
1097  widget = remmina_file_editor_create_chooser(gfe, grid, grid_row, 0,
1098  g_dgettext(priv->plugin->domain, settings->label),
1099  remmina_file_get_string(priv->remmina_file, setting_name),
1100  GTK_FILE_CHOOSER_ACTION_OPEN, setting_name);
1101  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1102  if (settings->opt2)
1103  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1104  break;
1105 
1107  widget = remmina_file_editor_create_chooser(gfe, grid, grid_row, 0,
1108  g_dgettext(priv->plugin->domain, settings->label),
1109  remmina_file_get_string(priv->remmina_file, setting_name),
1110  GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1111  setting_name);
1112  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1113  if (settings->opt2)
1114  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1115  break;
1117  widget = remmina_file_editor_create_int(gfe, grid, grid_row, 0,
1118  g_dgettext(priv->plugin->domain, settings->label),
1119  remmina_file_get_int(priv->remmina_file, setting_name, 0),
1120  0, 40, setting_name);
1121  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1122  if (settings->opt2)
1123  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1124  grid_row++;
1125  break;
1127  widget = remmina_file_editor_create_double(gfe, grid, grid_row, 0,
1128  g_dgettext(priv->plugin->domain, settings->label),
1129  remmina_file_get_double(priv->remmina_file, setting_name, 0.0f),
1130  0, 40, setting_name);
1131  g_hash_table_insert(priv->setting_widgets, setting_name, widget);
1132  if (settings->opt2)
1133  gtk_widget_set_tooltip_text(widget, _((const gchar *)settings->opt2));
1134  grid_row++;
1135  break;
1136 
1137  default:
1138  break;
1139  }
1140  /* If the setting wants compactness, move to the next column */
1141  if (settings->compact)
1142  grid_column++;
1143  /* Add a new settings row and move to the first column
1144  * if the setting doesn’t want the compactness
1145  * or we already have two columns */
1146  if (!settings->compact || grid_column > 1) {
1147  grid_row++;
1148  grid_column = 0;
1149  }
1150  settings++;
1151  }
1152 }
1153 
1155 {
1156  TRACE_CALL(__func__);
1157  RemminaFileEditorPriv *priv = gfe->priv;
1158  GtkWidget *grid;
1159  GtkWidget *widget;
1160  const gchar *cs;
1161 
1162  /* The Behavior tab (implementation) */
1163  grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Behavior"), 20, 2);
1164 
1165  /* Execute Command frame */
1166  remmina_public_create_group(GTK_GRID(grid), _("Execute a Command"), 0, 1, 2);
1167 
1168  /* PRE connection command */
1169  cs = remmina_file_get_string(priv->remmina_file, "precommand");
1170  widget = remmina_file_editor_create_text2(gfe, grid, 2, 0, _("Before connecting"), cs, 24, 26, "precommand");
1171  priv->behavior_precommand_entry = widget;
1172  gtk_entry_set_placeholder_text(GTK_ENTRY(widget), _("command %h %u %t %U %p %g --option"));
1173  gtk_widget_set_tooltip_markup(widget, _(cmd_tips));
1174 
1175  /* POST connection command */
1176  cs = remmina_file_get_string(priv->remmina_file, "postcommand");
1177  widget = remmina_file_editor_create_text2(gfe, grid, 3, 0, _("After connecting"), cs, 24, 16, "postcommand");
1178  priv->behavior_postcommand_entry = widget;
1179  gtk_entry_set_placeholder_text(GTK_ENTRY(widget), _("/path/to/command -opt1 arg %h %u %t -opt2 %U %p %g"));
1180  gtk_widget_set_tooltip_markup(widget, _(cmd_tips));
1181 
1182  /* Startup frame */
1183  remmina_public_create_group(GTK_GRID(grid), _("Start-up"), 4, 1, 2);
1184 
1185  /* Autostart profile option */
1186  priv->behavior_autostart_check = remmina_file_editor_create_check(gfe, grid, 6, 1, _("Auto-start this profile"),
1187  remmina_file_get_int(priv->remmina_file, "enable-autostart", FALSE), "enable-autostart");
1188 
1189  /* Startup frame */
1190  remmina_public_create_group(GTK_GRID(grid), _("Connection profile security"), 8, 1, 2);
1191 
1192  /* Autostart profile option */
1193  priv->behavior_lock_check = remmina_file_editor_create_check(gfe, grid, 10, 1, _("Require password to connect or edit the profile"),
1194  remmina_file_get_int(priv->remmina_file, "profile-lock", FALSE), "profile-lock");
1195 }
1196 
1197 #ifdef HAVE_LIBSSH
1198 static gpointer ssh_tunnel_auth_list[] =
1199 {
1200  "0", N_("Password"),
1201  "1", N_("SSH identity file"),
1202  "2", N_("SSH agent"),
1203  "3", N_("Public key (automatic)"),
1204  "4", N_("Kerberos (GSSAPI)"),
1205  NULL
1206 };
1207 #endif
1208 
1210 {
1211  TRACE_CALL(__func__);
1212 #ifdef HAVE_LIBSSH
1213  RemminaFileEditorPriv *priv = gfe->priv;
1214  GtkWidget *grid;
1215  GtkWidget *widget;
1216  const gchar *cs;
1217  gchar *s;
1218  gchar *p;
1219  gint row = 0;
1220 
1221  if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_NONE)
1222  return;
1223 
1224  /* The SSH tab (implementation) */
1225  grid = remmina_file_editor_create_notebook_tab(gfe, NULL,
1226  _("SSH Tunnel"), 9, 3);
1227  widget = gtk_toggle_button_new_with_label(_("Enable SSH tunnel"));
1228  gtk_widget_set_halign(widget, GTK_ALIGN_START);
1229  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
1230  g_signal_connect(G_OBJECT(widget), "toggled",
1232  priv->ssh_tunnel_enabled_check = widget;
1233 
1234  widget = gtk_check_button_new_with_label(_("Tunnel via loopback address"));
1235  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 2, 1);
1236  priv->ssh_tunnel_loopback_check = widget;
1237 
1238  // 1
1239  row++;
1240  /* SSH Server group */
1241 
1242  switch (ssh_setting) {
1244  s = g_strdup_printf(_("Same server at port %i"), DEFAULT_SSH_PORT);
1245  widget = gtk_radio_button_new_with_label(NULL, s);
1246  g_free(s);
1247  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 3, 1);
1248  priv->ssh_tunnel_server_default_radio = widget;
1249  // 2
1250  row++;
1251 
1252  widget = gtk_radio_button_new_with_label_from_widget(
1253  GTK_RADIO_BUTTON(priv->ssh_tunnel_server_default_radio), _("Custom"));
1254  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
1255  g_signal_connect(G_OBJECT(widget), "toggled",
1257  priv->ssh_tunnel_server_custom_radio = widget;
1258 
1259  widget = gtk_entry_new();
1260  gtk_entry_set_max_length(GTK_ENTRY(widget), 100);
1261  gtk_widget_set_tooltip_markup(widget, _(server_tips2));
1262  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 2, 1);
1263  priv->ssh_tunnel_server_entry = widget;
1264  // 3
1265  row++;
1266  break;
1267 
1269  priv->ssh_tunnel_server_default_radio = NULL;
1270  priv->ssh_tunnel_server_custom_radio = NULL;
1271 
1272  priv->ssh_tunnel_server_entry = remmina_file_editor_create_text(gfe, grid, 1, 0,
1273  _("Server"), NULL, "ssh_reverse_tunnel_server");
1274  gtk_widget_set_tooltip_markup(priv->ssh_tunnel_server_entry, _(server_tips));
1275  // 2
1276  row++;
1277  break;
1280  priv->ssh_tunnel_server_default_radio = NULL;
1281  priv->ssh_tunnel_server_custom_radio = NULL;
1282  priv->ssh_tunnel_server_entry = NULL;
1283 
1284  break;
1285 
1286  default:
1287  break;
1288  }
1289 
1290  /* This is not used? */
1291  p = remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->protocol_combo));
1292  if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_SFTP) {
1293  widget = remmina_file_editor_create_text(gfe, grid, row, 1,
1294  _("Start-up path"), NULL, "start-up-path");
1295  cs = remmina_file_get_string(priv->remmina_file, "execpath");
1296  gtk_entry_set_text(GTK_ENTRY(widget), cs ? cs : "");
1297  g_hash_table_insert(priv->setting_widgets, "execpath", widget);
1298  // 2
1299  row++;
1300  }
1301 
1302  /* SSH Authentication frame */
1303  remmina_public_create_group(GTK_GRID(grid), _("SSH Authentication"), row, 6, 1);
1304  // 5
1305  row += 2;
1306 
1307  priv->ssh_tunnel_auth_combo = remmina_file_editor_create_select(gfe, grid, row, 0,
1308  _("Authentication type"),
1309  (const gpointer *)ssh_tunnel_auth_list,
1310  remmina_file_get_string(priv->remmina_file, "ssh_tunnel_auth"), "ssh_tunnel_auth");
1311  row++;
1312 
1313  if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_TUNNEL ||
1315  priv->ssh_tunnel_username_entry =
1316  remmina_file_editor_create_text(gfe, grid, row, 0,
1317  _("Username"), NULL, "ssh_tunnel_username");
1318  // 5
1319  row++;
1320  }
1321 
1322  widget = remmina_file_editor_create_password(gfe, grid, row, 0,
1323  _("Password"),
1324  remmina_file_get_string(priv->remmina_file, "ssh_tunnel_password"),
1325  "ssh_tunnel_password");
1326  priv->ssh_tunnel_auth_password = widget;
1327  row++;
1328 
1329  priv->ssh_tunnel_privatekey_chooser = remmina_file_editor_create_chooser(gfe, grid, row, 0,
1330  _("SSH private key file"),
1331  remmina_file_get_string(priv->remmina_file, "ssh_tunnel_privatekey"),
1332  GTK_FILE_CHOOSER_ACTION_OPEN, "ssh_tunnel_privatekey");
1333  row++;
1334 
1335  priv->ssh_tunnel_certfile_chooser = remmina_file_editor_create_chooser(gfe, grid, row, 0,
1336  _("SSH certificate file"),
1337  remmina_file_get_string(priv->remmina_file, "ssh_tunnel_certfile"),
1338  GTK_FILE_CHOOSER_ACTION_OPEN, "ssh_tunnel_certfile");
1339  row++;
1340 
1341  widget = gtk_label_new(_("Password to unlock private key"));
1342  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 1);
1343  widget = gtk_entry_new();
1344  gtk_grid_attach(GTK_GRID(grid), widget, 1, row, 2, 1);
1345  gtk_entry_set_max_length(GTK_ENTRY(widget), 300);
1346  gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
1347  gtk_widget_set_hexpand(widget, TRUE);
1348  priv->ssh_tunnel_passphrase = widget;
1349  row++;
1350 
1351  /* Set the values */
1352  cs = remmina_file_get_string(priv->remmina_file, "ssh_tunnel_server");
1353  if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_TUNNEL) {
1354  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_enabled_check),
1355  remmina_file_get_int(priv->remmina_file, "ssh_tunnel_enabled", FALSE));
1356  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_loopback_check),
1357  remmina_file_get_int(priv->remmina_file, "ssh_tunnel_loopback", FALSE));
1358 
1359  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cs ? priv->ssh_tunnel_server_custom_radio : priv->ssh_tunnel_server_default_radio), TRUE);
1360  gtk_entry_set_text(GTK_ENTRY(priv->ssh_tunnel_server_entry),
1361  cs ? cs : "");
1362  } else if (ssh_setting == REMMINA_PROTOCOL_SSH_SETTING_REVERSE_TUNNEL) {
1363  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_enabled_check),
1364  remmina_file_get_int(priv->remmina_file, "ssh_tunnel_enabled", FALSE));
1365  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_loopback_check),
1366  remmina_file_get_int(priv->remmina_file, "ssh_tunnel_loopback", FALSE));
1367  gtk_entry_set_text(GTK_ENTRY(priv->ssh_tunnel_server_entry),
1368  cs ? cs : "");
1369  }
1370 
1372  gtk_widget_show_all(grid);
1373  g_free(p);
1374 #endif
1375 }
1376 
1378 {
1379  TRACE_CALL(__func__);
1380  RemminaFileEditorPriv *priv = gfe->priv;
1381  GtkWidget *grid;
1382 
1383  static const RemminaProtocolSetting notes_settings[] =
1384  {
1385  { REMMINA_PROTOCOL_SETTING_TYPE_TEXTAREA, "notes_text", NULL, FALSE, NULL, NULL },
1386  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
1387  };
1388 
1390 
1391  /* The Basic tab */
1392  if (priv->plugin->basic_settings) {
1393  grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Basic"), 20, 2);
1394  remmina_file_editor_create_settings(gfe, grid, priv->plugin->basic_settings);
1395  }
1396 
1397  /* The Advanced tab */
1398  if (priv->plugin->advanced_settings) {
1399  grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Advanced"), 20, 2);
1400  remmina_file_editor_create_settings(gfe, grid, priv->plugin->advanced_settings);
1401  }
1402 
1403  /* The Behavior tab */
1405 
1406  /* The SSH tab */
1407  remmina_file_editor_create_ssh_tunnel_tab(gfe, priv->plugin->ssh_setting);
1408 
1409  /* Notes tab */
1410  grid = remmina_file_editor_create_notebook_tab(gfe, NULL, _("Notes"), 1, 1);
1411  remmina_file_editor_create_settings(gfe, grid, notes_settings);
1412 }
1413 
1415 {
1416  TRACE_CALL(__func__);
1417  RemminaFileEditorPriv *priv = gfe->priv;
1418  gchar *protocol;
1419 
1420  if (priv->config_container) {
1421  gtk_widget_destroy(priv->config_container);
1422  priv->config_container = NULL;
1423  gtk_widget_destroy(priv->config_viewport);
1424  priv->config_viewport = NULL;
1425  gtk_widget_destroy(priv->config_scrollable);
1426  priv->config_scrollable = NULL;
1427  }
1428 
1429  priv->server_combo = NULL;
1430  priv->resolution_iws_radio = NULL;
1431  priv->resolution_auto_radio = NULL;
1432  priv->resolution_custom_radio = NULL;
1433  priv->resolution_custom_combo = NULL;
1434  priv->keymap_combo = NULL;
1435 
1436  priv->ssh_tunnel_enabled_check = NULL;
1437  priv->ssh_tunnel_loopback_check = NULL;
1438  priv->ssh_tunnel_server_default_radio = NULL;
1439  priv->ssh_tunnel_server_custom_radio = NULL;
1440  priv->ssh_tunnel_server_entry = NULL;
1441  priv->ssh_tunnel_username_entry = NULL;
1442  priv->ssh_tunnel_auth_combo = NULL;
1443  priv->ssh_tunnel_auth_password = NULL;
1444  priv->ssh_tunnel_privatekey_chooser = NULL;
1445  priv->ssh_tunnel_certfile_chooser = NULL;
1446 
1447  g_hash_table_remove_all(priv->setting_widgets);
1448 
1449  protocol = remmina_public_combo_get_active_text(combo);
1450  if (protocol) {
1452  protocol);
1453  g_free(protocol);
1455  }
1456 }
1457 
1459 {
1460  TRACE_CALL(__func__);
1461  RemminaFileEditorPriv *priv = gfe->priv;
1462 
1463  remmina_file_set_string(priv->remmina_file, "precommand", gtk_entry_get_text(GTK_ENTRY(priv->behavior_precommand_entry)));
1464  remmina_file_set_string(priv->remmina_file, "postcommand", gtk_entry_get_text(GTK_ENTRY(priv->behavior_postcommand_entry)));
1465 
1466  gboolean autostart_enabled = (priv->behavior_autostart_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->behavior_autostart_check)) : FALSE);
1467  remmina_file_set_int(priv->remmina_file, "enable-autostart", autostart_enabled);
1468  gboolean lock_enabled = (priv->behavior_lock_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->behavior_lock_check)) : FALSE);
1469  remmina_file_set_int(priv->remmina_file, "profile-lock", lock_enabled);
1470 }
1471 
1473 {
1474  TRACE_CALL(__func__);
1475  RemminaFileEditorPriv *priv = gfe->priv;
1476  gboolean ssh_tunnel_enabled;
1477  int ssh_tunnel_auth;
1478 
1479  ssh_tunnel_enabled = (priv->ssh_tunnel_enabled_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_enabled_check)) : FALSE);
1480  remmina_file_set_int(priv->remmina_file,
1481  "ssh_tunnel_loopback",
1482  (priv->ssh_tunnel_loopback_check ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->ssh_tunnel_loopback_check)) : FALSE));
1483  remmina_file_set_int(priv->remmina_file, "ssh_tunnel_enabled", ssh_tunnel_enabled);
1484  remmina_file_set_string(priv->remmina_file, "ssh_tunnel_auth",
1485  remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->ssh_tunnel_auth_combo)));
1486  remmina_file_set_string(priv->remmina_file, "ssh_tunnel_username",
1487  (ssh_tunnel_enabled ? gtk_entry_get_text(GTK_ENTRY(priv->ssh_tunnel_username_entry)) : NULL));
1489  priv->remmina_file,
1490  "ssh_tunnel_server",
1491  (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));
1492 
1493  ssh_tunnel_auth = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->ssh_tunnel_auth_combo));
1494 
1496  priv->remmina_file,
1497  "ssh_tunnel_auth",
1498  ssh_tunnel_auth);
1499 
1500  // If box is unchecked for private key and certfile file choosers,
1501  // set the string to NULL in the remmina file
1502  if (gtk_widget_get_sensitive(priv->ssh_tunnel_privatekey_chooser)) {
1504  priv->remmina_file,
1505  "ssh_tunnel_privatekey",
1506  (priv->ssh_tunnel_privatekey_chooser ? gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->ssh_tunnel_privatekey_chooser)) : NULL));
1507  }
1508  else {
1509  remmina_file_set_string(priv->remmina_file, "ssh_tunnel_privatekey", NULL);
1510  }
1511 
1512  if (gtk_widget_get_sensitive(priv->ssh_tunnel_certfile_chooser)) {
1514  priv->remmina_file,
1515  "ssh_tunnel_certfile",
1516  (priv->ssh_tunnel_certfile_chooser ? gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->ssh_tunnel_certfile_chooser)) : NULL));
1517  }
1518  else {
1519  remmina_file_set_string(priv->remmina_file, "ssh_tunnel_certfile", NULL);
1520  }
1521 
1523  priv->remmina_file,
1524  "ssh_tunnel_password",
1525  (ssh_tunnel_enabled && (ssh_tunnel_auth == SSH_AUTH_PASSWORD)) ? gtk_entry_get_text(GTK_ENTRY(priv->ssh_tunnel_auth_password)) : NULL);
1526 
1528  priv->remmina_file,
1529  "ssh_tunnel_passphrase",
1530  (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);
1531 }
1532 
1534  gchar * setting_name_to_validate,
1535  gconstpointer value,
1536  GError ** err)
1537 {
1538  if (!setting_name_to_validate || !value || !gfe) {
1539  if (!setting_name_to_validate) {
1540  g_critical(_("(%s: %i): Can't validate setting '%s' since 'value' or 'gfe' "
1541  "are NULL!"),
1542  __func__, __LINE__, setting_name_to_validate);
1543  } else {
1544  g_critical(_("(%s: %i): Can't validate user input since "
1545  "'setting_name_to_validate', 'value' or 'gfe' are NULL!"),
1546  __func__, __LINE__);
1547  }
1548  g_set_error(err, 1, 1, _("Internal error."));
1549  return FALSE;
1550  }
1551 
1552  if (strcmp(setting_name_to_validate, "notes_text") == 0) {
1553  // Not a plugin setting. Bail out early.
1554  return TRUE;
1555  }
1556 
1557  const RemminaProtocolSetting *setting_iter;
1558  RemminaProtocolPlugin *protocol_plugin;
1559  RemminaFileEditorPriv *priv = gfe->priv;
1560  protocol_plugin = priv->plugin;
1561 
1562  setting_iter = protocol_plugin->basic_settings;
1563  if (setting_iter) {
1564  // gboolean found = FALSE;
1565  while (setting_iter->type != REMMINA_PROTOCOL_SETTING_TYPE_END) {
1566  if (setting_iter->name == NULL) {
1567  g_error("Internal error: a setting name in protocol plugin %s is "
1568  "null. Please fix RemminaProtocolSetting struct content.",
1569  protocol_plugin->name);
1570  } else if ((gchar *)setting_name_to_validate) {
1571  if (strcmp((gchar *)setting_name_to_validate, setting_iter->name) == 0) {
1572  // found = TRUE;
1573 
1574  gpointer validator_data = setting_iter->validator_data;
1575  GCallback validator = setting_iter->validator;
1576 
1577  // Default behaviour is that everything is valid,
1578  // except a validator is given and its returned GError is not NULL.
1579  GError *err_ret = NULL;
1580 
1581  g_debug("Checking setting '%s' for validation.", setting_iter->name);
1582  if (validator != NULL) {
1583  // Looks weird but it calls the setting's validator
1584  // function using setting_name_to_validate, value and
1585  // validator_data as parameters and it returns a GError*.
1586  err_ret = ((GError * (*)(gpointer, gconstpointer, gpointer)) validator)(setting_name_to_validate, value, validator_data);
1587  }
1588 
1589  if (err_ret) {
1590  g_debug("it has a validator function and it had an error!");
1591  // pass err (returned value) to function caller.
1592  *err = err_ret;
1593  return FALSE;
1594  }
1595 
1596  break;
1597  }
1598  }
1599  setting_iter++;
1600  }
1601 
1602  // if (!found) {
1603  // TOO VERBOSE:
1604  // g_warning("%s is not a plugin setting!", setting_name_to_validate);
1605  // }
1606  }
1607 
1608  return TRUE;
1609 }
1610 
1612  GtkWidget ** failed_widget)
1613 {
1614  TRACE_CALL(__func__);
1615  RemminaFileEditorPriv *priv = gfe->priv;
1616  GHashTableIter iter;
1617  gpointer key;
1618  gpointer widget;
1619  GtkTextBuffer *buffer;
1620  gchar *escaped, *unescaped;
1621  GtkTextIter start, end;
1622 
1623  GError *err = NULL;
1624  *failed_widget = NULL;
1625 
1626  g_hash_table_iter_init(&iter, priv->setting_widgets);
1627  while (g_hash_table_iter_next(&iter, &key, &widget)) {
1628 
1629  // We don't want to save or validate grayed-out settings.
1630  // If widget is a file chooser, it was made not sensitive because
1631  // the box was unchecked. In that case, don't continue. The
1632  // relevant file strings will be set to NULL in the remmina file.
1633  if (!gtk_widget_get_sensitive(GTK_WIDGET(widget)) && !GTK_IS_FILE_CHOOSER(widget)) {
1634  g_debug("Grayed-out setting-widget '%s' will not be saved.",
1635  gtk_widget_get_name(widget));
1636  continue;
1637  }
1638 
1639  if (GTK_IS_ENTRY(widget)) {
1640  const gchar *value = gtk_entry_get_text(GTK_ENTRY(widget));
1641 
1642  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, value, &err)) {
1643  // Error while validating!
1644  // err should be set now.
1645  *failed_widget = widget;
1646  break;
1647  }
1648 
1649  remmina_file_set_string(priv->remmina_file, (gchar *)key, value);
1650  } else if (GTK_IS_TEXT_VIEW(widget)) {
1651  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
1652  gtk_text_buffer_get_start_iter(buffer, &start);
1653  gtk_text_buffer_get_end_iter(buffer, &end);
1654  unescaped = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1655  escaped = g_uri_escape_string(unescaped, NULL, TRUE);
1656 
1657  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, escaped, &err)) {
1658  // Error while validating!
1659  // err should be set now.
1660  *failed_widget = widget;
1661  break;
1662  }
1663 
1664  remmina_file_set_string(priv->remmina_file, (gchar *)key, escaped);
1665  g_free(escaped);
1666  } else if (GTK_IS_COMBO_BOX(widget)) {
1667  gchar *value = remmina_public_combo_get_active_text(GTK_COMBO_BOX(widget));
1668 
1669  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, value, &err)) {
1670  // Error while validating!
1671  // err should be set now.
1672  *failed_widget = widget;
1673  break;
1674  }
1675 
1676  remmina_file_set_string(priv->remmina_file, (gchar *)key, value);
1677  } else if (GTK_IS_FILE_CHOOSER(widget)) {
1678  gchar *value = gtk_widget_get_sensitive(GTK_WIDGET(widget)) ? gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)) : NULL;
1679 
1680  if (!gtk_widget_get_sensitive(GTK_WIDGET(widget))) {
1681  remmina_file_set_string(priv->remmina_file, (gchar *)key, value);
1682  continue;
1683  }
1684 
1685  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, value, &err)) {
1686  // Error while validating!
1687  // err should be set now.
1688  g_free(value);
1689  *failed_widget = widget;
1690  break;
1691  }
1692 
1693  remmina_file_set_string(priv->remmina_file, (gchar *)key, value);
1694  g_free(value);
1695  } else if (GTK_IS_TOGGLE_BUTTON(widget)) {
1696  gboolean value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
1697 
1698  if (!remmina_file_editor_validate_settings(gfe, (gchar *)key, &value, &err)) {
1699  // Error while validating!
1700  // err should be set now.
1701  *failed_widget = widget;
1702  break;
1703  }
1704 
1705  remmina_file_set_int(priv->remmina_file, (gchar *)key, value);
1706  }
1707  }
1708 
1709  if (err) {
1710  return err;
1711  }
1712 
1713  return NULL;
1714 }
1715 
1717  GtkWidget ** failed_widget)
1718 {
1719  TRACE_CALL(__func__);
1720  int res_w, res_h;
1721  gchar *custom_resolution;
1723 
1724  RemminaFileEditorPriv *priv = gfe->priv;
1725 
1726  remmina_file_set_string(priv->remmina_file, "name", gtk_entry_get_text(GTK_ENTRY(priv->name_entry)));
1727 
1728  remmina_file_set_string(priv->remmina_file, "labels", gtk_entry_get_text(GTK_ENTRY(priv->labels_entry)));
1729 
1730  remmina_file_set_string(priv->remmina_file, "group",
1731  (priv->group_combo ? remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->group_combo)) : NULL));
1732 
1733  remmina_file_set_string(priv->remmina_file, "protocol",
1734  remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->protocol_combo)));
1735 
1736  remmina_file_set_string(priv->remmina_file, "server",
1737  (priv->server_combo ? remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->server_combo)) : NULL));
1738 
1739  if (priv->resolution_auto_radio) {
1740  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->resolution_auto_radio))) {
1741  /* Resolution is set to auto (which means: Use client fullscreen resolution, aka use client resolution) */
1742  res_w = res_h = 0;
1743  res_mode = RES_USE_CLIENT;
1744  } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->resolution_iws_radio))) {
1745  /* Resolution is set to initial window size */
1746  res_w = res_h = 0;
1747  res_mode = RES_USE_INITIAL_WINDOW_SIZE;
1748  } else {
1749  /* Resolution is set to a value from the list */
1750  custom_resolution = remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->resolution_custom_combo));
1751  if (remmina_public_split_resolution_string(custom_resolution, &res_w, &res_h))
1752  res_mode = RES_USE_CUSTOM;
1753  else
1754  res_mode = RES_USE_INITIAL_WINDOW_SIZE;
1755  g_free(custom_resolution);
1756  }
1757  remmina_file_set_int(priv->remmina_file, "resolution_mode", res_mode);
1758  remmina_file_set_int(priv->remmina_file, "resolution_width", res_w);
1759  remmina_file_set_int(priv->remmina_file, "resolution_height", res_h);
1760  }
1761 
1762  if (priv->assistance_toggle){
1763  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->assistance_toggle))) {
1764  remmina_file_set_string(priv->remmina_file, "assistance_file", gtk_entry_get_text(GTK_ENTRY(priv->assistance_file)));
1765  remmina_file_set_string(priv->remmina_file, "assistance_pass", gtk_entry_get_text(GTK_ENTRY(priv->assistance_password)));
1766  remmina_file_set_int(priv->remmina_file, "assistance_mode", 1);
1767  }else{
1768  remmina_file_set_int(priv->remmina_file, "assistance_mode", 0);
1769  }
1770 
1771  }
1772 
1773  if (priv->keymap_combo)
1774  remmina_file_set_string(priv->remmina_file, "keymap",
1775  remmina_public_combo_get_active_text(GTK_COMBO_BOX(priv->keymap_combo)));
1776 
1779  return remmina_file_editor_update_settings(gfe, failed_widget);
1780 }
1781 
1782 static void remmina_file_editor_on_default(GtkWidget *button, RemminaFileEditor *gfe)
1783 {
1784  TRACE_CALL(__func__);
1785  RemminaFile *gf;
1786  GtkWidget *dialog;
1787 
1788  GtkWidget *failed_widget = NULL;
1789  GError *err = remmina_file_editor_update(gfe, &failed_widget);
1790  if (err) {
1791  g_warning(_("Couldn't validate user input. %s"), err->message);
1792  remmina_file_editor_show_validation_error_popup(gfe, failed_widget, err);
1793  return;
1794  }
1795 
1796  gf = remmina_file_dup(gfe->priv->remmina_file);
1797 
1799 
1800  /* Clear properties that should never be default */
1801  remmina_file_set_string(gf, "name", NULL);
1802  remmina_file_set_string(gf, "server", NULL);
1803  remmina_file_set_string(gf, "password", NULL);
1804  remmina_file_set_string(gf, "precommand", NULL);
1805  remmina_file_set_string(gf, "postcommand", NULL);
1806 
1807  remmina_file_set_string(gf, "ssh_tunnel_server", NULL);
1808  remmina_file_set_string(gf, "ssh_tunnel_password", NULL);
1809  remmina_file_set_string(gf, "ssh_tunnel_passphrase", NULL);
1810 
1811  remmina_file_save(gf);
1812  remmina_file_free(gf);
1813 
1814  dialog = gtk_message_dialog_new(GTK_WINDOW(gfe), GTK_DIALOG_MODAL, GTK_MESSAGE_INFO,
1815  GTK_BUTTONS_OK, _("Default settings saved."));
1816  gtk_dialog_run(GTK_DIALOG(dialog));
1817  gtk_widget_destroy(dialog);
1818 }
1819 
1820 static void remmina_file_editor_on_save(GtkWidget *button, RemminaFileEditor *gfe)
1821 {
1822  TRACE_CALL(__func__);
1823 
1824  GtkWidget *failed_widget = NULL;
1825  GError *err = remmina_file_editor_update(gfe, &failed_widget);
1826  if (err) {
1827  g_warning(_("Couldn't validate user input. %s"), err->message);
1828  remmina_file_editor_show_validation_error_popup(gfe, failed_widget, err);
1829  return;
1830  }
1831 
1833 
1834  remmina_file_save(gfe->priv->remmina_file);
1836 
1837  gtk_widget_destroy(GTK_WIDGET(gfe));
1838 }
1839 
1840 static void remmina_file_editor_on_connect(GtkWidget *button, RemminaFileEditor *gfe)
1841 {
1842  TRACE_CALL(__func__);
1843  RemminaFile *gf;
1844 
1845  GtkWidget *failed_widget = NULL;
1846  GError *err = remmina_file_editor_update(gfe, &failed_widget);
1847  if (err) {
1848  g_warning(_("Couldn't validate user input. %s"), err->message);
1849  remmina_file_editor_show_validation_error_popup(gfe, failed_widget, err);
1850  return;
1851  }
1852 
1853  gf = remmina_file_dup(gfe->priv->remmina_file);
1854  /* Put server into name for "Quick Connect" */
1855  if (remmina_file_get_filename(gf) == NULL)
1856  remmina_file_set_string(gf, "name", remmina_file_get_string(gf, "server"));
1857  gtk_widget_destroy(GTK_WIDGET(gfe));
1858  gf->prevent_saving = TRUE;
1859  rcw_open_from_file(gf);
1860 }
1861 
1862 static void remmina_file_editor_on_save_connect(GtkWidget *button, RemminaFileEditor *gfe)
1863 {
1864  TRACE_CALL(__func__);
1866  RemminaFile *gf;
1867 
1868  GtkWidget *failed_widget = NULL;
1869  GError *err = remmina_file_editor_update(gfe, &failed_widget);
1870  if (err) {
1871  g_warning(_("Couldn't validate user input. %s"), err->message);
1872  remmina_file_editor_show_validation_error_popup(gfe, failed_widget, err);
1873  return;
1874  }
1875 
1877 
1878  remmina_file_save(gfe->priv->remmina_file);
1880 
1881  gf = remmina_file_dup(gfe->priv->remmina_file);
1882  /* Put server into name for Quick Connect */
1883  if (remmina_file_get_filename(gf) == NULL)
1884  remmina_file_set_string(gf, "name", remmina_file_get_string(gf, "server"));
1885  gtk_widget_destroy(GTK_WIDGET(gfe));
1886  rcw_open_from_file(gf);
1887 }
1888 
1889 static void remmina_file_editor_on_cancel(GtkWidget *button, RemminaFileEditor *gfe)
1890 {
1891  TRACE_CALL(__func__);
1892  gtk_widget_destroy(GTK_WIDGET(gfe));
1893 }
1894 
1896 {
1897  TRACE_CALL(__func__);
1898  RemminaFileEditorPriv *priv;
1899  GtkWidget *widget;
1900 
1901  priv = g_new0(RemminaFileEditorPriv, 1);
1902  gfe->priv = priv;
1903 
1904  /* Create the editor dialog */
1905  gtk_window_set_title(GTK_WINDOW(gfe), _("Remote Connection Profile"));
1906 
1907  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("_Cancel")), GTK_RESPONSE_CANCEL);
1908  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_cancel), gfe);
1909 
1910  /* Default button */
1911  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("Save as Default")), GTK_RESPONSE_OK);
1912  gtk_widget_set_tooltip_text(GTK_WIDGET(widget), _("Use the current settings as the default for all new connection profiles"));
1913  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_default), gfe);
1914 
1915  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("_Save")), GTK_RESPONSE_APPLY);
1916  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_save), gfe);
1917  gtk_widget_set_sensitive(widget, FALSE);
1918  priv->save_button = widget;
1919 
1920  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("Connect")), GTK_RESPONSE_ACCEPT);
1921  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_connect), gfe);
1922 
1923  widget = gtk_dialog_add_button(GTK_DIALOG(gfe), (_("_Save and Connect")), GTK_RESPONSE_OK);
1924  gtk_widget_set_can_default(widget, TRUE);
1925  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_file_editor_on_save_connect), gfe);
1926 
1927  gtk_dialog_set_default_response(GTK_DIALOG(gfe), GTK_RESPONSE_OK);
1928  gtk_window_set_default_size(GTK_WINDOW(gfe), 800, 600);
1929 
1930  g_signal_connect(G_OBJECT(gfe), "destroy", G_CALLBACK(remmina_file_editor_destroy), NULL);
1931  g_signal_connect(G_OBJECT(gfe), "realize", G_CALLBACK(remmina_file_editor_on_realize), NULL);
1932 
1933  priv->setting_widgets = g_hash_table_new(g_str_hash, g_str_equal);
1934 
1935  remmina_widget_pool_register(GTK_WIDGET(gfe));
1936 }
1937 
1938 static gboolean remmina_file_editor_iterate_protocol(gchar *protocol, RemminaPlugin *plugin, gpointer data)
1939 {
1940  TRACE_CALL(__func__);
1941  RemminaFileEditor *gfe = REMMINA_FILE_EDITOR(data);
1942  GtkListStore *store;
1943  GtkTreeIter iter;
1944  gboolean first;
1945 
1946  store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(gfe->priv->protocol_combo)));
1947 
1948  first = !gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1949 
1950  gtk_list_store_append(store, &iter);
1951  gtk_list_store_set(store, &iter, 0, protocol, 1, g_dgettext(plugin->domain, plugin->description), 2,
1952  ((RemminaProtocolPlugin *)plugin)->icon_name, -1);
1953 
1954  if (first || g_strcmp0(protocol, remmina_file_get_string(gfe->priv->remmina_file, "protocol")) == 0)
1955  gtk_combo_box_set_active_iter(GTK_COMBO_BOX(gfe->priv->protocol_combo), &iter);
1956 
1957  return FALSE;
1958 }
1959 
1961 {
1962  TRACE_CALL(__func__);
1963  RemminaFileEditorPriv *priv;
1964 
1965  priv = gfe->priv;
1966  gtk_widget_set_sensitive(priv->group_combo, TRUE);
1967  gtk_widget_set_sensitive(priv->save_button, TRUE);
1968 }
1969 
1970 static void remmina_file_editor_entry_on_changed(GtkEditable *editable, RemminaFileEditor *gfe)
1971 {
1972  TRACE_CALL(__func__);
1973  RemminaFileEditorPriv *priv;
1974 
1975  priv = gfe->priv;
1976  if (remmina_file_get_filename(priv->remmina_file) == NULL) {
1977  remmina_file_generate_filename(priv->remmina_file);
1978  /* TODO: Probably to be removed */
1980  } else {
1981  remmina_file_delete(remmina_file_get_filename(priv->remmina_file));
1982  remmina_file_generate_filename(priv->remmina_file);
1984  }
1985 }
1986 
1988 {
1989  TRACE_CALL(__func__);
1990  RemminaFileEditorPriv *priv;
1991 
1992  priv = gfe->priv;
1993  if (remmina_file_get_filename(priv->remmina_file) == NULL) {
1994  remmina_file_generate_filename(priv->remmina_file);
1995  } else {
1996  remmina_file_delete(remmina_file_get_filename(priv->remmina_file));
1997  remmina_file_generate_filename(priv->remmina_file);
1998  }
1999 }
2000 
2002 {
2003  TRACE_CALL(__func__);
2004  RemminaFileEditor *gfe;
2005  RemminaFileEditorPriv *priv;
2006  GtkWidget *grid;
2007  GtkWidget *widget;
2008  gchar *groups;
2009  gchar *s;
2010  const gchar *cs;
2011 
2012  gfe = REMMINA_FILE_EDITOR(g_object_new(REMMINA_TYPE_FILE_EDITOR, NULL));
2013  priv = gfe->priv;
2014  priv->remmina_file = remminafile;
2015 
2016  if (remmina_file_get_filename(remminafile) == NULL)
2017  gtk_dialog_set_response_sensitive(GTK_DIALOG(gfe), GTK_RESPONSE_APPLY, FALSE);
2018 
2019  /* Create the "Profile" group on the top (for name and protocol) */
2020  grid = gtk_grid_new();
2021  gtk_widget_show(grid);
2022  gtk_grid_set_row_spacing(GTK_GRID(grid), 4);
2023  gtk_grid_set_column_spacing(GTK_GRID(grid), 8);
2024  gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE);
2025  gtk_container_set_border_width(GTK_CONTAINER(grid), 8);
2026  gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(gfe))), grid, FALSE, FALSE, 2);
2027 
2028  // remmina_public_create_group(GTK_GRID(grid), _("Profile"), 0, 4, 3);
2029 
2030  gboolean profile_file_exists = (remmina_file_get_filename(remminafile) != NULL);
2031 
2032  /* Profile: Name */
2033  widget = gtk_label_new(_("Name"));
2034  gtk_widget_show(widget);
2035  gtk_widget_set_valign(widget, GTK_ALIGN_START);
2036  gtk_widget_set_halign(widget, GTK_ALIGN_START);
2037  gtk_grid_attach(GTK_GRID(grid), widget, 0, 3, 2, 1);
2038  gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
2039 
2040  widget = gtk_entry_new();
2041  gtk_widget_show(widget);
2042  gtk_grid_attach(GTK_GRID(grid), widget, 1, 3, 3, 1);
2043  gtk_entry_set_max_length(GTK_ENTRY(widget), 100);
2044  priv->name_entry = widget;
2045 
2046  if (!profile_file_exists) {
2047  gtk_entry_set_text(GTK_ENTRY(widget), _("Quick Connect"));
2048 #if GTK_CHECK_VERSION(3, 16, 0)
2049  gtk_entry_grab_focus_without_selecting(GTK_ENTRY(widget));
2050 #endif
2051  g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(remmina_file_editor_entry_on_changed), gfe);
2052  } else {
2053  cs = remmina_file_get_string(remminafile, "name");
2054  gtk_entry_set_text(GTK_ENTRY(widget), cs ? cs : "");
2055  }
2056 
2057  /* Profile: Group */
2058  widget = gtk_label_new(_("Group"));
2059  gtk_widget_show(widget);
2060  gtk_widget_set_valign(widget, GTK_ALIGN_START);
2061  gtk_widget_set_halign(widget, GTK_ALIGN_START);
2062  gtk_grid_attach(GTK_GRID(grid), widget, 0, 6, 2, 1);
2063 
2065  priv->group_combo = remmina_public_create_combo_entry(groups, remmina_file_get_string(remminafile, "group"), FALSE);
2066  g_free(groups);
2067  gtk_widget_show(priv->group_combo);
2068  gtk_grid_attach(GTK_GRID(grid), priv->group_combo, 1, 6, 3, 1);
2069  gtk_widget_set_sensitive(priv->group_combo, FALSE);
2070 
2071  s = g_strdup_printf(_("Use '%s' as subgroup delimiter"), "/");
2072  gtk_widget_set_tooltip_text(priv->group_combo, s);
2073  g_free(s);
2074 
2075  /* Profile: Labels */
2076  widget = gtk_label_new(_("Labels"));
2077  gtk_widget_show(widget);
2078  gtk_widget_set_valign(widget, GTK_ALIGN_START);
2079  gtk_widget_set_halign(widget, GTK_ALIGN_START);
2080  gtk_grid_attach(GTK_GRID(grid), widget, 0, 9, 2, 1);
2081  gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
2082 
2083  widget = gtk_entry_new();
2084  gtk_widget_show(widget);
2085  gtk_grid_attach(GTK_GRID(grid), widget, 1, 9, 3, 1);
2086  gtk_entry_set_max_length(GTK_ENTRY(widget), 255);
2087  priv->labels_entry = widget;
2088 
2089  if (!profile_file_exists) {
2090  gtk_widget_set_tooltip_text(widget, _("Label1,Label2"));
2091 #if GTK_CHECK_VERSION(3, 16, 0)
2092  gtk_entry_grab_focus_without_selecting(GTK_ENTRY(widget));
2093 #endif
2094  g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(remmina_file_editor_entry_on_changed), gfe);
2095  } else {
2096  cs = remmina_file_get_string(remminafile, "labels");
2097  gtk_entry_set_text(GTK_ENTRY(widget), cs ? cs : "");
2098  }
2099 
2100  /* Profile: Protocol */
2101  widget = gtk_label_new(_("Protocol"));
2102  gtk_widget_show(widget);
2103  gtk_widget_set_valign(widget, GTK_ALIGN_START);
2104  gtk_widget_set_halign(widget, GTK_ALIGN_START);
2105  gtk_grid_attach(GTK_GRID(grid), widget, 0, 12, 2, 1);
2106 
2107  widget = remmina_public_create_combo(TRUE);
2108  gtk_widget_show(widget);
2109  gtk_grid_attach(GTK_GRID(grid), widget, 1, 12, 3, 1);
2110  priv->protocol_combo = widget;
2112  g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(remmina_file_editor_protocol_combo_on_changed), gfe);
2113 
2114  /* Create the "Preference" frame */
2115  widget = gtk_event_box_new();
2116  gtk_widget_show(widget);
2117  gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(gfe))), widget, TRUE, TRUE, 2);
2118  priv->config_box = widget;
2119 
2120  priv->config_container = NULL;
2121  priv->config_scrollable = NULL;
2122 
2123  remmina_file_editor_protocol_combo_on_changed(GTK_COMBO_BOX(priv->protocol_combo), gfe);
2124 
2126 
2127  return GTK_WIDGET(gfe);
2128 }
2129 
2130 GtkWidget *remmina_file_editor_new(void)
2131 {
2132  TRACE_CALL(__func__);
2133  return remmina_file_editor_new_full(NULL, NULL);
2134 }
2135 
2136 GtkWidget *remmina_file_editor_new_full(const gchar *server, const gchar *protocol)
2137 {
2138  TRACE_CALL(__func__);
2139  RemminaFile *remminafile;
2140 
2141  remminafile = remmina_file_new();
2142  if (server)
2143  remmina_file_set_string(remminafile, "server", server);
2144  if (protocol)
2145  remmina_file_set_string(remminafile, "protocol", protocol);
2146 
2147  return remmina_file_editor_new_from_file(remminafile);
2148 }
2149 
2150 GtkWidget *remmina_file_editor_new_copy(const gchar *filename)
2151 {
2152  TRACE_CALL(__func__);
2153  RemminaFile *remminafile;
2154  GtkWidget *dialog;
2155 
2156  remminafile = remmina_file_copy(filename);
2157 
2158  if (remminafile) {
2159  return remmina_file_editor_new_from_file(remminafile);
2160  } else {
2161  dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
2162  _("Could not find the file “%s”."), filename);
2163  gtk_dialog_run(GTK_DIALOG(dialog));
2164  gtk_widget_destroy(dialog);
2165  return NULL;
2166  }
2167 }
2168 
2169 GtkWidget *remmina_file_editor_new_from_filename(const gchar *filename)
2170 {
2171  TRACE_CALL(__func__);
2172  RemminaFile *remminafile;
2173 
2174  remminafile = remmina_file_manager_load_file(filename);
2175  if (remminafile) {
2176  if (remmina_file_get_int(remminafile, "profile-lock", FALSE) && remmina_unlock_new(remmina_main_get_window()) == 0)
2177  return NULL;
2178  return remmina_file_editor_new_from_file(remminafile);
2179  } else {
2180  GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
2181  _("Could not find the file “%s”."), filename);
2182  gtk_dialog_run(GTK_DIALOG(dialog));
2183  gtk_widget_destroy(dialog);
2184  return NULL;
2185  }
2186 }
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)
GtkDialog * remmina_string_list_new(gboolean two_columns, const gchar *fields_separator)
@@ -95,15 +95,15 @@ $(document).ready(function(){initNavTree('remmina__file__editor_8c_source.html',
const gchar * remmina_file_get_string(RemminaFile *remminafile, const gchar *setting)
Definition: remmina_file.c:516
gchar ** remmina_pref_keymap_groups(void)
-
static void remmina_file_editor_create_resolution(RemminaFileEditor *gfe, const RemminaProtocolSetting *setting, GtkWidget *grid, gint row)
+
static void remmina_file_editor_create_resolution(RemminaFileEditor *gfe, const RemminaProtocolSetting *setting, GtkWidget *grid, gint row)
-
static GtkWidget * remmina_file_editor_create_combo(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *list, const gchar *value, gchar *setting_name)
+
static GtkWidget * remmina_file_editor_create_combo(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *list, const gchar *value, gchar *setting_name)
RemminaFile * remmina_file_dup(RemminaFile *remminafile)
Definition: remmina_file.c:865
GtkWidget * remmina_public_create_combo_entry(const gchar *text, const gchar *def, gboolean descending)
-
static void remmina_file_editor_on_connect(GtkWidget *button, RemminaFileEditor *gfe)
+
static void remmina_file_editor_on_connect(GtkWidget *button, RemminaFileEditor *gfe)
gchar * remmina_pref_file
Definition: rcw.c:78
GtkWindow * remmina_main_get_window()
@@ -112,13 +112,13 @@ $(document).ready(function(){initNavTree('remmina__file__editor_8c_source.html',
G_DEFINE_TYPE(RemminaFileEditor, remmina_file_editor, GTK_TYPE_DIALOG)
-
static GtkWidget * remmina_file_editor_create_int(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gint value, gint left, gint right, gchar *setting_name)
-
static void remmina_file_editor_ssh_tunnel_enabled_check_on_toggled(GtkToggleButton *togglebutton, RemminaFileEditor *gfe, RemminaProtocolSSHSetting ssh_setting)
+
static GtkWidget * remmina_file_editor_create_int(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gint value, gint left, gint right, gchar *setting_name)
+
static void remmina_file_editor_ssh_tunnel_enabled_check_on_toggled(GtkToggleButton *togglebutton, RemminaFileEditor *gfe, RemminaProtocolSSHSetting ssh_setting)
const gchar * remmina_file_get_filename(RemminaFile *remminafile)
Definition: remmina_file.c:210
-
static GtkWidget * remmina_file_editor_create_double(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, gdouble value, gint left, gint right, gchar *setting_name)
-
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)
+
static GtkWidget * remmina_file_editor_create_double(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, gdouble value, gint left, gint right, gchar *setting_name)
+
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)
+
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_widget_pool_register(GtkWidget *widget)
@@ -132,12 +132,12 @@ $(document).ready(function(){initNavTree('remmina__file__editor_8c_source.html',
const gchar * domain
Definition: plugin.h:60
gchar * remmina_pref_get_recent(const gchar *protocol)
Definition: remmina_pref.c:974
-
static void remmina_file_editor_create_server(RemminaFileEditor *gfe, const RemminaProtocolSetting *setting, GtkWidget *grid, gint row)
+
static void remmina_file_editor_create_server(RemminaFileEditor *gfe, const RemminaProtocolSetting *setting, GtkWidget *grid, gint row)
void remmina_icon_populate_menu(void)
Definition: remmina_icon.c:256
-
GtkWidget * remmina_file_editor_new_full(const gchar *server, const gchar *protocol)
+
GtkWidget * remmina_file_editor_new_full(const gchar *server, const gchar *protocol)
gdouble remmina_file_get_double(RemminaFile *remminafile, const gchar *setting, gdouble default_value)
Definition: remmina_file.c:629
-
static void remmina_file_editor_init(RemminaFileEditor *gfe)
+
static void remmina_file_editor_init(RemminaFileEditor *gfe)
gchar * resolutions
Definition: remmina_pref.h:145
void remmina_file_generate_filename(RemminaFile *remminafile)
Generate a new Remmina connection profile file name.
Definition: remmina_file.c:114
@@ -149,17 +149,17 @@ $(document).ready(function(){initNavTree('remmina__file__editor_8c_source.html',
RemminaProtocolWidgetResolutionMode
Definition: types.h:148
static void remmina_file_editor_show_validation_error_popup(RemminaFileEditor *gfe, GtkWidget *failed_widget, GError *err)
Shows a tooltip-like window which tells the user what they did wrong to trigger the validation functi...
GtkWidget * remmina_public_create_combo_map(const gpointer *key_value_list, const gchar *def, gboolean use_icon, const gchar *domain)
-
static GtkWidget * remmina_file_editor_create_chooser(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gint type, gchar *setting_name)
Create checkbox + gtk_file_chooser for open files and select folders.
+
static GtkWidget * remmina_file_editor_create_chooser(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gint type, gchar *setting_name)
Create checkbox + gtk_file_chooser for open files and select folders.
-
static GtkWidget * remmina_file_editor_create_password(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gchar *setting_name)
+
static GtkWidget * remmina_file_editor_create_password(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gchar *setting_name)
void remmina_file_set_filename(RemminaFile *remminafile, const gchar *filename)
Definition: remmina_file.c:182
-
static gpointer ssh_tunnel_auth_list[]
+
static gpointer ssh_tunnel_auth_list[]
-
static GError * remmina_file_editor_update_settings(RemminaFileEditor *gfe, GtkWidget **failed_widget)
+
static GError * remmina_file_editor_update_settings(RemminaFileEditor *gfe, GtkWidget **failed_widget)
-
void remmina_file_editor_check_profile(RemminaFileEditor *gfe)
+
void remmina_file_editor_check_profile(RemminaFileEditor *gfe)
GtkWidget * remmina_public_create_combo_text_d(const gchar *text, const gchar *def, const gchar *empty_choice)
struct _RemminaFileEditorPriv RemminaFileEditorPriv
static GtkWidget * remmina_file_editor_create_notebook_tab(RemminaFileEditor *gfe, const gchar *stock_id, const gchar *label, gint rows, gint cols)
@@ -170,24 +170,24 @@ $(document).ready(function(){initNavTree('remmina__file__editor_8c_source.html',
void remmina_file_set_int(RemminaFile *remminafile, const gchar *setting, gint value)
Definition: remmina_file.c:585
void remmina_file_delete(const gchar *filename)
Definition: remmina_file.c:911
-
static void remmina_file_editor_entry_on_changed(GtkEditable *editable, RemminaFileEditor *gfe)
-
static GtkWidget * remmina_file_editor_create_text2(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gint left, gint right, gchar *setting_name)
+
static void remmina_file_editor_entry_on_changed(GtkEditable *editable, RemminaFileEditor *gfe)
+
static GtkWidget * remmina_file_editor_create_text2(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gint left, gint right, gchar *setting_name)
const RemminaProtocolSetting * basic_settings
Definition: plugin.h:74
-
static GtkWidget * remmina_file_editor_create_check(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint top, const gchar *label, gboolean value, gchar *setting_name)
-
static void remmina_file_editor_create_all_settings(RemminaFileEditor *gfe)
+
static GtkWidget * remmina_file_editor_create_check(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint top, const gchar *label, gboolean value, gchar *setting_name)
+
static void remmina_file_editor_create_all_settings(RemminaFileEditor *gfe)
static void remmina_file_editor_create_notebook_container(RemminaFileEditor *gfe)
-
GtkWidget * remmina_file_editor_new_from_file(RemminaFile *remminafile)
+
GtkWidget * remmina_file_editor_new_from_file(RemminaFile *remminafile)
gpointer validator_data
Definition: types.h:124
-
void remmina_file_editor_int_setting_filter(GtkEditable *editable, const gchar *text, gint length, gint *position, gpointer data)
+
void remmina_file_editor_int_setting_filter(GtkEditable *editable, const gchar *text, gint length, gint *position, gpointer data)
void remmina_string_list_set_text(const gchar *text, const gboolean clear_data)
-
GtkWidget * remmina_file_editor_new_from_filename(const gchar *filename)
+
GtkWidget * remmina_file_editor_new_from_filename(const gchar *filename)
-
static void remmina_file_editor_create_ssh_tunnel_tab(RemminaFileEditor *gfe, RemminaProtocolSSHSetting ssh_setting)
+
static void remmina_file_editor_create_ssh_tunnel_tab(RemminaFileEditor *gfe, RemminaProtocolSSHSetting ssh_setting)
RemminaProtocolSettingType type
Definition: types.h:118
@@ -196,58 +196,58 @@ $(document).ready(function(){initNavTree('remmina__file__editor_8c_source.html',
RemminaFile * remmina_file_new(void)
Definition: remmina_file.c:93
void remmina_string_list_set_titles(gchar *title1, gchar *title2)
GCallback validator
Definition: types.h:125
-
static void remmina_file_editor_create_settings(RemminaFileEditor *gfe, GtkWidget *grid, const RemminaProtocolSetting *settings)
-
static void remmina_file_editor_on_save_connect(GtkWidget *button, RemminaFileEditor *gfe)
+
static void remmina_file_editor_create_settings(RemminaFileEditor *gfe, GtkWidget *grid, const RemminaProtocolSetting *settings)
+
static void remmina_file_editor_on_save_connect(GtkWidget *button, RemminaFileEditor *gfe)
void rcw_open_from_file(RemminaFile *remminafile)
Definition: rcw.c:4446
RemminaPref remmina_pref
Definition: rcw.c:79
-
void remmina_file_editor_file_save(RemminaFileEditor *gfe)
+
void remmina_file_editor_file_save(RemminaFileEditor *gfe)
gint remmina_file_get_int(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: remmina_file.c:603
-
GtkWidget * remmina_file_editor_new(void)
+
GtkWidget * remmina_file_editor_new(void)
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)
+
static void remmina_file_editor_on_cancel(GtkWidget *button, RemminaFileEditor *gfe)
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
-
static void remmina_file_editor_on_save(GtkWidget *button, RemminaFileEditor *gfe)
-
static void remmina_file_editor_on_default(GtkWidget *button, RemminaFileEditor *gfe)
+
static void remmina_file_editor_on_save(GtkWidget *button, RemminaFileEditor *gfe)
+
static void remmina_file_editor_on_default(GtkWidget *button, RemminaFileEditor *gfe)
static void remmina_file_editor_assistance_enabled_check_on_toggled(GtkToggleButton *togglebutton, RemminaFileEditor *gfe)
const gchar * name
Definition: plugin.h:67
void remmina_main_toggle_password_view(GtkWidget *widget, gpointer data)
-
void remmina_file_editor_double_setting_filter(GtkEditable *editable, const gchar *text, gint length, gint *position, gpointer data)
+
void remmina_file_editor_double_setting_filter(GtkEditable *editable, const gchar *text, gint length, gint *position, gpointer data)
gchar * remmina_file_manager_get_groups(void)
-
static GtkWidget * remmina_file_editor_create_text(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gchar *setting_name)
+
static GtkWidget * remmina_file_editor_create_text(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gchar *setting_name)
void remmina_file_set_string(RemminaFile *remminafile, const gchar *setting, const gchar *value)
Definition: remmina_file.c:469
-
static void remmina_file_editor_save_behavior_tab(RemminaFileEditor *gfe)
-
static void remmina_file_editor_create_behavior_tab(RemminaFileEditor *gfe)
+
static void remmina_file_editor_save_behavior_tab(RemminaFileEditor *gfe)
+
static void remmina_file_editor_create_behavior_tab(RemminaFileEditor *gfe)
void remmina_file_save(RemminaFile *remminafile)
Definition: remmina_file.c:730
-
static void remmina_file_editor_browse_resolution(GtkWidget *button, RemminaFileEditor *gfe)
-
static gboolean remmina_file_editor_validate_settings(RemminaFileEditor *gfe, gchar *setting_name_to_validate, gconstpointer value, GError **err)
+
static void remmina_file_editor_browse_resolution(GtkWidget *button, RemminaFileEditor *gfe)
+
static gboolean remmina_file_editor_validate_settings(RemminaFileEditor *gfe, gchar *setting_name_to_validate, gconstpointer value, GError **err)
static void remmina_file_editor_ssh_tunnel_server_custom_radio_on_toggled(GtkToggleButton *togglebutton, RemminaFileEditor *gfe)
RemminaFile * remmina_file_copy(const gchar *filename)
Definition: remmina_file.c:217
-
static void remmina_file_editor_create_assistance(RemminaFileEditor *gfe, const RemminaProtocolSetting *setting, GtkWidget *grid, gint row)
+
static void remmina_file_editor_create_assistance(RemminaFileEditor *gfe, const RemminaProtocolSetting *setting, GtkWidget *grid, gint row)
RemminaFile * remmina_file_manager_load_file(const gchar *filename)
-
static gboolean remmina_file_editor_iterate_protocol(gchar *protocol, RemminaPlugin *plugin, gpointer data)
-
static GError * remmina_file_editor_update(RemminaFileEditor *gfe, GtkWidget **failed_widget)
+
static gboolean remmina_file_editor_iterate_protocol(gchar *protocol, RemminaPlugin *plugin, gpointer data)
+
static GError * remmina_file_editor_update(RemminaFileEditor *gfe, GtkWidget **failed_widget)
-
GtkWidget * remmina_file_editor_new_copy(const gchar *filename)
+
GtkWidget * remmina_file_editor_new_copy(const gchar *filename)
RemminaFileEditorPriv * priv
-
static GtkWidget * remmina_file_editor_create_textarea(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gchar *setting_name)
+
static GtkWidget * remmina_file_editor_create_textarea(RemminaFileEditor *gfe, GtkWidget *grid, gint row, gint col, const gchar *label, const gchar *value, gchar *setting_name)
static void remmina_file_editor_browse_avahi(GtkWidget *button, RemminaFileEditor *gfe)
gint remmina_unlock_new(GtkWindow *parent)
void remmina_public_load_combo_text_d(GtkWidget *combo, const gchar *text, const gchar *def, const gchar *empty_choice)
-
static void remmina_file_editor_update_resolution(GtkWidget *widget, RemminaFileEditor *gfe)
+
static void remmina_file_editor_update_resolution(GtkWidget *widget, RemminaFileEditor *gfe)
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
static void remmina_file_editor_class_init(RemminaFileEditorClass *klass)
const gchar * label
Definition: types.h:120
-
static void remmina_file_editor_save_ssh_tunnel_tab(RemminaFileEditor *gfe)
+
static void remmina_file_editor_save_ssh_tunnel_tab(RemminaFileEditor *gfe)
gchar * remmina_string_list_get_text(void)
const gchar * name
Definition: types.h:119
diff --git a/public/remmina__file__editor_8h.html b/public/remmina__file__editor_8h.html index dec212dca..3a1a15824 100644 --- a/public/remmina__file__editor_8h.html +++ b/public/remmina__file__editor_8h.html @@ -190,7 +190,7 @@ Functions
-

Definition at line 1963 of file remmina_file_editor.c.

+

Definition at line 1960 of file remmina_file_editor.c.

@@ -210,7 +210,7 @@ Functions
-

Definition at line 1990 of file remmina_file_editor.c.

+

Definition at line 1987 of file remmina_file_editor.c.

@@ -248,7 +248,7 @@ Functions
-

Definition at line 2133 of file remmina_file_editor.c.

+

Definition at line 2130 of file remmina_file_editor.c.

@@ -268,7 +268,7 @@ Functions
-

Definition at line 2153 of file remmina_file_editor.c.

+

Definition at line 2150 of file remmina_file_editor.c.

@@ -288,7 +288,7 @@ Functions
-

Definition at line 2004 of file remmina_file_editor.c.

+

Definition at line 2001 of file remmina_file_editor.c.

@@ -308,7 +308,7 @@ Functions
-

Definition at line 2172 of file remmina_file_editor.c.

+

Definition at line 2169 of file remmina_file_editor.c.

@@ -338,7 +338,7 @@ Functions
-

Definition at line 2139 of file remmina_file_editor.c.

+

Definition at line 2136 of file remmina_file_editor.c.

diff --git a/public/remmina__file__editor_8h_source.html b/public/remmina__file__editor_8h_source.html index eea117042..f5c4a6322 100644 --- a/public/remmina__file__editor_8h_source.html +++ b/public/remmina__file__editor_8h_source.html @@ -89,19 +89,19 @@ $(document).ready(function(){initNavTree('remmina__file__editor_8h_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 #pragma once
38 
39 #include <stdarg.h>
40 #include <glib/gi18n.h>
41 #include <gtk/gtk.h>
42 
43 #include "remmina_file.h"
44 
45 G_BEGIN_DECLS
46 
47 #define REMMINA_TYPE_FILE_EDITOR (remmina_file_editor_get_type())
48 #define REMMINA_FILE_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), REMMINA_TYPE_FILE_EDITOR, RemminaFileEditor))
49 #define REMMINA_FILE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), REMMINA_TYPE_FILE_EDITOR, RemminaFileEditorClass))
50 #define REMMINA_IS_FILE_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), REMMINA_TYPE_FILE_EDITOR))
51 #define REMMINA_IS_FILE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), REMMINA_TYPE_FILE_EDITOR))
52 #define REMMINA_FILE_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), REMMINA_TYPE_FILE_EDITOR, RemminaFileEditorClass))
53 
54 typedef struct _RemminaFileEditorPriv RemminaFileEditorPriv;
55 
56 typedef struct _RemminaFileEditor {
57  GtkDialog dialog;
58 
61 
62 typedef struct _RemminaFileEditorClass {
63  GtkDialogClass parent_class;
65 
67 G_GNUC_CONST;
68 
69 /* Base constructor */
70 GtkWidget *remmina_file_editor_new_from_file(RemminaFile *remminafile);
71 /* Create new file */
72 GtkWidget *remmina_file_editor_new(void);
73 GtkWidget *remmina_file_editor_new_full(const gchar *server, const gchar *protocol);
74 GtkWidget *remmina_file_editor_new_copy(const gchar *filename);
75 /* Open existing file */
76 GtkWidget *remmina_file_editor_new_from_filename(const gchar *filename);
79 
80 G_END_DECLS
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:44
struct _RemminaFileEditor RemminaFileEditor
-
void remmina_file_editor_check_profile(RemminaFileEditor *gfe)
+
void remmina_file_editor_check_profile(RemminaFileEditor *gfe)
struct _RemminaFileEditorPriv RemminaFileEditorPriv
-
void remmina_file_editor_file_save(RemminaFileEditor *gfe)
-
GtkWidget * remmina_file_editor_new_copy(const gchar *filename)
+
void remmina_file_editor_file_save(RemminaFileEditor *gfe)
+
GtkWidget * remmina_file_editor_new_copy(const gchar *filename)
struct _RemminaFileEditorClass RemminaFileEditorClass
-
GtkWidget * remmina_file_editor_new(void)
+
GtkWidget * remmina_file_editor_new(void)
GType remmina_file_editor_get_type(void) G_GNUC_CONST
-
GtkWidget * remmina_file_editor_new_from_file(RemminaFile *remminafile)
-
GtkWidget * remmina_file_editor_new_from_filename(const gchar *filename)
-
GtkWidget * remmina_file_editor_new_full(const gchar *server, const gchar *protocol)
+
GtkWidget * remmina_file_editor_new_from_file(RemminaFile *remminafile)
+
GtkWidget * remmina_file_editor_new_from_filename(const gchar *filename)
+
GtkWidget * remmina_file_editor_new_full(const gchar *server, const gchar *protocol)
RemminaFileEditorPriv * priv
diff --git a/public/remmina__main_8c_source.html b/public/remmina__main_8c_source.html index c443f3340..a3d449c57 100644 --- a/public/remmina__main_8c_source.html +++ b/public/remmina__main_8c_source.html @@ -234,7 +234,7 @@ $(document).ready(function(){initNavTree('remmina__main_8c_source.html','');});
gint view_file_mode
Definition: remmina_pref.h:213
RemminaStringArray * remmina_string_array_new(void)
void remmina_file_touch(RemminaFile *remminafile)
Update the atime and mtime of a given filename.
-
GtkWidget * remmina_file_editor_new_from_filename(const gchar *filename)
+
GtkWidget * remmina_file_editor_new_from_filename(const gchar *filename)
Definition: remmina_main.c:79
static void remmina_main_save_expanded_group_func(GtkTreeView *tree_view, GtkTreePath *path, gpointer user_data)
Definition: remmina_main.c:150
void remmina_main_on_show(GtkWidget *w, gpointer user_data)
@@ -261,7 +261,7 @@ $(document).ready(function(){initNavTree('remmina__main_8c_source.html','');});
gint remmina_file_get_int(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: remmina_file.c:603
void remmina_main_on_action_application_dark_theme(GSimpleAction *action, GVariant *param, gpointer data)
void remmina_about_open(GtkWindow *parent)
Definition: remmina_about.c:44
-
GtkWidget * remmina_file_editor_new(void)
+
GtkWidget * remmina_file_editor_new(void)
Definition: remmina_pref.h:55
Definition: remmina_exec.h:60
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
@@ -292,7 +292,7 @@ $(document).ready(function(){initNavTree('remmina__main_8c_source.html','');});
static GtkTargetEntry remmina_drop_types[]
Definition: remmina_main.c:126
GtkSearchBar * search_bar
Definition: remmina_main.h:67
static RemminaMain * remminamain
Definition: remmina_main.c:69
-
GtkWidget * remmina_file_editor_new_copy(const gchar *filename)
+
GtkWidget * remmina_file_editor_new_copy(const gchar *filename)
void remmina_main_on_action_application_quit(GSimpleAction *action, GVariant *param, gpointer data)
GtkEntry * entry_quick_connect_server
Definition: remmina_main.h:71
gint remmina_unlock_new(GtkWindow *parent)
diff --git a/public/vnc__plugin_8c.html b/public/vnc__plugin_8c.html index fcb2b2a09..86862a7b9 100644 --- a/public/vnc__plugin_8c.html +++ b/public/vnc__plugin_8c.html @@ -500,7 +500,7 @@ Variables
-

Definition at line 2214 of file vnc_plugin.c.

+

Definition at line 2213 of file vnc_plugin.c.

@@ -923,7 +923,7 @@ Variables
-

Definition at line 1960 of file vnc_plugin.c.

+

Definition at line 1959 of file vnc_plugin.c.

@@ -1230,7 +1230,7 @@ Variables
-

Definition at line 1926 of file vnc_plugin.c.

+

Definition at line 1925 of file vnc_plugin.c.

@@ -2535,7 +2535,7 @@ Variables
Initial value:
=
N_("Lock the aspect ratio when dynamic resolution is enabled:\n"
"\n"
" • The aspect ratio should be entered as a decimal number, e.g. 1.777\n"
" • 16:9 corresponds roughly to 1.7777, 4:3 corresponds roughly to 1.333\n"
" • The default value of 0 does not enforce any aspect ratio")
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2063 of file vnc_plugin.c.

+

Definition at line 2062 of file vnc_plugin.c.

@@ -2560,7 +2560,7 @@ Variables
Initial value:
=
{
"32", N_("True colour (32 bpp)"),
"16", N_("High colour (16 bpp)"),
"8", N_("256 colours (8 bpp)"),
NULL
}
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2028 of file vnc_plugin.c.

+

Definition at line 2027 of file vnc_plugin.c.

@@ -2657,7 +2657,7 @@ Variables
Initial value:
=
{
"2", N_("Good"),
"9", N_("Best (slowest)"),
"1", N_("Medium"),
"0", N_("Poor (fastest)"),
NULL
}
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2037 of file vnc_plugin.c.

+

Definition at line 2036 of file vnc_plugin.c.

@@ -2704,19 +2704,19 @@ Variables
-Initial value:
=
{
VNC_PLUGIN_NAME,
VNC_PLUGIN_DESCRIPTION,
GETTEXT_PACKAGE,
VNC_PLUGIN_VERSION,
VNC_PLUGIN_APPICON,
VNC_PLUGIN_SSH_APPICON,
}
static void remmina_plugin_vnc_init(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1960
-
static const RemminaProtocolFeature remmina_plugin_vnc_features[]
Definition: vnc_plugin.c:2146
+Initial value:
=
{
VNC_PLUGIN_NAME,
VNC_PLUGIN_DESCRIPTION,
GETTEXT_PACKAGE,
VNC_PLUGIN_VERSION,
VNC_PLUGIN_APPICON,
VNC_PLUGIN_SSH_APPICON,
}
static void remmina_plugin_vnc_init(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1959
+
static const RemminaProtocolFeature remmina_plugin_vnc_features[]
Definition: vnc_plugin.c:2145
static gboolean remmina_plugin_vnc_open_connection(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1681
-
static const RemminaProtocolSetting remmina_plugin_vnc_advanced_settings[]
Definition: vnc_plugin.c:2128
+
static const RemminaProtocolSetting remmina_plugin_vnc_advanced_settings[]
Definition: vnc_plugin.c:2127
static void remmina_plugin_vnc_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: vnc_plugin.c:1837
static gboolean remmina_plugin_vnc_close_connection(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1804
static gboolean remmina_plugin_vnc_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: vnc_plugin.c:1823
-
static const RemminaProtocolSetting remmina_plugin_vnc_basic_settings[]
Definition: vnc_plugin.c:2095
+
static const RemminaProtocolSetting remmina_plugin_vnc_basic_settings[]
Definition: vnc_plugin.c:2094
static void remmina_plugin_vnc_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
Definition: vnc_plugin.c:1896
-

Definition at line 2167 of file vnc_plugin.c.

+

Definition at line 2166 of file vnc_plugin.c.

@@ -2741,12 +2741,12 @@ Variables
Initial value:
=
{
{ REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "encodings", N_("Override pre-set VNC encodings"), FALSE, NULL, vncencodings_tooltip },
{ REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "aspect_ratio", N_("Dynamic resolution enforced aspec ratio"), FALSE, NULL, aspect_ratio_tooltip },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "tightencoding", N_("Force tight encoding"), TRUE, NULL, N_("Enabling this may help when the remote desktop looks scrambled") },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablesmoothscrolling", N_("Disable smooth scrolling"), FALSE, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"), TRUE, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableserverbell", N_("Ignore remote bell messages"), FALSE, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableserverinput", N_("Prevent local interaction on the server"), TRUE, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "showcursor", N_("Show remote cursor"), FALSE, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", N_("Turn off clipboard sync"), TRUE, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableencryption", N_("Turn off encryption"), FALSE, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "viewonly", N_("View only"), TRUE, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
}
-
static gchar vncencodings_tooltip[]
Definition: vnc_plugin.c:2070
+
static gchar vncencodings_tooltip[]
Definition: vnc_plugin.c:2069
-
static gchar aspect_ratio_tooltip[]
Definition: vnc_plugin.c:2063
+
static gchar aspect_ratio_tooltip[]
Definition: vnc_plugin.c:2062
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2128 of file vnc_plugin.c.

+

Definition at line 2127 of file vnc_plugin.c.

@@ -2770,17 +2770,17 @@ Variables
Initial value:
=
{
{ REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, "_rfb._tcp", NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "proxy", N_("Repeater"), FALSE, NULL, repeater_tooltip, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("User password"), FALSE, NULL, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "colordepth", N_("Colour depth"), FALSE, colordepth_list, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "quality", N_("Quality"), FALSE, quality_list, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_KEYMAP, "keymap", NULL, FALSE, NULL, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL }
}
-
static gchar repeater_tooltip[]
Definition: vnc_plugin.c:2046
+
static gchar repeater_tooltip[]
Definition: vnc_plugin.c:2045
-
static gpointer colordepth_list[]
Definition: vnc_plugin.c:2028
+
static gpointer colordepth_list[]
Definition: vnc_plugin.c:2027
-
static gpointer quality_list[]
Definition: vnc_plugin.c:2037
+
static gpointer quality_list[]
Definition: vnc_plugin.c:2036
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2095 of file vnc_plugin.c.

+

Definition at line 2094 of file vnc_plugin.c.

@@ -2804,16 +2804,16 @@ Variables
Initial value:
=
{
{ REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_QUALITY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_RADIO), "quality",
{ REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_COLOR, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_RADIO), "colordepth",
{ REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_VIEWONLY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "viewonly",
N_("View only") },
{ REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_DISABLESERVERINPUT, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "disableserverinput",N_("Prevent local interaction on the server") },
{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_VNC_FEATURE_TOOL_REFRESH, N_("Refresh"), NULL, NULL },
{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_VNC_FEATURE_TOOL_CHAT, N_("Open Chat…"), "face-smile", NULL },
{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_VNC_FEATURE_TOOL_SENDCTRLALTDEL, N_("Send Ctrl+Alt+Delete"), NULL, NULL },
{ REMMINA_PROTOCOL_FEATURE_TYPE_SCALE, REMMINA_PLUGIN_VNC_FEATURE_SCALE, NULL, NULL, NULL },
{ REMMINA_PROTOCOL_FEATURE_TYPE_UNFOCUS, REMMINA_PLUGIN_VNC_FEATURE_UNFOCUS, NULL, NULL, NULL },
#if LIBVNCSERVER_CHECK_VERSION_VERSION(0, 9, 14)
{ REMMINA_PROTOCOL_FEATURE_TYPE_DYNRESUPDATE, REMMINA_PLUGIN_VNC_FEATURE_DYNRESUPDATE, NULL, NULL, NULL },
#endif
{ REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
}
-
static gpointer colordepth_list[]
Definition: vnc_plugin.c:2028
+
static gpointer colordepth_list[]
Definition: vnc_plugin.c:2027
-
static gpointer quality_list[]
Definition: vnc_plugin.c:2037
+
static gpointer quality_list[]
Definition: vnc_plugin.c:2036
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2146 of file vnc_plugin.c.

+

Definition at line 2145 of file vnc_plugin.c.

@@ -2860,19 +2860,19 @@ Variables
-Initial value:
=
{
VNCI_PLUGIN_NAME,
VNCI_PLUGIN_DESCRIPTION,
GETTEXT_PACKAGE,
VERSION,
VNCI_PLUGIN_APPICON,
VNCI_PLUGIN_SSH_APPICON,
NULL,
NULL,
NULL
}
static void remmina_plugin_vnc_init(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1960
-
static const RemminaProtocolFeature remmina_plugin_vnc_features[]
Definition: vnc_plugin.c:2146
+Initial value:
=
{
VNCI_PLUGIN_NAME,
VNCI_PLUGIN_DESCRIPTION,
GETTEXT_PACKAGE,
VERSION,
VNCI_PLUGIN_APPICON,
VNCI_PLUGIN_SSH_APPICON,
NULL,
NULL,
NULL
}
static void remmina_plugin_vnc_init(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1959
+
static const RemminaProtocolFeature remmina_plugin_vnc_features[]
Definition: vnc_plugin.c:2145
static gboolean remmina_plugin_vnc_open_connection(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1681
-
static const RemminaProtocolSetting remmina_plugin_vnc_advanced_settings[]
Definition: vnc_plugin.c:2128
+
static const RemminaProtocolSetting remmina_plugin_vnc_advanced_settings[]
Definition: vnc_plugin.c:2127
static void remmina_plugin_vnc_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: vnc_plugin.c:1837
static gboolean remmina_plugin_vnc_close_connection(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1804
static gboolean remmina_plugin_vnc_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: vnc_plugin.c:1823
-
static const RemminaProtocolSetting remmina_plugin_vnci_basic_settings[]
Definition: vnc_plugin.c:2108
+
static const RemminaProtocolSetting remmina_plugin_vnci_basic_settings[]
Definition: vnc_plugin.c:2107
static void remmina_plugin_vnc_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
Definition: vnc_plugin.c:1896
-

Definition at line 2189 of file vnc_plugin.c.

+

Definition at line 2188 of file vnc_plugin.c.

@@ -2896,16 +2896,16 @@ Variables
Initial value:
=
{
{ REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "listenport", N_("Listen on port"), FALSE, NULL, vnciport_tooltip, NULL, NULL},
{ REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL, NULL, NULL},
{ REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("User password"), FALSE, NULL, NULL, NULL, NULL},
{ REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "colordepth", N_("Colour depth"), FALSE, colordepth_list, NULL, NULL, NULL},
{ REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "quality", N_("Quality"), FALSE, quality_list, NULL, NULL, NULL},
{ REMMINA_PROTOCOL_SETTING_TYPE_KEYMAP, "keymap", NULL, FALSE, NULL, NULL, NULL, NULL},
{ REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL}
}
-
static gpointer colordepth_list[]
Definition: vnc_plugin.c:2028
+
static gpointer colordepth_list[]
Definition: vnc_plugin.c:2027
-
static gchar vnciport_tooltip[]
Definition: vnc_plugin.c:2055
+
static gchar vnciport_tooltip[]
Definition: vnc_plugin.c:2054
-
static gpointer quality_list[]
Definition: vnc_plugin.c:2037
+
static gpointer quality_list[]
Definition: vnc_plugin.c:2036
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2108 of file vnc_plugin.c.

+

Definition at line 2107 of file vnc_plugin.c.

@@ -2930,7 +2930,7 @@ Variables
Initial value:
=
N_("Connect to VNC using a repeater:\n"
" • The server field must contain the repeater ID, e.g. ID:123456789\n"
" • The repeater field have to be set to the repeater IP and port, like:\n"
" 10.10.10.12:5901\n"
" • From the remote VNC server, you will connect to\n"
" the repeater, e.g. with x11vnc:\n"
" x11vnc -connect repeater=ID:123456789+10.10.10.12:5500")
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2046 of file vnc_plugin.c.

+

Definition at line 2045 of file vnc_plugin.c.

@@ -2955,7 +2955,7 @@ Variables
Initial value:
=
N_("Overriding the pre-set VNC encoding quality:\n"
"\n"
" • “Poor (fastest)” sets encoding to “copyrect zlib hextile raw”\n"
" • “Medium” sets encoding to “tight zrle ultra copyrect hextile zlib corre rre raw”\n"
" • “Good” sets encoding to “tight zrle ultra copyrect hextile zlib corre rre raw”\n"
" • “Best (slowest)” sets encoding to “copyrect zrle ultra zlib hextile corre rre raw”")
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2070 of file vnc_plugin.c.

+

Definition at line 2069 of file vnc_plugin.c.

@@ -2980,7 +2980,7 @@ Variables
Initial value:
=
N_("Listening for remote VNC connection:\n"
" • The “Listen on port” field is the port Remmina will listen to,\n"
" e.g. 8888\n"
" • From the remote VNC server, you will connect to\n"
" Remmina, e.g. with x11vnc:\n"
" x11vnc -display :0 -connect 192.168.1.36:8888")
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-

Definition at line 2055 of file vnc_plugin.c.

+

Definition at line 2054 of file vnc_plugin.c.

diff --git a/public/vnc__plugin_8c_source.html b/public/vnc__plugin_8c_source.html index a96cf6119..dbaabde65 100644 --- a/public/vnc__plugin_8c_source.html +++ b/public/vnc__plugin_8c_source.html @@ -86,7 +86,7 @@ $(document).ready(function(){initNavTree('vnc__plugin_8c_source.html','');});
vnc_plugin.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-2022 Antenore Gatta, Giovanni Panozzo
6  * Copyright (C) 2022-2023 Antenore Gatta, Giovanni Panozzo, Hiroyuki Tanaka
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  * In addition, as a special exception, the copyright holders give
24  * permission to link the code of portions of this program with the
25  * OpenSSL library under certain conditions as described in each
26  * individual source file, and distribute linked combinations
27  * including the two.
28  * You must obey the GNU General Public License in all respects
29  * for all of the code used other than OpenSSL. * If you modify
30  * file(s) with this exception, you may extend this exception to your
31  * version of the file(s), but you are not obligated to do so. * If you
32  * do not wish to do so, delete this exception statement from your
33  * version. * If you delete this exception statement from all source
34  * files in the program, then also delete it here.
35  *
36  */
37 
38 #include <gmodule.h>
39 #include "vnc_plugin.h"
40 #include <rfb/rfbclient.h>
41 
42 #define REMMINA_PLUGIN_VNC_FEATURE_PREF_QUALITY 1
43 #define REMMINA_PLUGIN_VNC_FEATURE_PREF_VIEWONLY 2
44 #define REMMINA_PLUGIN_VNC_FEATURE_PREF_DISABLESERVERINPUT 3
45 #define REMMINA_PLUGIN_VNC_FEATURE_TOOL_REFRESH 4
46 #define REMMINA_PLUGIN_VNC_FEATURE_TOOL_CHAT 5
47 #define REMMINA_PLUGIN_VNC_FEATURE_SCALE 6
48 #define REMMINA_PLUGIN_VNC_FEATURE_UNFOCUS 7
49 #define REMMINA_PLUGIN_VNC_FEATURE_TOOL_SENDCTRLALTDEL 8
50 #define REMMINA_PLUGIN_VNC_FEATURE_PREF_COLOR 9
51 #define REMMINA_PLUGIN_VNC_FEATURE_DYNRESUPDATE 10
52 
53 #define VNC_DEFAULT_PORT 5900
54 
55 #define GET_PLUGIN_DATA(gp) (RemminaPluginVncData *)g_object_get_data(G_OBJECT(gp), "plugin-data")
56 
58 
59 static int dot_cursor_x_hot = 2;
60 static int dot_cursor_y_hot = 2;
61 static const gchar *dot_cursor_xpm[] =
62 { "5 5 3 1", " c None", ". c #000000", "+ c #FFFFFF", " ... ", ".+++.", ".+ +.", ".+++.", " ... " };
63 
64 
65 #define LOCK_BUFFER(t) if (t) { CANCEL_DEFER } pthread_mutex_lock(&gpdata->buffer_mutex);
66 #define UNLOCK_BUFFER(t) pthread_mutex_unlock(&gpdata->buffer_mutex); if (t) { CANCEL_ASYNC }
67 
70  GtkWidget * widget;
71  gint x, y, width, height;
73  gboolean scale;
74 
75  /* Mutex for thread synchronization */
76  pthread_mutex_t mu;
77  /* Flag to catch cancellations */
78  gboolean cancelled;
79 };
80 
81 static gboolean onMainThread_cb(struct onMainThread_cb_data *d)
82 {
83  TRACE_CALL(__func__);
84  if (!d->cancelled) {
85  switch (d->func) {
86  case FUNC_UPDATE_SCALE:
88  break;
89  }
90  pthread_mutex_unlock(&d->mu);
91  } else {
92  /* thread has been cancelled, so we must free d memory here */
93  g_free(d);
94  }
95  return G_SOURCE_REMOVE;
96 }
97 
98 
99 static void onMainThread_cleanup_handler(gpointer data)
100 {
101  TRACE_CALL(__func__);
102  struct onMainThread_cb_data *d = data;
103 
104  d->cancelled = TRUE;
105 }
106 
107 
109 {
110  TRACE_CALL(__func__);
111  d->cancelled = FALSE;
112  pthread_cleanup_push(onMainThread_cleanup_handler, d);
113  pthread_mutex_init(&d->mu, NULL);
114  pthread_mutex_lock(&d->mu);
115  gdk_threads_add_idle((GSourceFunc)onMainThread_cb, (gpointer)d);
116 
117  pthread_mutex_lock(&d->mu);
118 
119  pthread_cleanup_pop(0);
120  pthread_mutex_unlock(&d->mu);
121  pthread_mutex_destroy(&d->mu);
122 }
123 
128 static gboolean check_for_endianness()
129 {
130  unsigned int x = 1;
131  char *c = (char *)&x;
132 
133  return (int)*c;
134 }
135 
136 static void remmina_plugin_vnc_event_push(RemminaProtocolWidget *gp, gint event_type, gpointer p1, gpointer p2, gpointer p3)
137 {
138  TRACE_CALL(__func__);
139  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
140  RemminaPluginVncEvent *event;
141 
142  event = g_new(RemminaPluginVncEvent, 1);
143  event->event_type = event_type;
144  switch (event_type) {
146  event->event_data.key.keyval = GPOINTER_TO_UINT(p1);
147  event->event_data.key.pressed = GPOINTER_TO_INT(p2);
148  break;
150  event->event_data.pointer.x = GPOINTER_TO_INT(p1);
151  event->event_data.pointer.y = GPOINTER_TO_INT(p2);
152  event->event_data.pointer.button_mask = GPOINTER_TO_INT(p3);
153  break;
156  event->event_data.text.text = g_strdup((char *)p1);
157  break;
158  default:
159  break;
160  }
161 
162  pthread_mutex_lock(&gpdata->vnc_event_queue_mutex);
163  g_queue_push_tail(gpdata->vnc_event_queue, event);
164  pthread_mutex_unlock(&gpdata->vnc_event_queue_mutex);
165 
166  if (write(gpdata->vnc_event_pipe[1], "\0", 1)) {
167  /* Ignore */
168  }
169 }
170 
172 {
173  TRACE_CALL(__func__);
174  switch (event->event_type) {
177  g_free(event->event_data.text.text);
178  break;
179  default:
180  break;
181  }
182  g_free(event);
183 }
184 
186 {
187  TRACE_CALL(__func__);
188  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
189  RemminaPluginVncEvent *event;
190 
191  /* This is called from main thread after plugin thread has
192  * been closed, so no queue locking is necessary here */
193  while ((event = g_queue_pop_head(gpdata->vnc_event_queue)) != NULL)
195 }
196 
197 static void remmina_plugin_vnc_scale_area(RemminaProtocolWidget *gp, gint *x, gint *y, gint *w, gint *h)
198 {
199  TRACE_CALL(__func__);
200  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
201  GtkAllocation widget_allocation;
202  gint width, height;
203  gint sx, sy, sw, sh;
204 
205  if (gpdata->rgb_buffer == NULL)
206  return;
207 
208  width = remmina_plugin_service->protocol_plugin_get_width(gp);
209  height = remmina_plugin_service->protocol_plugin_get_height(gp);
210 
211  gtk_widget_get_allocation(GTK_WIDGET(gp), &widget_allocation);
212 
213  if (widget_allocation.width == width && widget_allocation.height == height)
214  return; /* Same size, no scaling */
215 
216  /* We have to extend the scaled region 2 scaled pixels, to avoid gaps */
217  sx = MIN(MAX(0, (*x) * widget_allocation.width / width - widget_allocation.width / width - 2), widget_allocation.width - 1);
218  sy = MIN(MAX(0, (*y) * widget_allocation.height / height - widget_allocation.height / height - 2), widget_allocation.height - 1);
219  sw = MIN(widget_allocation.width - sx, (*w) * widget_allocation.width / width + widget_allocation.width / width + 4);
220  sh = MIN(widget_allocation.height - sy, (*h) * widget_allocation.height / height + widget_allocation.height / height + 4);
221 
222  *x = sx;
223  *y = sy;
224  *w = sw;
225  *h = sh;
226 }
227 
229 {
230  TRACE_CALL(__func__);
231  /* This function can be called from a non main thread */
232 
233  RemminaPluginVncData *gpdata;
234  gint width, height;
235 
236  if (!remmina_plugin_service->is_main_thread()) {
237  struct onMainThread_cb_data *d;
238  d = (struct onMainThread_cb_data *)g_malloc(sizeof(struct onMainThread_cb_data));
239  d->func = FUNC_UPDATE_SCALE;
240  d->gp = gp;
241  d->scale = scale;
243  g_free(d);
244  return;
245  }
246 
247  gpdata = GET_PLUGIN_DATA(gp);
248 
249  width = remmina_plugin_service->protocol_plugin_get_width(gp);
250  height = remmina_plugin_service->protocol_plugin_get_height(gp);
251  if (scale)
252  /* In scaled mode, drawing_area will get its dimensions from its parent */
253  gtk_widget_set_size_request(GTK_WIDGET(gpdata->drawing_area), -1, -1);
254  else
255  /* In non scaled mode, the plugins forces dimensions of drawing area */
256  gtk_widget_set_size_request(GTK_WIDGET(gpdata->drawing_area), width, height);
257 
258  remmina_plugin_service->protocol_plugin_update_align(gp);
259 }
260 
262 {
263  TRACE_CALL(__func__);
264  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
265  GdkCursor *cur;
266 
267  LOCK_BUFFER(FALSE);
268  gpdata->queuecursor_handler = 0;
269 
270  if (gpdata->queuecursor_surface) {
271  cur = gdk_cursor_new_from_surface(gdk_display_get_default(), gpdata->queuecursor_surface, gpdata->queuecursor_x,
272  gpdata->queuecursor_y);
273  gdk_window_set_cursor(gtk_widget_get_window(gpdata->drawing_area), cur);
274  g_object_unref(cur);
275  cairo_surface_destroy(gpdata->queuecursor_surface);
276  gpdata->queuecursor_surface = NULL;
277  } else {
278  gdk_window_set_cursor(gtk_widget_get_window(gpdata->drawing_area), NULL);
279  }
280  UNLOCK_BUFFER(FALSE);
281 
282  return FALSE;
283 }
284 
285 static void remmina_plugin_vnc_queuecursor(RemminaProtocolWidget *gp, cairo_surface_t *surface, gint x, gint y)
286 {
287  TRACE_CALL(__func__);
288  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
289 
290  if (gpdata->queuecursor_surface)
291  cairo_surface_destroy(gpdata->queuecursor_surface);
292  gpdata->queuecursor_surface = surface;
293  gpdata->queuecursor_x = x;
294  gpdata->queuecursor_y = y;
295  if (!gpdata->queuecursor_handler)
296  gpdata->queuecursor_handler = IDLE_ADD((GSourceFunc)remmina_plugin_vnc_setcursor, gp);
297 }
298 
299 typedef struct _RemminaKeyVal {
300  guint keyval;
301  guint16 keycode;
302 } RemminaKeyVal;
303 
304 /***************************** LibVNCClient related codes *********************************/
305 
307 { rfbNoAuth, rfbVncAuth, rfbMSLogon, 0 };
308 
310 {
311  RemminaPluginVncEvent *event;
312 
313  CANCEL_DEFER;
314  pthread_mutex_lock(&gpdata->vnc_event_queue_mutex);
315 
316  event = g_queue_pop_head(gpdata->vnc_event_queue);
317 
318  pthread_mutex_unlock(&gpdata->vnc_event_queue_mutex);
319  CANCEL_ASYNC;
320 
321  return event;
322 }
323 
325 {
326  TRACE_CALL(__func__);
327  RemminaPluginVncEvent *event;
328  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
329  rfbClient *cl;
330  gchar buf[100];
331 
332  cl = (rfbClient *)gpdata->client;
333  while ((event = remmina_plugin_vnc_event_queue_pop_head(gpdata)) != NULL) {
334  if (cl) {
335  switch (event->event_type) {
337  SendKeyEvent(cl, event->event_data.key.keyval, event->event_data.key.pressed);
338  break;
340  SendPointerEvent(cl, event->event_data.pointer.x, event->event_data.pointer.y,
341  event->event_data.pointer.button_mask);
342  break;
344  if (event->event_data.text.text) {
345  rfbClientLog("sending clipboard text '%s'\n", event->event_data.text.text);
346  SendClientCutText(cl, event->event_data.text.text, strlen(event->event_data.text.text));
347  }
348  break;
350  TextChatOpen(cl);
351  break;
353  TextChatSend(cl, event->event_data.text.text);
354  break;
356  TextChatClose(cl);
357  TextChatFinish(cl);
358  break;
359  default:
360  rfbClientLog("Ignoring VNC event: 0x%x\n", event->event_type);
361  break;
362  }
363  }
365  }
366  if (read(gpdata->vnc_event_pipe[0], buf, sizeof(buf))) {
367  /* Ignore */
368  }
369 }
370 
373  gchar * text;
374  gint textlen;
376 
377 static void remmina_plugin_vnc_update_quality(rfbClient *cl, gint quality)
378 {
379  TRACE_CALL(__func__);
380  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
381  RemminaFile *remminafile;
382  gchar *enc = NULL;
383 
390  switch (quality) {
391  case 9:
392  cl->appData.useBGR233 = 0;
393  cl->appData.encodingsString = "copyrect zlib hextile raw";
394  cl->appData.compressLevel = 1;
395  cl->appData.qualityLevel = 9;
396  break;
397  case 2:
398  cl->appData.useBGR233 = 0;
399  cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
400  cl->appData.compressLevel = 2;
401  cl->appData.qualityLevel = 7;
402  break;
403  case 1:
404  cl->appData.useBGR233 = 0;
405  cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
406  cl->appData.compressLevel = 3;
407  cl->appData.qualityLevel = 5;
408  break;
409  case 0:
410  default:
411  // bpp8 and tight encoding is not supported in libvnc
412  cl->appData.useBGR233 = 1;
413  cl->appData.encodingsString = "copyrect zrle ultra zlib hextile corre rre raw";
414  cl->appData.qualityLevel = 1;
415  break;
416  }
417  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
418  enc = g_strdup(remmina_plugin_service->file_get_string(remminafile, "encodings"));
419  if (enc) {
420  cl->appData.encodingsString = g_strdup(enc);
421  g_free (enc), enc = NULL;
422  }
423  gboolean tight = remmina_plugin_service->file_get_int (remminafile, "tightencoding", FALSE);
424  if (tight) {
425  if (!g_strrstr ( g_strdup(cl->appData.encodingsString), "tight\0")) {
426  cl->appData.encodingsString = g_strdup_printf("%s %s", "tight", g_strdup(cl->appData.encodingsString));
427  }
428  }
429 
430  REMMINA_PLUGIN_DEBUG("Quality: %d", quality);
431  REMMINA_PLUGIN_DEBUG("Encodings: %s", cl->appData.encodingsString);
432 }
433 
434 static void remmina_plugin_vnc_update_colordepth(rfbClient *cl, gint colordepth)
435 {
436  TRACE_CALL(__func__);
437 
438  cl->format.depth = colordepth;
439  cl->appData.requestedDepth = colordepth;
440 
441  cl->format.trueColour = 1;
442  cl->format.bigEndian = check_for_endianness()?FALSE:TRUE;
443 
444  switch (colordepth) {
445  case 8:
446  cl->format.depth = 8;
447  cl->format.bitsPerPixel = 8;
448  cl->format.blueMax = 3;
449  cl->format.blueShift = 6;
450  cl->format.greenMax = 7;
451  cl->format.greenShift = 3;
452  cl->format.redMax = 7;
453  cl->format.redShift = 0;
454  break;
455  case 16:
456  cl->format.depth = 15;
457  cl->format.bitsPerPixel = 16;
458  cl->format.redShift = 11;
459  cl->format.greenShift = 6;
460  cl->format.blueShift = 1;
461  cl->format.redMax = 31;
462  cl->format.greenMax = 31;
463  cl->format.blueMax = 31;
464  break;
465  case 32:
466  default:
467  cl->format.depth = 24;
468  cl->format.bitsPerPixel = 32;
469  cl->format.blueShift = 0;
470  cl->format.redShift = 16;
471  cl->format.greenShift = 8;
472  cl->format.blueMax = 0xff;
473  cl->format.redMax = 0xff;
474  cl->format.greenMax = 0xff;
475  break;
476  }
477 
478  rfbClientLog("colordepth = %d\n", colordepth);
479  rfbClientLog("format.depth = %d\n", cl->format.depth);
480  rfbClientLog("format.bitsPerPixel = %d\n", cl->format.bitsPerPixel);
481  rfbClientLog("format.blueShift = %d\n", cl->format.blueShift);
482  rfbClientLog("format.redShift = %d\n", cl->format.redShift);
483  rfbClientLog("format.trueColour = %d\n", cl->format.trueColour);
484  rfbClientLog("format.greenShift = %d\n", cl->format.greenShift);
485  rfbClientLog("format.blueMax = %d\n", cl->format.blueMax);
486  rfbClientLog("format.redMax = %d\n", cl->format.redMax);
487  rfbClientLog("format.greenMax = %d\n", cl->format.greenMax);
488  rfbClientLog("format.bigEndian = %d\n", cl->format.bigEndian);
489 }
490 
491 static rfbBool remmina_plugin_vnc_rfb_allocfb(rfbClient *cl)
492 {
493  TRACE_CALL(__func__);
494  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
495  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
496  gint width, height, depth, size;
497  gboolean scale;
498  cairo_surface_t *new_surface, *old_surface;
499 
500  width = cl->width;
501  height = cl->height;
502  depth = cl->format.bitsPerPixel;
503  size = width * height * (depth / 8);
504 
505  new_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
506  if (cairo_surface_status(new_surface) != CAIRO_STATUS_SUCCESS)
507  return FALSE;
508  old_surface = gpdata->rgb_buffer;
509 
510  LOCK_BUFFER(TRUE);
511 
512  remmina_plugin_service->protocol_plugin_set_width(gp, width);
513  remmina_plugin_service->protocol_plugin_set_height(gp, height);
514 
515  gpdata->rgb_buffer = new_surface;
516 
517  if (gpdata->vnc_buffer)
518  g_free(gpdata->vnc_buffer);
519  gpdata->vnc_buffer = (guchar *)g_malloc(size);
520  cl->frameBuffer = gpdata->vnc_buffer;
521 
522  UNLOCK_BUFFER(TRUE);
523 
524  if (old_surface)
525  cairo_surface_destroy(old_surface);
526 
529 
530  /* Notify window of change so that scroll border can be hidden or shown if needed */
531  remmina_plugin_service->protocol_plugin_desktop_resize(gp);
532 
533  /* Refresh the client’s updateRect - bug in xvncclient */
534  cl->updateRect.w = width;
535  cl->updateRect.h = height;
536 
537  return TRUE;
538 }
539 
540 static gint remmina_plugin_vnc_bits(gint n)
541 {
542  TRACE_CALL(__func__);
543  gint b = 0;
544 
545  while (n) {
546  b++;
547  n >>= 1;
548  }
549  return b ? b : 1;
550 }
551 
553 {
554  TRACE_CALL(__func__);
555  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
556  gint x, y, w, h;
557 
558  if (GTK_IS_WIDGET(gp) && gpdata->connected) {
559  LOCK_BUFFER(FALSE);
560  x = gpdata->queuedraw_x;
561  y = gpdata->queuedraw_y;
562  w = gpdata->queuedraw_w;
563  h = gpdata->queuedraw_h;
564  gpdata->queuedraw_handler = 0;
565  UNLOCK_BUFFER(FALSE);
566 
567  gtk_widget_queue_draw_area(GTK_WIDGET(gp), x, y, w, h);
568  }
569  return FALSE;
570 }
571 
572 static void remmina_plugin_vnc_queue_draw_area(RemminaProtocolWidget *gp, gint x, gint y, gint w, gint h)
573 {
574  TRACE_CALL(__func__);
575  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
576  gint nx2, ny2, ox2, oy2;
577 
578  LOCK_BUFFER(TRUE);
579  if (gpdata->queuedraw_handler) {
580  nx2 = x + w;
581  ny2 = y + h;
582  ox2 = gpdata->queuedraw_x + gpdata->queuedraw_w;
583  oy2 = gpdata->queuedraw_y + gpdata->queuedraw_h;
584  gpdata->queuedraw_x = MIN(gpdata->queuedraw_x, x);
585  gpdata->queuedraw_y = MIN(gpdata->queuedraw_y, y);
586  gpdata->queuedraw_w = MAX(ox2, nx2) - gpdata->queuedraw_x;
587  gpdata->queuedraw_h = MAX(oy2, ny2) - gpdata->queuedraw_y;
588  } else {
589  gpdata->queuedraw_x = x;
590  gpdata->queuedraw_y = y;
591  gpdata->queuedraw_w = w;
592  gpdata->queuedraw_h = h;
593  gpdata->queuedraw_handler = IDLE_ADD((GSourceFunc)remmina_plugin_vnc_queue_draw_area_real, gp);
594  }
595  UNLOCK_BUFFER(TRUE);
596 }
597 
598 static void remmina_plugin_vnc_rfb_fill_buffer(rfbClient *cl, guchar *dest, gint dest_rowstride, guchar *src,
599  gint src_rowstride, guchar *mask, gint w, gint h)
600 {
601  TRACE_CALL(__func__);
602  guchar *srcptr;
603  gint bytesPerPixel;
604  guint32 src_pixel;
605  gint ix, iy;
606  gint i;
607  guchar c;
608  gint rs, gs, bs, rm, gm, bm, rl, gl, bl, rr, gr, br;
609  gint r;
610  guint32 *destptr;
611 
612  union {
613  struct {
614  guchar a, r, g, b;
615  } colors;
616  guint32 argb;
617  } dst_pixel;
618 
619  bytesPerPixel = cl->format.bitsPerPixel / 8;
620  switch (cl->format.bitsPerPixel) {
621  case 32:
622  /* The following codes fill in the Alpha channel swap red/green value */
623  for (iy = 0; iy < h; iy++) {
624  destptr = (guint32 *)(dest + iy * dest_rowstride);
625  srcptr = src + iy * src_rowstride;
626  for (ix = 0; ix < w; ix++) {
627  if (!mask || *mask++) {
628  dst_pixel.colors.a = 0xff;
629  dst_pixel.colors.r = *(srcptr + 2);
630  dst_pixel.colors.g = *(srcptr + 1);
631  dst_pixel.colors.b = *srcptr;
632  *destptr++ = ntohl(dst_pixel.argb);
633  } else {
634  *destptr++ = 0;
635  }
636  srcptr += 4;
637  }
638  }
639  break;
640  default:
641  rm = cl->format.redMax;
642  gm = cl->format.greenMax;
643  bm = cl->format.blueMax;
644  rr = remmina_plugin_vnc_bits(rm);
645  gr = remmina_plugin_vnc_bits(gm);
646  br = remmina_plugin_vnc_bits(bm);
647  rl = 8 - rr;
648  gl = 8 - gr;
649  bl = 8 - br;
650  rs = cl->format.redShift;
651  gs = cl->format.greenShift;
652  bs = cl->format.blueShift;
653  for (iy = 0; iy < h; iy++) {
654  destptr = (guint32 *)(dest + iy * dest_rowstride);
655  srcptr = src + iy * src_rowstride;
656  for (ix = 0; ix < w; ix++) {
657  src_pixel = 0;
658  for (i = 0; i < bytesPerPixel; i++)
659  src_pixel += (*srcptr++) << (8 * i);
660 
661  if (!mask || *mask++) {
662  dst_pixel.colors.a = 0xff;
663  c = (guchar)((src_pixel >> rs) & rm) << rl;
664  for (r = rr; r < 8; r *= 2)
665  c |= c >> r;
666  dst_pixel.colors.r = c;
667  c = (guchar)((src_pixel >> gs) & gm) << gl;
668  for (r = gr; r < 8; r *= 2)
669  c |= c >> r;
670  dst_pixel.colors.g = c;
671  c = (guchar)((src_pixel >> bs) & bm) << bl;
672  for (r = br; r < 8; r *= 2)
673  c |= c >> r;
674  dst_pixel.colors.b = c;
675  *destptr++ = ntohl(dst_pixel.argb);
676  } else {
677  *destptr++ = 0;
678  }
679  }
680  }
681  break;
682  }
683 }
684 
685 static void remmina_plugin_vnc_rfb_updatefb(rfbClient *cl, int x, int y, int w, int h)
686 {
687  TRACE_CALL(__func__);
688  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
689  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
690  gint bytesPerPixel;
691  gint rowstride;
692  gint width;
693 
694  LOCK_BUFFER(TRUE);
695 
696  if (w >= 1 || h >= 1) {
697  width = remmina_plugin_service->protocol_plugin_get_width(gp);
698  bytesPerPixel = cl->format.bitsPerPixel / 8;
699  rowstride = cairo_image_surface_get_stride(gpdata->rgb_buffer);
700  cairo_surface_flush(gpdata->rgb_buffer);
701  remmina_plugin_vnc_rfb_fill_buffer(cl, cairo_image_surface_get_data(gpdata->rgb_buffer) + y * rowstride + x * 4,
702  rowstride, gpdata->vnc_buffer + ((y * width + x) * bytesPerPixel), width * bytesPerPixel, NULL,
703  w, h);
704  cairo_surface_mark_dirty(gpdata->rgb_buffer);
705  }
706 
708  remmina_plugin_vnc_scale_area(gp, &x, &y, &w, &h);
709 
710  UNLOCK_BUFFER(TRUE);
711 
712  remmina_plugin_vnc_queue_draw_area(gp, x, y, w, h);
713 }
714 
715 static void remmina_plugin_vnc_rfb_finished(rfbClient *cl) __attribute__ ((unused));
716 static void remmina_plugin_vnc_rfb_finished(rfbClient *cl)
717 {
718  TRACE_CALL(__func__);
719  REMMINA_PLUGIN_DEBUG("FinishedFrameBufferUpdate");
720 }
721 
722 static void remmina_plugin_vnc_rfb_led_state(rfbClient *cl, int value, int pad)
723 {
724  TRACE_CALL(__func__);
725  REMMINA_PLUGIN_DEBUG("Led state - value: %d, pad: %d", value, pad);
726 }
727 
729 {
730  TRACE_CALL(__func__);
731  RemminaProtocolWidget *gp = param->gp;
732  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
733  GDateTime *t;
734  glong diff;
735  const char *cur_charset;
736  gchar *text;
737  gsize br, bw;
738 
739  if (GTK_IS_WIDGET(gp) && gpdata->connected) {
740  t = g_date_time_new_now_utc();
741  diff = g_date_time_difference(t, gpdata->clipboard_timer) / 100000; // tenth of second
742  if (diff >= 10) {
743  g_date_time_unref(gpdata->clipboard_timer);
744  gpdata->clipboard_timer = t;
745  /* Convert text from VNC latin-1 to current GTK charset (usually UTF-8) */
746  g_get_charset(&cur_charset);
747  text = g_convert_with_fallback(param->text, param->textlen, cur_charset, "ISO-8859-1", "?", &br, &bw, NULL);
748  gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), text, bw);
749  g_free(text);
750  } else {
751  g_date_time_unref(t);
752  }
753  }
754  g_free(param->text);
755  g_free(param);
756  return FALSE;
757 }
758 
759 static void remmina_plugin_vnc_rfb_cuttext(rfbClient *cl, const char *text, int textlen)
760 {
761  TRACE_CALL(__func__);
763 
764  param = g_new(RemminaPluginVncCuttextParam, 1);
765  param->gp = (RemminaProtocolWidget *)rfbClientGetClientData(cl, NULL);
766  param->text = g_malloc(textlen);
767  memcpy(param->text, text, textlen);
768  param->textlen = textlen;
769  IDLE_ADD((GSourceFunc)remmina_plugin_vnc_queue_cuttext, param);
770 }
771 
772 static char *
774 {
775  TRACE_CALL(__func__);
776  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
777  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
778  RemminaFile *remminafile;
779  gchar *pwd = NULL;
780 
781  gpdata->auth_called = TRUE;
782  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
783 
784  if (gpdata->auth_first)
785  pwd = g_strdup(remmina_plugin_service->file_get_string(remminafile, "password"));
786  if (!pwd) {
787  gboolean save;
788  gint ret;
789  gboolean disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
790  ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
791  (disablepasswordstoring ? 0 : REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD),
792  _("Enter VNC password"),
793  NULL,
794  remmina_plugin_service->file_get_string(remminafile, "password"),
795  NULL,
796  NULL);
797  if (ret != GTK_RESPONSE_OK) {
798  gpdata->connected = FALSE;
799  return NULL;
800  }
801  pwd = remmina_plugin_service->protocol_plugin_init_get_password(gp);
802  save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
803  if (save)
804  remmina_plugin_service->file_set_string(remminafile, "password", pwd);
805  else
806  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
807  }
808  return pwd;
809 }
810 
811 static rfbCredential *
812 remmina_plugin_vnc_rfb_credential(rfbClient *cl, int credentialType)
813 {
814  TRACE_CALL(__func__);
815  rfbCredential *cred;
816  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
817  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
818  RemminaFile *remminafile;
819  gint ret;
820  gchar *s1, *s2;
821  gboolean disablepasswordstoring;
822 
823  gpdata->auth_called = TRUE;
824  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
825 
826  cred = g_new0(rfbCredential, 1);
827 
828  switch (credentialType) {
829  case rfbCredentialTypeUser:
830 
831  s1 = g_strdup(remmina_plugin_service->file_get_string(remminafile, "username"));
832 
833  s2 = g_strdup(remmina_plugin_service->file_get_string(remminafile, "password"));
834 
835  if (gpdata->auth_first && s1 && s2) {
836  cred->userCredential.username = s1;
837  cred->userCredential.password = s2;
838  } else {
839  g_free(s1);
840  g_free(s2);
841 
842  disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
843  ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
845  _("Enter VNC authentication credentials"),
846  remmina_plugin_service->file_get_string(remminafile, "username"),
847  remmina_plugin_service->file_get_string(remminafile, "password"),
848  NULL,
849  NULL);
850  if (ret == GTK_RESPONSE_OK) {
851  gboolean save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
852  cred->userCredential.username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
853  cred->userCredential.password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
854  if (save) {
855  remmina_plugin_service->file_set_string(remminafile, "username", cred->userCredential.username);
856  remmina_plugin_service->file_set_string(remminafile, "password", cred->userCredential.password);
857  } else {
858  remmina_plugin_service->file_set_string(remminafile, "username", NULL);
859  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
860  }
861  } else {
862  g_free(cred);
863  cred = NULL;
864  gpdata->connected = FALSE;
865  }
866  }
867  break;
868 
869  case rfbCredentialTypeX509:
870  if (gpdata->auth_first &&
871  remmina_plugin_service->file_get_string(remminafile, "cacert")) {
872  cred->x509Credential.x509CACertFile = g_strdup(remmina_plugin_service->file_get_string(remminafile, "cacert"));
873  cred->x509Credential.x509CACrlFile = g_strdup(remmina_plugin_service->file_get_string(remminafile, "cacrl"));
874  cred->x509Credential.x509ClientCertFile = g_strdup(remmina_plugin_service->file_get_string(remminafile, "clientcert"));
875  cred->x509Credential.x509ClientKeyFile = g_strdup(remmina_plugin_service->file_get_string(remminafile, "clientkey"));
876  } else {
877  ret = remmina_plugin_service->protocol_plugin_init_authx509(gp);
878 
879  if (ret == GTK_RESPONSE_OK) {
880  cred->x509Credential.x509CACertFile = remmina_plugin_service->protocol_plugin_init_get_cacert(gp);
881  cred->x509Credential.x509CACrlFile = remmina_plugin_service->protocol_plugin_init_get_cacrl(gp);
882  cred->x509Credential.x509ClientCertFile = remmina_plugin_service->protocol_plugin_init_get_clientcert(gp);
883  cred->x509Credential.x509ClientKeyFile = remmina_plugin_service->protocol_plugin_init_get_clientkey(gp);
884  } else {
885  g_free(cred);
886  cred = NULL;
887  gpdata->connected = FALSE;
888  }
889  }
890  break;
891 
892  default:
893  g_free(cred);
894  cred = NULL;
895  break;
896  }
897  return cred;
898 }
899 
900 static void remmina_plugin_vnc_rfb_cursor_shape(rfbClient *cl, int xhot, int yhot, int width, int height, int bytesPerPixel)
901 {
902  TRACE_CALL(__func__);
903  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
904  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
905 
906  if (!gtk_widget_get_window(GTK_WIDGET(gp)))
907  return;
908 
909  if (width && height) {
910  gint stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
911  guchar *data = g_malloc(stride * height);
912  remmina_plugin_vnc_rfb_fill_buffer(cl, data, stride, cl->rcSource,
913  width * cl->format.bitsPerPixel / 8, cl->rcMask, width, height);
914  cairo_surface_t *surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, stride);
915  if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
916  g_free(data);
917  return;
918  }
919  if (cairo_surface_set_user_data(surface, NULL, NULL, g_free) != CAIRO_STATUS_SUCCESS) {
920  g_free(data);
921  return;
922  }
923 
924  LOCK_BUFFER(TRUE);
925  remmina_plugin_vnc_queuecursor(gp, surface, xhot, yhot);
926  UNLOCK_BUFFER(TRUE);
927  }
928 }
929 
930 static void remmina_plugin_vnc_rfb_bell(rfbClient *cl)
931 {
932  TRACE_CALL(__func__);
933  REMMINA_PLUGIN_DEBUG("Bell message received");
935  RemminaFile *remminafile;
936  GdkWindow *window;
937 
938  gp = (RemminaProtocolWidget *)(rfbClientGetClientData(cl, NULL));
939  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
940 
941  if (remmina_plugin_service->file_get_int(remminafile, "disableserverbell", FALSE))
942  return;
943 
944  window = gtk_widget_get_window(GTK_WIDGET(gp));
945 
946  if (window)
947  gdk_window_beep(window);
948  REMMINA_PLUGIN_DEBUG("Beep emitted");
949 }
950 
951 /* Translate known VNC messages. It’s for intltool only, not for gcc */
952 #ifdef __DO_NOT_COMPILE_ME__
953 N_("Unable to connect to VNC server")
954 N_("Couldn’t convert '%s' to host address")
955 N_("VNC connection failed: %s")
956 N_("Your connection has been rejected.")
957 #endif
958 
959 #define MAX_ERROR_LENGTH 1000
960 static gchar vnc_error[MAX_ERROR_LENGTH + 1];
961 static gboolean vnc_encryption_disable_requested;
962 
963 static void remmina_plugin_vnc_rfb_output(const char *format, ...)
964 {
965  TRACE_CALL(__func__);
966  gchar *f, *p, *ff;
967 
968  if (!rfbEnableClientLogging)
969  return;
970 
971  va_list args;
972  va_start(args, format);
973  /* eliminate the last \n */
974  f = g_strdup(format);
975  if (f[strlen(f) - 1] == '\n') f[strlen(f) - 1] = '\0';
976 
977  if (g_strcmp0(f, "VNC connection failed: %s") == 0) {
978  p = va_arg(args, gchar *);
979  g_snprintf(vnc_error, MAX_ERROR_LENGTH, _(f), _(p));
980  } else if (g_strcmp0(f, "The VNC server requested an unknown authentication method. %s") == 0) {
981  p = va_arg(args, gchar *);
982  if (vnc_encryption_disable_requested) {
983  ff = g_strconcat(_("The VNC server requested an unknown authentication method. %s"),
984  ". ",
985  _("Please retry after turning on encryption for this profile."),
986  NULL);
987  g_snprintf(vnc_error, MAX_ERROR_LENGTH, ff, p);
988  g_free(ff);
989  } else {
990  g_snprintf(vnc_error, MAX_ERROR_LENGTH, _(f), p);
991  }
992  } else {
993  g_vsnprintf(vnc_error, MAX_ERROR_LENGTH, _(f), args);
994  }
995  g_free(f);
996  va_end(args);
997 
998  REMMINA_PLUGIN_DEBUG("VNC returned: %s", vnc_error);
999 }
1000 
1002 {
1003  TRACE_CALL(__func__);
1004  gchar *ptr;
1005 
1006  /* Need to add a line-feed for UltraVNC */
1007  ptr = g_strdup_printf("%s\n", text);
1009  g_free(ptr);
1010 }
1011 
1013 {
1014  TRACE_CALL(__func__);
1016 }
1017 
1018 /* Send CTRL+ALT+DEL keys keystrokes to the plugin drawing_area widget */
1020 {
1021  TRACE_CALL(__func__);
1022  guint keys[] = { GDK_KEY_Control_L, GDK_KEY_Alt_L, GDK_KEY_Delete };
1023  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1024 
1025  remmina_plugin_service->protocol_plugin_send_keys_signals(gpdata->drawing_area,
1026  keys, G_N_ELEMENTS(keys), GDK_KEY_PRESS | GDK_KEY_RELEASE);
1027 }
1028 
1030 {
1031  TRACE_CALL(__func__);
1032  remmina_plugin_service->protocol_plugin_chat_close(gp);
1033  return FALSE;
1034 }
1035 
1037 {
1038  TRACE_CALL(__func__);
1039  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1040  rfbClient *cl;
1041 
1042  cl = (rfbClient *)gpdata->client;
1043 
1044  remmina_plugin_service->protocol_plugin_chat_open(gp, cl->desktopName, remmina_plugin_vnc_chat_on_send,
1047  return FALSE;
1048 }
1049 
1050 static void remmina_plugin_vnc_rfb_chat(rfbClient *cl, int value, char *text)
1051 {
1052  TRACE_CALL(__func__);
1054 
1055  gp = (RemminaProtocolWidget *)(rfbClientGetClientData(cl, NULL));
1056  switch (value) {
1057  case rfbTextChatOpen:
1058  IDLE_ADD((GSourceFunc)remmina_plugin_vnc_open_chat, gp);
1059  break;
1060  case rfbTextChatClose:
1061  /* Do nothing… but wait for the next rfbTextChatFinished signal */
1062  break;
1063  case rfbTextChatFinished:
1064  IDLE_ADD((GSourceFunc)remmina_plugin_vnc_close_chat, gp);
1065  break;
1066  default:
1067  /* value is the text length */
1068  remmina_plugin_service->protocol_plugin_chat_receive(gp, text);
1069  break;
1070  }
1071 }
1072 
1074 {
1075  TRACE_CALL(__func__);
1076  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1077  fd_set fds;
1078 
1083  gpdata->listen_sock = ListenAtTcpPort(cl->listenPort);
1084  if (gpdata->listen_sock < 0)
1085  return FALSE;
1086 
1087  remmina_plugin_service->protocol_plugin_init_show_listen(gp, cl->listenPort);
1088 
1089  remmina_plugin_service->protocol_plugin_start_reverse_tunnel(gp, cl->listenPort);
1090 
1091  FD_ZERO(&fds);
1092  if (gpdata->listen_sock >= 0)
1093  FD_SET(gpdata->listen_sock, &fds);
1094 
1095  select(gpdata->listen_sock + 1, &fds, NULL, NULL, NULL);
1096 
1097  if (!FD_ISSET(gpdata->listen_sock, &fds)) {
1098  close(gpdata->listen_sock);
1099  gpdata->listen_sock = -1;
1100  return FALSE;
1101  }
1102 
1103  if (FD_ISSET(gpdata->listen_sock, &fds))
1104  cl->sock = AcceptTcpConnection(gpdata->listen_sock);
1105  if (cl->sock >= 0) {
1106  close(gpdata->listen_sock);
1107  gpdata->listen_sock = -1;
1108  }
1109  if (cl->sock < 0 || !SetNonBlocking(cl->sock))
1110  return FALSE;
1111 
1112  return TRUE;
1113 }
1114 
1115 
1117 {
1118  TRACE_CALL(__func__);
1119  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1120  gint ret;
1121  gint i;
1122  rfbClient *cl;
1123  fd_set fds;
1124  struct timeval timeout;
1125 
1126  if (!gpdata->connected) {
1127  gpdata->running = FALSE;
1128  return FALSE;
1129  }
1130 
1131  cl = (rfbClient *)gpdata->client;
1132 
1133  /*
1134  * Do not explicitly wait while data is on the buffer, see:
1135  * - https://jira.glyptodon.com/browse/GUAC-1056
1136  * - https://jira.glyptodon.com/browse/GUAC-1056?focusedCommentId=14348&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-14348
1137  * - https://github.com/apache/guacamole-server/blob/67680bd2d51e7949453f0f7ffc7f4234a1136715/src/protocols/vnc/vnc.c#L155
1138  */
1139  if (cl->buffered)
1140  goto handle_buffered;
1141 
1142  timeout.tv_sec = 10;
1143  timeout.tv_usec = 0;
1144  FD_ZERO(&fds);
1145  FD_SET(cl->sock, &fds);
1146  FD_SET(gpdata->vnc_event_pipe[0], &fds);
1147  ret = select(MAX(cl->sock, gpdata->vnc_event_pipe[0]) + 1, &fds, NULL, NULL, &timeout);
1148 
1149  /* Sometimes it returns <0 when opening a modal dialog in other window. Absolutely weird */
1150  /* So we continue looping anyway */
1151  if (ret <= 0)
1152  return TRUE;
1153 
1154  if (FD_ISSET(gpdata->vnc_event_pipe[0], &fds))
1156  if (FD_ISSET(cl->sock, &fds)) {
1157  i = WaitForMessage(cl, 500);
1158  if (i < 0)
1159  return TRUE;
1160 handle_buffered:
1161  if (!HandleRFBServerMessage(cl)) {
1162  gpdata->running = FALSE;
1163  if (gpdata->connected && !remmina_plugin_service->protocol_plugin_is_closed(gp))
1164  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
1165  return FALSE;
1166  }
1167  }
1168 
1169  return TRUE;
1170 }
1171 
1173 {
1174  TRACE_CALL(__func__);
1175  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1176  RemminaFile *remminafile;
1177  rfbClient *cl = NULL;
1178  gchar *host;
1179  gchar *s = NULL;
1180 
1181  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1182  gpdata->running = TRUE;
1183 
1184  rfbClientLog = rfbClientErr = remmina_plugin_vnc_rfb_output;
1185 
1186  gint colordepth = remmina_plugin_service->file_get_int(remminafile, "colordepth", 32);
1187  gint quality = remmina_plugin_service->file_get_int(remminafile, "quality", 9);
1188 
1189  while (gpdata->connected) {
1190  gpdata->auth_called = FALSE;
1191 
1192  host = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, VNC_DEFAULT_PORT, TRUE);
1193 
1194  if (host == NULL) {
1195  REMMINA_PLUGIN_DEBUG("host is null");
1196  gpdata->connected = FALSE;
1197  break;
1198  }
1199 
1200  /* int bitsPerSample,int samplesPerPixel, int bytesPerPixel */
1201  switch (colordepth) {
1202  case 8:
1203  cl = rfbGetClient(2, 3, 1);
1204  break;
1205  case 15:
1206  case 16:
1207  cl = rfbGetClient(5, 3, 2);
1208  break;
1209  case 24:
1210  cl = rfbGetClient(6, 3, 3);
1211  break;
1212  case 32:
1213  default:
1214  cl = rfbGetClient(8, 3, 4);
1215  break;
1216  }
1217  REMMINA_PLUGIN_DEBUG("Color depth: %d", colordepth);
1218  cl->MallocFrameBuffer = remmina_plugin_vnc_rfb_allocfb;
1219  cl->canHandleNewFBSize = TRUE;
1220  cl->GetPassword = remmina_plugin_vnc_rfb_password;
1221  cl->GetCredential = remmina_plugin_vnc_rfb_credential;
1222  cl->GotFrameBufferUpdate = remmina_plugin_vnc_rfb_updatefb;
1233  cl->HandleKeyboardLedState = remmina_plugin_vnc_rfb_led_state;
1234  cl->GotXCutText = (
1235  remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE) ?
1237  cl->GotCursorShape = remmina_plugin_vnc_rfb_cursor_shape;
1238  cl->Bell = remmina_plugin_vnc_rfb_bell;
1239  cl->HandleTextChat = remmina_plugin_vnc_rfb_chat;
1245  rfbClientSetClientData(cl, NULL, gp);
1246 
1247  if (host[0] == '\0') {
1248  cl->serverHost = g_strdup(host);
1249  cl->listenSpecified = TRUE;
1250  if (remmina_plugin_service->file_get_int(remminafile, "ssh_tunnel_enabled", FALSE))
1251  /* When we use reverse tunnel, the local port does not really matter.
1252  * Hardcode a default port just in case the remote port is customized
1253  * to a privilege port then we will have problem listening. */
1254  cl->listenPort = 5500;
1255  else
1256  cl->listenPort = remmina_plugin_service->file_get_int(remminafile, "listenport", 5500);
1257 
1259  } else {
1260  if (strstr(host, "unix://") == host) {
1261  cl->serverHost = g_strdup(host + strlen("unix://"));
1262  cl->serverPort = 0;
1263  } else {
1264  remmina_plugin_service->get_server_port(host, VNC_DEFAULT_PORT, &s, &cl->serverPort);
1265  cl->serverHost = g_strdup(s);
1266  g_free(s);
1267  /* Support short-form (:0, :1) */
1268  if (cl->serverPort < 100)
1269  cl->serverPort += VNC_DEFAULT_PORT;
1270  }
1271  }
1272  g_free(host);
1273  host = NULL;
1274 
1275  if (cl->serverHost && strstr(cl->serverHost, "unix://") != cl->serverHost && remmina_plugin_service->file_get_string(remminafile, "proxy")) {
1276  remmina_plugin_service->get_server_port(
1277  remmina_plugin_service->file_get_string(remminafile, "server"),
1278  VNC_DEFAULT_PORT,
1279  &cl->destHost,
1280  &cl->destPort);
1281  remmina_plugin_service->get_server_port(
1282  remmina_plugin_service->file_get_string(remminafile, "proxy"),
1283  VNC_DEFAULT_PORT,
1284  &cl->serverHost,
1285  &cl->serverPort);
1286  REMMINA_PLUGIN_DEBUG("cl->serverHost: %s", cl->serverHost);
1287  REMMINA_PLUGIN_DEBUG("cl->serverPort: %d", cl->serverPort);
1288  REMMINA_PLUGIN_DEBUG("cl->destHost: %s", cl->destHost);
1289  REMMINA_PLUGIN_DEBUG("cl->destPort: %d", cl->destPort);
1290  }
1291 
1292  cl->appData.useRemoteCursor = (
1293  remmina_plugin_service->file_get_int(remminafile, "showcursor", FALSE) ? FALSE : TRUE);
1294 
1295  remmina_plugin_vnc_update_quality(cl, quality);
1296  remmina_plugin_vnc_update_colordepth(cl, colordepth);
1297  if ((cl->format.depth == 8) && (quality == 9))
1298  cl->appData.encodingsString = "copyrect zlib hextile raw";
1299  else if ((cl->format.depth == 8) && (quality == 2))
1300  cl->appData.encodingsString = "zrle ultra copyrect hextile zlib corre rre raw";
1301  else if ((cl->format.depth == 8) && (quality == 1))
1302  cl->appData.encodingsString = "zrle ultra copyrect hextile zlib corre rre raw";
1303  else if ((cl->format.depth == 8) && (quality == 0))
1304  cl->appData.encodingsString = "zrle ultra copyrect hextile zlib corre rre raw";
1305  SetFormatAndEncodings(cl);
1306 
1307  if (remmina_plugin_service->file_get_int(remminafile, "disableencryption", FALSE)) {
1308  vnc_encryption_disable_requested = TRUE;
1309  SetClientAuthSchemes(cl, remmina_plugin_vnc_no_encrypt_auth_types, -1);
1310  } else {
1311  vnc_encryption_disable_requested = FALSE;
1312  }
1313 
1314  if (rfbInitClient(cl, NULL, NULL)) {
1315  REMMINA_PLUGIN_DEBUG("Client initialization successfull");
1316  break;
1317  } else {
1318  REMMINA_PLUGIN_DEBUG("Client initialization failed");
1319  }
1320 
1321  /* If the authentication is not called, it has to be a fatal error and must quit */
1322  if (!gpdata->auth_called) {
1323  REMMINA_PLUGIN_DEBUG("Client not authenticated");
1324  gpdata->connected = FALSE;
1325  break;
1326  }
1327 
1328  /* vnc4server reports "already in use" after authentication. Workaround here */
1329  if (strstr(vnc_error, "The server is already in use")) {
1330  gpdata->connected = FALSE;
1331  gpdata->auth_called = FALSE;
1332  break;
1333  }
1334  /* Don't assume authentication failed for known network-related errors in
1335  libvncclient/sockets.c. */
1336  if (strstr(vnc_error, "read (") || strstr(vnc_error, "select\n") ||
1337  strstr(vnc_error, "write\n") || strstr(vnc_error, "Connection timed out")) {
1338  gpdata->connected = FALSE;
1339  gpdata->auth_called = FALSE;
1340  break;
1341  }
1342 
1343  /* Otherwise, it’s a password error. Try to clear saved password if any */
1344  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
1345 
1346  if (!gpdata->connected)
1347  break;
1348 
1349  remmina_plugin_service->protocol_plugin_init_show_retry(gp);
1350 
1351  /* It’s safer to sleep a while before reconnect */
1352  sleep(2);
1353 
1354  gpdata->auth_first = FALSE;
1355  }
1356 
1357  if (!gpdata->connected) {
1358  REMMINA_PLUGIN_DEBUG("Client not connected with error: %s", vnc_error);
1359  if (cl && !gpdata->auth_called && !(remmina_plugin_service->protocol_plugin_has_error(gp)))
1360  remmina_plugin_service->protocol_plugin_set_error(gp, "%s", vnc_error);
1361  gpdata->running = FALSE;
1362 
1363  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
1364 
1365  return FALSE;
1366  }
1367 
1368  REMMINA_PLUGIN_DEBUG("Client connected");
1369  remmina_plugin_service->protocol_plugin_init_save_cred(gp);
1370 
1371  gpdata->client = cl;
1372 
1373  remmina_plugin_service->protocol_plugin_signal_connection_opened(gp);
1374 
1375  if (remmina_plugin_service->file_get_int(remminafile, "disableserverinput", FALSE))
1376  PermitServerInput(cl, 1);
1377 
1378  if (gpdata->thread) {
1379  while (remmina_plugin_vnc_main_loop(gp)) {
1380  }
1381  gpdata->running = FALSE;
1382  } else {
1383  IDLE_ADD((GSourceFunc)remmina_plugin_vnc_main_loop, gp);
1384  }
1385 
1386  return FALSE;
1387 }
1388 
1389 
1390 static gpointer
1392 {
1393  TRACE_CALL(__func__);
1394  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
1395 
1396  CANCEL_ASYNC
1398  return NULL;
1399 }
1400 
1401 
1403 {
1404  GtkAllocation widget_allocation;
1406 
1408  gtk_widget_get_allocation(widget, &widget_allocation);
1409  result.x = x * remmina_plugin_service->protocol_plugin_get_width(gp) / widget_allocation.width;
1410  result.y = y * remmina_plugin_service->protocol_plugin_get_height(gp) / widget_allocation.height;
1411  } else {
1412  result.x = x;
1413  result.y = y;
1414  }
1415 
1416  return result;
1417 }
1418 
1419 static gboolean remmina_plugin_vnc_on_motion(GtkWidget *widget, GdkEventMotion *event, RemminaProtocolWidget *gp)
1420 {
1421  TRACE_CALL(__func__);
1422  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1423  RemminaFile *remminafile;
1424  RemminaPluginVncCoordinates coordinates;
1425 
1426  if (!gpdata->connected || !gpdata->client)
1427  return FALSE;
1428  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1429  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1430  return FALSE;
1431 
1432  coordinates = remmina_plugin_vnc_scale_coordinates(widget, gp, event->x, event->y);
1433  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_POINTER, GINT_TO_POINTER(coordinates.x), GINT_TO_POINTER(coordinates.y),
1434  GINT_TO_POINTER(gpdata->button_mask));
1435  return TRUE;
1436 }
1437 
1438 static gboolean remmina_plugin_vnc_on_button(GtkWidget *widget, GdkEventButton *event, RemminaProtocolWidget *gp)
1439 {
1440  TRACE_CALL(__func__);
1441  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1442  RemminaFile *remminafile;
1443  RemminaPluginVncCoordinates coordinates;
1444  gint mask;
1445 
1446  if (!gpdata->connected || !gpdata->client)
1447  return FALSE;
1448  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1449  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1450  return FALSE;
1451 
1452  /* We only accept 3 buttons */
1453  if (event->button < 1 || event->button > 3)
1454  return FALSE;
1455  /* We bypass 2button-press and 3button-press events */
1456  if (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE)
1457  return TRUE;
1458 
1459  mask = (1 << (event->button - 1));
1460  gpdata->button_mask = (event->type == GDK_BUTTON_PRESS ? (gpdata->button_mask | mask) :
1461  (gpdata->button_mask & (0xff - mask)));
1462 
1463  coordinates = remmina_plugin_vnc_scale_coordinates(widget, gp, event->x, event->y);
1464  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_POINTER, GINT_TO_POINTER(coordinates.x), GINT_TO_POINTER(coordinates.y),
1465  GINT_TO_POINTER(gpdata->button_mask));
1466  return TRUE;
1467 }
1468 
1469 static gint delta_to_mask(float delta, float *accum, gint mask_plus, gint mask_minus)
1470 {
1471  *accum += delta;
1472  if (*accum >= 1.0) {
1473  *accum = 0.0;
1474  return mask_plus;
1475  } else if (*accum <= -1.0) {
1476  *accum = 0.0;
1477  return mask_minus;
1478  }
1479  return 0;
1480 }
1481 
1482 static gboolean remmina_plugin_vnc_on_scroll(GtkWidget *widget, GdkEventScroll *event, RemminaProtocolWidget *gp)
1483 {
1484  TRACE_CALL(__func__);
1485  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1486  RemminaFile *remminafile;
1487  RemminaPluginVncCoordinates coordinates;
1488  gint mask;
1489 
1490  if (!gpdata->connected || !gpdata->client)
1491  return FALSE;
1492  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1493  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1494  return FALSE;
1495 
1496  switch (event->direction) {
1497  case GDK_SCROLL_UP:
1498  mask = (1 << 3);
1499  gpdata->scroll_y_accumulator = 0;
1500  break;
1501  case GDK_SCROLL_DOWN:
1502  mask = (1 << 4);
1503  gpdata->scroll_y_accumulator = 0;
1504  break;
1505  case GDK_SCROLL_LEFT:
1506  mask = (1 << 5);
1507  gpdata->scroll_x_accumulator = 0;
1508  break;
1509  case GDK_SCROLL_RIGHT:
1510  mask = (1 << 6);
1511  gpdata->scroll_x_accumulator = 0;
1512  break;
1513 #if GTK_CHECK_VERSION(3, 4, 0)
1514  case GDK_SCROLL_SMOOTH:
1515  /* RFB does not seems to support SMOOTH scroll, so we accumulate GTK delta requested
1516  * up to 1.0 and then send a normal RFB wheel scroll when the accumulator reaches 1.0 */
1517  mask = delta_to_mask(event->delta_y, &(gpdata->scroll_y_accumulator), (1 << 4), (1 << 3));
1518  mask |= delta_to_mask(event->delta_x, &(gpdata->scroll_x_accumulator), (1 << 6), (1 << 5));
1519  if (!mask)
1520  return FALSE;
1521  break;
1522 #endif
1523  default:
1524  return FALSE;
1525  }
1526 
1527  coordinates = remmina_plugin_vnc_scale_coordinates(widget, gp, event->x, event->y);
1528  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_POINTER, GINT_TO_POINTER(coordinates.x), GINT_TO_POINTER(coordinates.y),
1529  GINT_TO_POINTER(mask | gpdata->button_mask));
1530  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_POINTER, GINT_TO_POINTER(coordinates.x), GINT_TO_POINTER(coordinates.y),
1531  GINT_TO_POINTER(gpdata->button_mask));
1532 
1533  return TRUE;
1534 }
1535 
1537 {
1538  TRACE_CALL(__func__);
1539  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1540  RemminaKeyVal *k;
1541  gint i;
1542 
1543  if (!gpdata)
1544  return;
1545 
1546  if (keycode == 0) {
1547  /* Send all release key events for previously pressed keys */
1548  for (i = 0; i < gpdata->pressed_keys->len; i++) {
1549  k = g_ptr_array_index(gpdata->pressed_keys, i);
1551  GINT_TO_POINTER(FALSE), NULL);
1552  g_free(k);
1553  }
1554  g_ptr_array_set_size(gpdata->pressed_keys, 0);
1555  } else {
1556  /* Unregister the keycode only */
1557  for (i = 0; i < gpdata->pressed_keys->len; i++) {
1558  k = g_ptr_array_index(gpdata->pressed_keys, i);
1559  if (k->keycode == keycode) {
1560  g_free(k);
1561  g_ptr_array_remove_index_fast(gpdata->pressed_keys, i);
1562  break;
1563  }
1564  }
1565  }
1566 }
1567 
1568 static gboolean remmina_plugin_vnc_on_key(GtkWidget *widget, GdkEventKey *event, RemminaProtocolWidget *gp)
1569 {
1570  TRACE_CALL(__func__);
1571  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1572  RemminaFile *remminafile;
1573  RemminaKeyVal *k;
1574  guint event_keyval;
1575  guint keyval;
1576 
1577  if (!gpdata->connected || !gpdata->client)
1578  return FALSE;
1579  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1580  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1581  return FALSE;
1582 
1583  gpdata->scroll_x_accumulator = 0;
1584  gpdata->scroll_y_accumulator = 0;
1585 
1586  /* When sending key release, try first to find out a previously sent keyval
1587  * to workaround bugs like https://bugs.freedesktop.org/show_bug.cgi?id=7430 */
1588 
1589  event_keyval = event->keyval;
1590  if (event->type == GDK_KEY_RELEASE) {
1591  for (int i = 0; i < gpdata->pressed_keys->len; i++) {
1592  k = g_ptr_array_index(gpdata->pressed_keys, i);
1593  if (k->keycode == event->hardware_keycode) {
1594  event_keyval = k->keyval;
1595  break;
1596  }
1597  }
1598  }
1599 
1600  keyval = remmina_plugin_service->pref_keymap_get_keyval(remmina_plugin_service->file_get_string(remminafile, "keymap"),
1601  event_keyval);
1602 
1603  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_KEY, GUINT_TO_POINTER(keyval),
1604  GINT_TO_POINTER(event->type == GDK_KEY_PRESS ? TRUE : FALSE), NULL);
1605 
1606  /* Register/unregister the pressed key */
1607  if (event->type == GDK_KEY_PRESS) {
1608  k = g_new(RemminaKeyVal, 1);
1609  k->keyval = keyval;
1610  k->keycode = event->hardware_keycode;
1611  g_ptr_array_add(gpdata->pressed_keys, k);
1612  } else {
1613  remmina_plugin_vnc_release_key(gp, event->hardware_keycode);
1614  }
1615  return TRUE;
1616 }
1617 
1618 static void remmina_plugin_vnc_on_cuttext_request(GtkClipboard *clipboard, const gchar *text, RemminaProtocolWidget *gp)
1619 {
1620  TRACE_CALL(__func__);
1621  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1622  GDateTime *t;
1623  glong diff;
1624  gsize br, bw;
1625  gchar *latin1_text;
1626  const char *cur_charset;
1627 
1628  if (text) {
1629  /* A timer (1 second) to avoid clipboard "loopback": text cut out from VNC won’t paste back into VNC */
1630  t = g_date_time_new_now_utc();
1631  diff = g_date_time_difference(t, gpdata->clipboard_timer) / 100000; // tenth of second
1632  if (diff < 10)
1633  return;
1634  g_date_time_unref(gpdata->clipboard_timer);
1635  gpdata->clipboard_timer = t;
1636  /* Convert text from current charset to latin-1 before sending to remote server.
1637  * See RFC6143 7.5.6 */
1638  g_get_charset(&cur_charset);
1639  latin1_text = g_convert_with_fallback(text, -1, "ISO-8859-1", cur_charset, "?", &br, &bw, NULL);
1640  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_CUTTEXT, (gpointer)latin1_text, NULL, NULL);
1641  g_free(latin1_text);
1642  }
1643 }
1644 
1645 static void remmina_plugin_vnc_on_cuttext(GtkClipboard *clipboard, GdkEvent *event, RemminaProtocolWidget *gp)
1646 {
1647  TRACE_CALL(__func__);
1648  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1649  RemminaFile *remminafile;
1650 
1651  if (!gpdata->connected || !gpdata->client)
1652  return;
1653  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1654  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1655  return;
1656 
1657  gtk_clipboard_request_text(clipboard, (GtkClipboardTextReceivedFunc)remmina_plugin_vnc_on_cuttext_request, gp);
1658 }
1659 
1661 {
1662  TRACE_CALL(__func__);
1663  RemminaFile *remminafile;
1664  GdkCursor *cursor;
1665  GdkPixbuf *pixbuf;
1666 
1667  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1668 
1669  if (remmina_plugin_service->file_get_int(remminafile, "showcursor", FALSE)) {
1670  /* Hide local cursor (show a small dot instead) */
1671  pixbuf = gdk_pixbuf_new_from_xpm_data(dot_cursor_xpm);
1672  cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), pixbuf, dot_cursor_x_hot, dot_cursor_y_hot);
1673  g_object_unref(pixbuf);
1674  gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(gp)), cursor);
1675  g_object_unref(cursor);
1676  }
1677 }
1678 
1679 /******************************************************************************************/
1680 
1682 {
1683  TRACE_CALL(__func__);
1684  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1685  RemminaFile *remminafile;
1686 
1687  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1688 
1689  gpdata->connected = TRUE;
1690  gchar *server;
1691  gint port;
1692  const gchar* raw_server;
1693 
1694  remmina_plugin_service->protocol_plugin_register_hostkey(gp, gpdata->drawing_area);
1695 
1696  g_signal_connect(G_OBJECT(gp), "realize", G_CALLBACK(remmina_plugin_vnc_on_realize), NULL);
1697  g_signal_connect(G_OBJECT(gpdata->drawing_area), "motion-notify-event", G_CALLBACK(remmina_plugin_vnc_on_motion), gp);
1698  g_signal_connect(G_OBJECT(gpdata->drawing_area), "button-press-event", G_CALLBACK(remmina_plugin_vnc_on_button), gp);
1699  g_signal_connect(G_OBJECT(gpdata->drawing_area), "button-release-event", G_CALLBACK(remmina_plugin_vnc_on_button), gp);
1700  g_signal_connect(G_OBJECT(gpdata->drawing_area), "scroll-event", G_CALLBACK(remmina_plugin_vnc_on_scroll), gp);
1701  g_signal_connect(G_OBJECT(gpdata->drawing_area), "key-press-event", G_CALLBACK(remmina_plugin_vnc_on_key), gp);
1702  g_signal_connect(G_OBJECT(gpdata->drawing_area), "key-release-event", G_CALLBACK(remmina_plugin_vnc_on_key), gp);
1703 
1704  if (!remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE))
1705  gpdata->clipboard_handler = g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)),
1706  "owner-change", G_CALLBACK(remmina_plugin_vnc_on_cuttext), gp);
1707 
1708 
1709  if (pthread_create(&gpdata->thread, NULL, remmina_plugin_vnc_main_thread, gp)) {
1710  /* I don’t think this will ever happen… */
1711  g_print("Could not initialize pthread. Falling back to non-thread mode…\n");
1712  g_timeout_add(0, (GSourceFunc)remmina_plugin_vnc_main, gp);
1713  gpdata->thread = 0;
1714  }
1715 
1716  raw_server = remmina_plugin_service->file_get_string(remminafile, "server");
1717 
1718  if (raw_server && strstr(raw_server, "unix://") == raw_server) {
1719  REMMINA_PLUGIN_AUDIT(_("Connected to %s via VNC"), raw_server);
1720  } else {
1721  remmina_plugin_service->get_server_port(raw_server,
1722  VNC_DEFAULT_PORT,
1723  &server,
1724  &port);
1725 
1726  REMMINA_PLUGIN_AUDIT(_("Connected to %s:%d via VNC"), server, port);
1727  g_free(server), server = NULL;
1728  }
1729 #if LIBVNCSERVER_CHECK_VERSION_VERSION(0, 9, 14)
1730  remmina_plugin_service->protocol_plugin_unlock_dynres(gp);
1731 #endif
1732  return TRUE;
1733 }
1734 
1736 {
1737  TRACE_CALL(__func__);
1738  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1739 
1740  gchar *server;
1741  gint port;
1742 
1743  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1744  remmina_plugin_service->get_server_port(remmina_plugin_service->file_get_string(remminafile, "server"),
1745  VNC_DEFAULT_PORT,
1746  &server,
1747  &port);
1748 
1749  REMMINA_PLUGIN_AUDIT(_("Disconnected from %s:%d via VNC"), server, port);
1750  g_free(server), server = NULL;
1751 
1752  /* wait until the running attribute is set to false by the VNC thread */
1753  if (gpdata->running)
1754  return TRUE;
1755 
1756  /* unregister the clipboard monitor */
1757  if (gpdata->clipboard_handler) {
1758  g_signal_handler_disconnect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)), gpdata->clipboard_handler);
1759  gpdata->clipboard_handler = 0;
1760  }
1761 
1762  if (gpdata->queuecursor_handler) {
1763  g_source_remove(gpdata->queuecursor_handler);
1764  gpdata->queuecursor_handler = 0;
1765  }
1766  if (gpdata->queuecursor_surface) {
1767  cairo_surface_destroy(gpdata->queuecursor_surface);
1768  gpdata->queuecursor_surface = NULL;
1769  }
1770 
1771  if (gpdata->queuedraw_handler) {
1772  g_source_remove(gpdata->queuedraw_handler);
1773  gpdata->queuedraw_handler = 0;
1774  }
1775  if (gpdata->listen_sock >= 0)
1776  close(gpdata->listen_sock);
1777  if (gpdata->client) {
1778  rfbClientCleanup((rfbClient *)gpdata->client);
1779  gpdata->client = NULL;
1780  }
1781  if (gpdata->rgb_buffer) {
1782  cairo_surface_destroy(gpdata->rgb_buffer);
1783  gpdata->rgb_buffer = NULL;
1784  }
1785  if (gpdata->vnc_buffer) {
1786  g_free(gpdata->vnc_buffer);
1787  gpdata->vnc_buffer = NULL;
1788  }
1789  g_ptr_array_free(gpdata->pressed_keys, TRUE);
1790  g_date_time_unref(gpdata->clipboard_timer);
1792  g_queue_free(gpdata->vnc_event_queue);
1793  pthread_mutex_destroy(&gpdata->vnc_event_queue_mutex);
1794  close(gpdata->vnc_event_pipe[0]);
1795  close(gpdata->vnc_event_pipe[1]);
1796 
1797 
1798  pthread_mutex_destroy(&gpdata->buffer_mutex);
1799  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
1800 
1801  return FALSE;
1802 }
1803 
1805 {
1806  TRACE_CALL(__func__);
1807  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1808 
1809  gpdata->connected = FALSE;
1810 
1811  if (gpdata->thread) {
1812  pthread_cancel(gpdata->thread);
1813  if (gpdata->thread) pthread_join(gpdata->thread, NULL);
1814  gpdata->running = FALSE;
1816  } else {
1817  g_timeout_add(200, (GSourceFunc)remmina_plugin_vnc_close_connection_timeout, gp);
1818  }
1819 
1820  return FALSE;
1821 }
1822 
1824 {
1825  TRACE_CALL(__func__);
1826  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1827  switch (feature->id) {
1828  case REMMINA_PLUGIN_VNC_FEATURE_PREF_DISABLESERVERINPUT:
1829  return SupportsClient2Server((rfbClient *)(gpdata->client), rfbSetServerInput) ? TRUE : FALSE;
1830  case REMMINA_PLUGIN_VNC_FEATURE_TOOL_CHAT:
1831  return SupportsClient2Server((rfbClient *)(gpdata->client), rfbTextChat) ? TRUE : FALSE;
1832  default:
1833  return TRUE;
1834  }
1835 }
1836 
1838 {
1839  TRACE_CALL(__func__);
1840  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1841  RemminaFile *remminafile;
1842  rfbClient* client;
1843  uint8_t previous_bpp;
1844  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1845  switch (feature->id) {
1846  case REMMINA_PLUGIN_VNC_FEATURE_PREF_QUALITY:
1847  remmina_plugin_vnc_update_quality((rfbClient *)(gpdata->client),
1848  remmina_plugin_service->file_get_int(remminafile, "quality", 9));
1849  remmina_plugin_vnc_update_colordepth((rfbClient *)(gpdata->client),
1850  remmina_plugin_service->file_get_int(remminafile, "colordepth", 32));
1851  SetFormatAndEncodings((rfbClient *)(gpdata->client));
1852  break;
1853  case REMMINA_PLUGIN_VNC_FEATURE_PREF_COLOR:
1854  client = (rfbClient *)(gpdata->client);
1855  previous_bpp = client->format.bitsPerPixel;
1857  remmina_plugin_service->file_get_int(remminafile, "colordepth", 32));
1858  SetFormatAndEncodings(client);
1859  //Need to clear away old and reallocate if we're increasing bpp
1860  if (client->format.bitsPerPixel > previous_bpp){
1861  remmina_plugin_vnc_rfb_allocfb((rfbClient *)(gpdata->client));
1862  SendFramebufferUpdateRequest((rfbClient *)(gpdata->client), 0, 0,
1863  remmina_plugin_service->protocol_plugin_get_width(gp),
1864  remmina_plugin_service->protocol_plugin_get_height(gp), FALSE);
1865  }
1866  break;
1867  case REMMINA_PLUGIN_VNC_FEATURE_PREF_VIEWONLY:
1868  break;
1869  case REMMINA_PLUGIN_VNC_FEATURE_PREF_DISABLESERVERINPUT:
1870  PermitServerInput((rfbClient *)(gpdata->client),
1871  remmina_plugin_service->file_get_int(remminafile, "disableserverinput", FALSE) ? 1 : 0);
1872  break;
1873  case REMMINA_PLUGIN_VNC_FEATURE_UNFOCUS:
1875  break;
1876  case REMMINA_PLUGIN_VNC_FEATURE_SCALE:
1877  remmina_plugin_vnc_update_scale(gp, remmina_plugin_service->file_get_int(remminafile, "scale", FALSE));
1878  break;
1879  case REMMINA_PLUGIN_VNC_FEATURE_TOOL_REFRESH:
1880  SendFramebufferUpdateRequest((rfbClient *)(gpdata->client), 0, 0,
1881  remmina_plugin_service->protocol_plugin_get_width(gp),
1882  remmina_plugin_service->protocol_plugin_get_height(gp), FALSE);
1883  break;
1884  case REMMINA_PLUGIN_VNC_FEATURE_TOOL_CHAT:
1886  break;
1887  case REMMINA_PLUGIN_VNC_FEATURE_TOOL_SENDCTRLALTDEL:
1889  break;
1890  default:
1891  break;
1892  }
1893 }
1894 
1895 /* Send a keystroke to the plugin window */
1896 static void remmina_plugin_vnc_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
1897 {
1898  TRACE_CALL(__func__);
1899  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1900 
1901  remmina_plugin_service->protocol_plugin_send_keys_signals(gpdata->drawing_area,
1902  keystrokes, keylen, GDK_KEY_PRESS | GDK_KEY_RELEASE);
1903  return;
1904 }
1905 
1906 #if LIBVNCSERVER_CHECK_VERSION_VERSION(0, 9, 14)
1907 static gboolean remmina_plugin_vnc_on_size_allocate(GtkWidget *widget, GtkAllocation *alloc, RemminaProtocolWidget *gp)
1908 {
1909  TRACE_CALL(__func__);
1910  RemminaScaleMode scale_mode = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
1911  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1912 
1913  if (scale_mode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES){
1914  char str[1024];
1915  sprintf(str, "DEBUG: %d x %d", alloc->width, alloc->height);
1916  TRACE_CALL(str);
1917  if (gpdata->client){
1918  rfbClient *cl;
1919  SendExtDesktopSize(gpdata->client, alloc->width, alloc->height);
1920  }
1921  }
1922  return TRUE;
1923 }
1924 #endif
1925 
1926 static gboolean remmina_plugin_vnc_on_draw(GtkWidget *widget, cairo_t *context, RemminaProtocolWidget *gp)
1927 {
1928  TRACE_CALL(__func__);
1929  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1930  cairo_surface_t *surface;
1931  gint width, height;
1932  GtkAllocation widget_allocation;
1933 
1934  LOCK_BUFFER(FALSE);
1935 
1936  surface = gpdata->rgb_buffer;
1937  if (!surface) {
1938  UNLOCK_BUFFER(FALSE);
1939  return FALSE;
1940  }
1941 
1942  width = remmina_plugin_service->protocol_plugin_get_width(gp);
1943  height = remmina_plugin_service->protocol_plugin_get_height(gp);
1944 
1946  gtk_widget_get_allocation(widget, &widget_allocation);
1947  cairo_scale(context,
1948  (double)widget_allocation.width / width,
1949  (double)widget_allocation.height / height);
1950  }
1951 
1952  cairo_rectangle(context, 0, 0, width, height);
1953  cairo_set_source_surface(context, surface, 0, 0);
1954  cairo_fill(context);
1955 
1956  UNLOCK_BUFFER(FALSE);
1957  return TRUE;
1958 }
1959 
1961 {
1962  TRACE_CALL(__func__);
1963  RemminaPluginVncData *gpdata;
1964  gint flags;
1965  gdouble aspect_ratio;
1966 
1967  gpdata = g_new0(RemminaPluginVncData, 1);
1968  g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
1969 
1970  gboolean disable_smooth_scrolling = FALSE;
1971  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1972 
1973  disable_smooth_scrolling = remmina_plugin_service->file_get_int(remminafile, "disablesmoothscrolling", FALSE);
1974  REMMINA_PLUGIN_DEBUG("Disable smooth scrolling is set to %d", disable_smooth_scrolling);
1975 
1976  gpdata->drawing_area = gtk_drawing_area_new();
1977  gtk_widget_show(gpdata->drawing_area);
1978 
1979  aspect_ratio = remmina_plugin_service->file_get_double(remminafile, "aspect_ratio", 0);
1980  if (aspect_ratio > 0){
1981  GtkWidget* aspectframe = gtk_aspect_frame_new(NULL, 0, 0, aspect_ratio, FALSE);
1982 
1983  gtk_frame_set_shadow_type(aspectframe, GTK_SHADOW_NONE);
1984  gtk_widget_show(aspectframe);
1985  gtk_container_add(aspectframe, gpdata->drawing_area);
1986  gtk_container_add(GTK_CONTAINER(gp), aspectframe);
1987  }
1988  else{
1989  gtk_container_add(GTK_CONTAINER(gp), gpdata->drawing_area);
1990  }
1991 
1992  gtk_widget_add_events(
1993  gpdata->drawing_area,
1994  GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK
1995  | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK
1996  | GDK_KEY_RELEASE_MASK
1997  | GDK_SCROLL_MASK);
1998  gtk_widget_set_can_focus(gpdata->drawing_area, TRUE);
1999 
2000  if (!disable_smooth_scrolling) {
2001  REMMINA_PLUGIN_DEBUG("Adding GDK_SMOOTH_SCROLL_MASK");
2002  gtk_widget_add_events(gpdata->drawing_area, GDK_SMOOTH_SCROLL_MASK);
2003  }
2004 
2005 
2006  g_signal_connect(G_OBJECT(gpdata->drawing_area), "draw", G_CALLBACK(remmina_plugin_vnc_on_draw), gp);
2007 #if LIBVNCSERVER_CHECK_VERSION_VERSION(0, 9, 14)
2008  g_signal_connect(G_OBJECT(gpdata->drawing_area), "size-allocate", G_CALLBACK(remmina_plugin_vnc_on_size_allocate), gp);
2009 #endif
2010  gpdata->auth_first = TRUE;
2011  gpdata->clipboard_timer = g_date_time_new_now_utc();
2012  gpdata->listen_sock = -1;
2013  gpdata->pressed_keys = g_ptr_array_new();
2014  gpdata->vnc_event_queue = g_queue_new();
2015  pthread_mutex_init(&gpdata->vnc_event_queue_mutex, NULL);
2016  if (pipe(gpdata->vnc_event_pipe)) {
2017  g_print("Error creating pipes.\n");
2018  gpdata->vnc_event_pipe[0] = 0;
2019  gpdata->vnc_event_pipe[1] = 0;
2020  }
2021  flags = fcntl(gpdata->vnc_event_pipe[0], F_GETFL, 0);
2022  fcntl(gpdata->vnc_event_pipe[0], F_SETFL, flags | O_NONBLOCK);
2023 
2024  pthread_mutex_init(&gpdata->buffer_mutex, NULL);
2025 }
2026 
2027 /* Array of key/value pairs for color depths */
2028 static gpointer colordepth_list[] =
2029 {
2030  "32", N_("True colour (32 bpp)"),
2031  "16", N_("High colour (16 bpp)"),
2032  "8", N_("256 colours (8 bpp)"),
2033  NULL
2034 };
2035 
2036 /* Array of key/value pairs for quality selection */
2037 static gpointer quality_list[] =
2038 {
2039  "2", N_("Good"),
2040  "9", N_("Best (slowest)"),
2041  "1", N_("Medium"),
2042  "0", N_("Poor (fastest)"),
2043  NULL
2044 };
2045 
2046 static gchar repeater_tooltip[] =
2047  N_("Connect to VNC using a repeater:\n"
2048  " • The server field must contain the repeater ID, e.g. ID:123456789\n"
2049  " • The repeater field have to be set to the repeater IP and port, like:\n"
2050  " 10.10.10.12:5901\n"
2051  " • From the remote VNC server, you will connect to\n"
2052  " the repeater, e.g. with x11vnc:\n"
2053  " x11vnc -connect repeater=ID:123456789+10.10.10.12:5500");
2054 
2055 static gchar vnciport_tooltip[] =
2056  N_("Listening for remote VNC connection:\n"
2057  " • The “Listen on port” field is the port Remmina will listen to,\n"
2058  " e.g. 8888\n"
2059  " • From the remote VNC server, you will connect to\n"
2060  " Remmina, e.g. with x11vnc:\n"
2061  " x11vnc -display :0 -connect 192.168.1.36:8888");
2062 
2063 static gchar aspect_ratio_tooltip[] =
2064  N_("Lock the aspect ratio when dynamic resolution is enabled:\n"
2065  "\n"
2066  " • The aspect ratio should be entered as a decimal number, e.g. 1.777\n"
2067  " • 16:9 corresponds roughly to 1.7777, 4:3 corresponds roughly to 1.333\n"
2068  " • The default value of 0 does not enforce any aspect ratio");
2069 
2070 static gchar vncencodings_tooltip[] =
2071  N_("Overriding the pre-set VNC encoding quality:\n"
2072  "\n"
2073  " • “Poor (fastest)” sets encoding to “copyrect zlib hextile raw”\n"
2074  " • “Medium” sets encoding to “tight zrle ultra copyrect hextile zlib corre rre raw”\n"
2075  " • “Good” sets encoding to “tight zrle ultra copyrect hextile zlib corre rre raw”\n"
2076  " • “Best (slowest)” sets encoding to “copyrect zrle ultra zlib hextile corre rre raw”");
2077 
2078 /* Array of RemminaProtocolSetting for basic settings.
2079  * Each item is composed by:
2080  * a) RemminaProtocolSettingType for setting type
2081  * b) Setting name
2082  * c) Setting description
2083  * d) Compact disposition
2084  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
2085  * f) Setting tooltip
2086  * g) Validation data pointer, will be passed to the validation callback method.
2087  * h) Validation callback method (Can be NULL. Every entry will be valid then.)
2088  * use following prototype:
2089  * gboolean mysetting_validator_method(gpointer key, gpointer value,
2090  * gpointer validator_data);
2091  * gpointer key is a gchar* containing the setting's name,
2092  * gpointer value contains the value which should be validated,
2093  * gpointer validator_data contains your passed data.
2094  */
2096 {
2097  { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, "_rfb._tcp", NULL, NULL, NULL },
2098  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "proxy", N_("Repeater"), FALSE, NULL, repeater_tooltip, NULL, NULL },
2099  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL, NULL, NULL },
2100  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("User password"), FALSE, NULL, NULL, NULL, NULL },
2101  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "colordepth", N_("Colour depth"), FALSE, colordepth_list, NULL, NULL, NULL },
2102  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "quality", N_("Quality"), FALSE, quality_list, NULL, NULL, NULL },
2103  { REMMINA_PROTOCOL_SETTING_TYPE_KEYMAP, "keymap", NULL, FALSE, NULL, NULL, NULL, NULL },
2104  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL }
2105 };
2106 
2107 // Same as above.
2109 {
2110  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "listenport", N_("Listen on port"), FALSE, NULL, vnciport_tooltip, NULL, NULL},
2111  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL, NULL, NULL},
2112  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("User password"), FALSE, NULL, NULL, NULL, NULL},
2113  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "colordepth", N_("Colour depth"), FALSE, colordepth_list, NULL, NULL, NULL},
2114  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "quality", N_("Quality"), FALSE, quality_list, NULL, NULL, NULL},
2115  { REMMINA_PROTOCOL_SETTING_TYPE_KEYMAP, "keymap", NULL, FALSE, NULL, NULL, NULL, NULL},
2116  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL}
2117 };
2118 
2119 /* Array of RemminaProtocolSetting for advanced settings.
2120  * Each item is composed by:
2121  * a) RemminaProtocolSettingType for setting type
2122  * b) Setting name
2123  * c) Setting description
2124  * d) Compact disposition
2125  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
2126  * f) Setting Tooltip
2127  */
2129 {
2130  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "encodings", N_("Override pre-set VNC encodings"), FALSE, NULL, vncencodings_tooltip },
2131  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "aspect_ratio", N_("Dynamic resolution enforced aspec ratio"), FALSE, NULL, aspect_ratio_tooltip },
2132  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "tightencoding", N_("Force tight encoding"), TRUE, NULL, N_("Enabling this may help when the remote desktop looks scrambled") },
2133  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablesmoothscrolling", N_("Disable smooth scrolling"), FALSE, NULL, NULL },
2134  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"), TRUE, NULL, NULL },
2135  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableserverbell", N_("Ignore remote bell messages"), FALSE, NULL, NULL },
2136  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableserverinput", N_("Prevent local interaction on the server"), TRUE, NULL, NULL },
2137  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "showcursor", N_("Show remote cursor"), FALSE, NULL, NULL },
2138  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", N_("Turn off clipboard sync"), TRUE, NULL, NULL },
2139  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableencryption", N_("Turn off encryption"), FALSE, NULL, NULL },
2140  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "viewonly", N_("View only"), TRUE, NULL, NULL },
2141  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
2142 };
2143 
2144 /* Array for available features.
2145  * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
2147 {
2148  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_QUALITY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_RADIO), "quality",
2149  quality_list },
2150  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_COLOR, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_RADIO), "colordepth",
2151  colordepth_list },
2152  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_VIEWONLY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "viewonly",
2153  N_("View only") },
2154  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_DISABLESERVERINPUT, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "disableserverinput",N_("Prevent local interaction on the server") },
2155  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_VNC_FEATURE_TOOL_REFRESH, N_("Refresh"), NULL, NULL },
2156  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_VNC_FEATURE_TOOL_CHAT, N_("Open Chat…"), "face-smile", NULL },
2157  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_VNC_FEATURE_TOOL_SENDCTRLALTDEL, N_("Send Ctrl+Alt+Delete"), NULL, NULL },
2158  { REMMINA_PROTOCOL_FEATURE_TYPE_SCALE, REMMINA_PLUGIN_VNC_FEATURE_SCALE, NULL, NULL, NULL },
2159  { REMMINA_PROTOCOL_FEATURE_TYPE_UNFOCUS, REMMINA_PLUGIN_VNC_FEATURE_UNFOCUS, NULL, NULL, NULL },
2160 #if LIBVNCSERVER_CHECK_VERSION_VERSION(0, 9, 14)
2161  { REMMINA_PROTOCOL_FEATURE_TYPE_DYNRESUPDATE, REMMINA_PLUGIN_VNC_FEATURE_DYNRESUPDATE, NULL, NULL, NULL },
2162 #endif
2163  { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
2164 };
2165 
2166 /* Protocol plugin definition and features */
2168 {
2170  VNC_PLUGIN_NAME, // Name
2171  VNC_PLUGIN_DESCRIPTION, // Description
2172  GETTEXT_PACKAGE, // Translation domain
2173  VNC_PLUGIN_VERSION, // Version number
2174  VNC_PLUGIN_APPICON, // Icon for normal connection
2175  VNC_PLUGIN_SSH_APPICON, // Icon for SSH connection
2176  remmina_plugin_vnc_basic_settings, // Array for basic settings
2177  remmina_plugin_vnc_advanced_settings, // Array for advanced settings
2178  REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type
2179  remmina_plugin_vnc_features, // Array for available features
2180  remmina_plugin_vnc_init, // Plugin initialization
2181  remmina_plugin_vnc_open_connection, // Plugin open connection
2182  remmina_plugin_vnc_close_connection, // Plugin close connection
2183  remmina_plugin_vnc_query_feature, // Query for available features
2184  remmina_plugin_vnc_call_feature, // Call a feature
2185  remmina_plugin_vnc_keystroke // Send a keystroke
2186 };
2187 
2188 /* Protocol plugin definition and features */
2190 {
2192  VNCI_PLUGIN_NAME, // Name
2193  VNCI_PLUGIN_DESCRIPTION, // Description
2194  GETTEXT_PACKAGE, // Translation domain
2195  VERSION, // Version number
2196  VNCI_PLUGIN_APPICON, // Icon for normal connection
2197  VNCI_PLUGIN_SSH_APPICON, // Icon for SSH connection
2198  remmina_plugin_vnci_basic_settings, // Array for basic settings
2199  remmina_plugin_vnc_advanced_settings, // Array for advanced settings
2200  REMMINA_PROTOCOL_SSH_SETTING_REVERSE_TUNNEL, // SSH settings type
2201  remmina_plugin_vnc_features, // Array for available features
2202  remmina_plugin_vnc_init, // Plugin initialization
2203  remmina_plugin_vnc_open_connection, // Plugin open connection
2204  remmina_plugin_vnc_close_connection, // Plugin close connection
2205  remmina_plugin_vnc_query_feature, // Query for available features
2206  remmina_plugin_vnc_call_feature, // Call a feature
2207  remmina_plugin_vnc_keystroke, // Send a keystroke
2208  NULL, // No screenshot support available
2209  NULL, // RCW map event
2210  NULL // RCW unmap event
2211 };
2212 
2213 G_MODULE_EXPORT gboolean
2215 {
2216  TRACE_CALL(__func__);
2217  remmina_plugin_service = service;
2218 
2219  bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
2220  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
2221 
2222  if (!service->register_plugin((RemminaPlugin *)&remmina_plugin_vnc))
2223  return FALSE;
2224 
2225  if (!service->register_plugin((RemminaPlugin *)&remmina_plugin_vnci))
2226  return FALSE;
2227 
2228  return TRUE;
2229 }
+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-2022 Antenore Gatta, Giovanni Panozzo
6  * Copyright (C) 2022-2023 Antenore Gatta, Giovanni Panozzo, Hiroyuki Tanaka
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  * In addition, as a special exception, the copyright holders give
24  * permission to link the code of portions of this program with the
25  * OpenSSL library under certain conditions as described in each
26  * individual source file, and distribute linked combinations
27  * including the two.
28  * You must obey the GNU General Public License in all respects
29  * for all of the code used other than OpenSSL. * If you modify
30  * file(s) with this exception, you may extend this exception to your
31  * version of the file(s), but you are not obligated to do so. * If you
32  * do not wish to do so, delete this exception statement from your
33  * version. * If you delete this exception statement from all source
34  * files in the program, then also delete it here.
35  *
36  */
37 
38 #include <gmodule.h>
39 #include "vnc_plugin.h"
40 #include <rfb/rfbclient.h>
41 
42 #define REMMINA_PLUGIN_VNC_FEATURE_PREF_QUALITY 1
43 #define REMMINA_PLUGIN_VNC_FEATURE_PREF_VIEWONLY 2
44 #define REMMINA_PLUGIN_VNC_FEATURE_PREF_DISABLESERVERINPUT 3
45 #define REMMINA_PLUGIN_VNC_FEATURE_TOOL_REFRESH 4
46 #define REMMINA_PLUGIN_VNC_FEATURE_TOOL_CHAT 5
47 #define REMMINA_PLUGIN_VNC_FEATURE_SCALE 6
48 #define REMMINA_PLUGIN_VNC_FEATURE_UNFOCUS 7
49 #define REMMINA_PLUGIN_VNC_FEATURE_TOOL_SENDCTRLALTDEL 8
50 #define REMMINA_PLUGIN_VNC_FEATURE_PREF_COLOR 9
51 #define REMMINA_PLUGIN_VNC_FEATURE_DYNRESUPDATE 10
52 
53 #define VNC_DEFAULT_PORT 5900
54 
55 #define GET_PLUGIN_DATA(gp) (RemminaPluginVncData *)g_object_get_data(G_OBJECT(gp), "plugin-data")
56 
58 
59 static int dot_cursor_x_hot = 2;
60 static int dot_cursor_y_hot = 2;
61 static const gchar *dot_cursor_xpm[] =
62 { "5 5 3 1", " c None", ". c #000000", "+ c #FFFFFF", " ... ", ".+++.", ".+ +.", ".+++.", " ... " };
63 
64 
65 #define LOCK_BUFFER(t) if (t) { CANCEL_DEFER } pthread_mutex_lock(&gpdata->buffer_mutex);
66 #define UNLOCK_BUFFER(t) pthread_mutex_unlock(&gpdata->buffer_mutex); if (t) { CANCEL_ASYNC }
67 
70  GtkWidget * widget;
71  gint x, y, width, height;
73  gboolean scale;
74 
75  /* Mutex for thread synchronization */
76  pthread_mutex_t mu;
77  /* Flag to catch cancellations */
78  gboolean cancelled;
79 };
80 
81 static gboolean onMainThread_cb(struct onMainThread_cb_data *d)
82 {
83  TRACE_CALL(__func__);
84  if (!d->cancelled) {
85  switch (d->func) {
86  case FUNC_UPDATE_SCALE:
88  break;
89  }
90  pthread_mutex_unlock(&d->mu);
91  } else {
92  /* thread has been cancelled, so we must free d memory here */
93  g_free(d);
94  }
95  return G_SOURCE_REMOVE;
96 }
97 
98 
99 static void onMainThread_cleanup_handler(gpointer data)
100 {
101  TRACE_CALL(__func__);
102  struct onMainThread_cb_data *d = data;
103 
104  d->cancelled = TRUE;
105 }
106 
107 
109 {
110  TRACE_CALL(__func__);
111  d->cancelled = FALSE;
112  pthread_cleanup_push(onMainThread_cleanup_handler, d);
113  pthread_mutex_init(&d->mu, NULL);
114  pthread_mutex_lock(&d->mu);
115  gdk_threads_add_idle((GSourceFunc)onMainThread_cb, (gpointer)d);
116 
117  pthread_mutex_lock(&d->mu);
118 
119  pthread_cleanup_pop(0);
120  pthread_mutex_unlock(&d->mu);
121  pthread_mutex_destroy(&d->mu);
122 }
123 
128 static gboolean check_for_endianness()
129 {
130  unsigned int x = 1;
131  char *c = (char *)&x;
132 
133  return (int)*c;
134 }
135 
136 static void remmina_plugin_vnc_event_push(RemminaProtocolWidget *gp, gint event_type, gpointer p1, gpointer p2, gpointer p3)
137 {
138  TRACE_CALL(__func__);
139  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
140  RemminaPluginVncEvent *event;
141 
142  event = g_new(RemminaPluginVncEvent, 1);
143  event->event_type = event_type;
144  switch (event_type) {
146  event->event_data.key.keyval = GPOINTER_TO_UINT(p1);
147  event->event_data.key.pressed = GPOINTER_TO_INT(p2);
148  break;
150  event->event_data.pointer.x = GPOINTER_TO_INT(p1);
151  event->event_data.pointer.y = GPOINTER_TO_INT(p2);
152  event->event_data.pointer.button_mask = GPOINTER_TO_INT(p3);
153  break;
156  event->event_data.text.text = g_strdup((char *)p1);
157  break;
158  default:
159  break;
160  }
161 
162  pthread_mutex_lock(&gpdata->vnc_event_queue_mutex);
163  g_queue_push_tail(gpdata->vnc_event_queue, event);
164  pthread_mutex_unlock(&gpdata->vnc_event_queue_mutex);
165 
166  if (write(gpdata->vnc_event_pipe[1], "\0", 1)) {
167  /* Ignore */
168  }
169 }
170 
172 {
173  TRACE_CALL(__func__);
174  switch (event->event_type) {
177  g_free(event->event_data.text.text);
178  break;
179  default:
180  break;
181  }
182  g_free(event);
183 }
184 
186 {
187  TRACE_CALL(__func__);
188  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
189  RemminaPluginVncEvent *event;
190 
191  /* This is called from main thread after plugin thread has
192  * been closed, so no queue locking is necessary here */
193  while ((event = g_queue_pop_head(gpdata->vnc_event_queue)) != NULL)
195 }
196 
197 static void remmina_plugin_vnc_scale_area(RemminaProtocolWidget *gp, gint *x, gint *y, gint *w, gint *h)
198 {
199  TRACE_CALL(__func__);
200  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
201  GtkAllocation widget_allocation;
202  gint width, height;
203  gint sx, sy, sw, sh;
204 
205  if (gpdata->rgb_buffer == NULL)
206  return;
207 
208  width = remmina_plugin_service->protocol_plugin_get_width(gp);
209  height = remmina_plugin_service->protocol_plugin_get_height(gp);
210 
211  gtk_widget_get_allocation(GTK_WIDGET(gp), &widget_allocation);
212 
213  if (widget_allocation.width == width && widget_allocation.height == height)
214  return; /* Same size, no scaling */
215 
216  /* We have to extend the scaled region 2 scaled pixels, to avoid gaps */
217  sx = MIN(MAX(0, (*x) * widget_allocation.width / width - widget_allocation.width / width - 2), widget_allocation.width - 1);
218  sy = MIN(MAX(0, (*y) * widget_allocation.height / height - widget_allocation.height / height - 2), widget_allocation.height - 1);
219  sw = MIN(widget_allocation.width - sx, (*w) * widget_allocation.width / width + widget_allocation.width / width + 4);
220  sh = MIN(widget_allocation.height - sy, (*h) * widget_allocation.height / height + widget_allocation.height / height + 4);
221 
222  *x = sx;
223  *y = sy;
224  *w = sw;
225  *h = sh;
226 }
227 
229 {
230  TRACE_CALL(__func__);
231  /* This function can be called from a non main thread */
232 
233  RemminaPluginVncData *gpdata;
234  gint width, height;
235 
236  if (!remmina_plugin_service->is_main_thread()) {
237  struct onMainThread_cb_data *d;
238  d = (struct onMainThread_cb_data *)g_malloc(sizeof(struct onMainThread_cb_data));
239  d->func = FUNC_UPDATE_SCALE;
240  d->gp = gp;
241  d->scale = scale;
243  g_free(d);
244  return;
245  }
246 
247  gpdata = GET_PLUGIN_DATA(gp);
248 
249  width = remmina_plugin_service->protocol_plugin_get_width(gp);
250  height = remmina_plugin_service->protocol_plugin_get_height(gp);
251  if (scale)
252  /* In scaled mode, drawing_area will get its dimensions from its parent */
253  gtk_widget_set_size_request(GTK_WIDGET(gpdata->drawing_area), -1, -1);
254  else
255  /* In non scaled mode, the plugins forces dimensions of drawing area */
256  gtk_widget_set_size_request(GTK_WIDGET(gpdata->drawing_area), width, height);
257 
258  remmina_plugin_service->protocol_plugin_update_align(gp);
259 }
260 
262 {
263  TRACE_CALL(__func__);
264  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
265  GdkCursor *cur;
266 
267  LOCK_BUFFER(FALSE);
268  gpdata->queuecursor_handler = 0;
269 
270  if (gpdata->queuecursor_surface) {
271  cur = gdk_cursor_new_from_surface(gdk_display_get_default(), gpdata->queuecursor_surface, gpdata->queuecursor_x,
272  gpdata->queuecursor_y);
273  gdk_window_set_cursor(gtk_widget_get_window(gpdata->drawing_area), cur);
274  g_object_unref(cur);
275  cairo_surface_destroy(gpdata->queuecursor_surface);
276  gpdata->queuecursor_surface = NULL;
277  } else {
278  gdk_window_set_cursor(gtk_widget_get_window(gpdata->drawing_area), NULL);
279  }
280  UNLOCK_BUFFER(FALSE);
281 
282  return FALSE;
283 }
284 
285 static void remmina_plugin_vnc_queuecursor(RemminaProtocolWidget *gp, cairo_surface_t *surface, gint x, gint y)
286 {
287  TRACE_CALL(__func__);
288  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
289 
290  if (gpdata->queuecursor_surface)
291  cairo_surface_destroy(gpdata->queuecursor_surface);
292  gpdata->queuecursor_surface = surface;
293  gpdata->queuecursor_x = x;
294  gpdata->queuecursor_y = y;
295  if (!gpdata->queuecursor_handler)
296  gpdata->queuecursor_handler = IDLE_ADD((GSourceFunc)remmina_plugin_vnc_setcursor, gp);
297 }
298 
299 typedef struct _RemminaKeyVal {
300  guint keyval;
301  guint16 keycode;
302 } RemminaKeyVal;
303 
304 /***************************** LibVNCClient related codes *********************************/
305 
307 { rfbNoAuth, rfbVncAuth, rfbMSLogon, 0 };
308 
310 {
311  RemminaPluginVncEvent *event;
312 
313  CANCEL_DEFER;
314  pthread_mutex_lock(&gpdata->vnc_event_queue_mutex);
315 
316  event = g_queue_pop_head(gpdata->vnc_event_queue);
317 
318  pthread_mutex_unlock(&gpdata->vnc_event_queue_mutex);
319  CANCEL_ASYNC;
320 
321  return event;
322 }
323 
325 {
326  TRACE_CALL(__func__);
327  RemminaPluginVncEvent *event;
328  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
329  rfbClient *cl;
330  gchar buf[100];
331 
332  cl = (rfbClient *)gpdata->client;
333  while ((event = remmina_plugin_vnc_event_queue_pop_head(gpdata)) != NULL) {
334  if (cl) {
335  switch (event->event_type) {
337  SendKeyEvent(cl, event->event_data.key.keyval, event->event_data.key.pressed);
338  break;
340  SendPointerEvent(cl, event->event_data.pointer.x, event->event_data.pointer.y,
341  event->event_data.pointer.button_mask);
342  break;
344  if (event->event_data.text.text) {
345  rfbClientLog("sending clipboard text '%s'\n", event->event_data.text.text);
346  SendClientCutText(cl, event->event_data.text.text, strlen(event->event_data.text.text));
347  }
348  break;
350  TextChatOpen(cl);
351  break;
353  TextChatSend(cl, event->event_data.text.text);
354  break;
356  TextChatClose(cl);
357  TextChatFinish(cl);
358  break;
359  default:
360  rfbClientLog("Ignoring VNC event: 0x%x\n", event->event_type);
361  break;
362  }
363  }
365  }
366  if (read(gpdata->vnc_event_pipe[0], buf, sizeof(buf))) {
367  /* Ignore */
368  }
369 }
370 
373  gchar * text;
374  gint textlen;
376 
377 static void remmina_plugin_vnc_update_quality(rfbClient *cl, gint quality)
378 {
379  TRACE_CALL(__func__);
380  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
381  RemminaFile *remminafile;
382  gchar *enc = NULL;
383 
390  switch (quality) {
391  case 9:
392  cl->appData.useBGR233 = 0;
393  cl->appData.encodingsString = "copyrect zlib hextile raw";
394  cl->appData.compressLevel = 1;
395  cl->appData.qualityLevel = 9;
396  break;
397  case 2:
398  cl->appData.useBGR233 = 0;
399  cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
400  cl->appData.compressLevel = 2;
401  cl->appData.qualityLevel = 7;
402  break;
403  case 1:
404  cl->appData.useBGR233 = 0;
405  cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
406  cl->appData.compressLevel = 3;
407  cl->appData.qualityLevel = 5;
408  break;
409  case 0:
410  default:
411  // bpp8 and tight encoding is not supported in libvnc
412  cl->appData.useBGR233 = 1;
413  cl->appData.encodingsString = "copyrect zrle ultra zlib hextile corre rre raw";
414  cl->appData.qualityLevel = 1;
415  break;
416  }
417  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
418  enc = g_strdup(remmina_plugin_service->file_get_string(remminafile, "encodings"));
419  if (enc) {
420  cl->appData.encodingsString = g_strdup(enc);
421  g_free (enc), enc = NULL;
422  }
423  gboolean tight = remmina_plugin_service->file_get_int (remminafile, "tightencoding", FALSE);
424  if (tight) {
425  if (!g_strrstr ( g_strdup(cl->appData.encodingsString), "tight\0")) {
426  cl->appData.encodingsString = g_strdup_printf("%s %s", "tight", g_strdup(cl->appData.encodingsString));
427  }
428  }
429 
430  REMMINA_PLUGIN_DEBUG("Quality: %d", quality);
431  REMMINA_PLUGIN_DEBUG("Encodings: %s", cl->appData.encodingsString);
432 }
433 
434 static void remmina_plugin_vnc_update_colordepth(rfbClient *cl, gint colordepth)
435 {
436  TRACE_CALL(__func__);
437 
438  cl->format.depth = colordepth;
439  cl->appData.requestedDepth = colordepth;
440 
441  cl->format.trueColour = 1;
442  cl->format.bigEndian = check_for_endianness()?FALSE:TRUE;
443 
444  switch (colordepth) {
445  case 8:
446  cl->format.depth = 8;
447  cl->format.bitsPerPixel = 8;
448  cl->format.blueMax = 3;
449  cl->format.blueShift = 6;
450  cl->format.greenMax = 7;
451  cl->format.greenShift = 3;
452  cl->format.redMax = 7;
453  cl->format.redShift = 0;
454  break;
455  case 16:
456  cl->format.depth = 15;
457  cl->format.bitsPerPixel = 16;
458  cl->format.redShift = 11;
459  cl->format.greenShift = 6;
460  cl->format.blueShift = 1;
461  cl->format.redMax = 31;
462  cl->format.greenMax = 31;
463  cl->format.blueMax = 31;
464  break;
465  case 32:
466  default:
467  cl->format.depth = 24;
468  cl->format.bitsPerPixel = 32;
469  cl->format.blueShift = 0;
470  cl->format.redShift = 16;
471  cl->format.greenShift = 8;
472  cl->format.blueMax = 0xff;
473  cl->format.redMax = 0xff;
474  cl->format.greenMax = 0xff;
475  break;
476  }
477 
478  rfbClientLog("colordepth = %d\n", colordepth);
479  rfbClientLog("format.depth = %d\n", cl->format.depth);
480  rfbClientLog("format.bitsPerPixel = %d\n", cl->format.bitsPerPixel);
481  rfbClientLog("format.blueShift = %d\n", cl->format.blueShift);
482  rfbClientLog("format.redShift = %d\n", cl->format.redShift);
483  rfbClientLog("format.trueColour = %d\n", cl->format.trueColour);
484  rfbClientLog("format.greenShift = %d\n", cl->format.greenShift);
485  rfbClientLog("format.blueMax = %d\n", cl->format.blueMax);
486  rfbClientLog("format.redMax = %d\n", cl->format.redMax);
487  rfbClientLog("format.greenMax = %d\n", cl->format.greenMax);
488  rfbClientLog("format.bigEndian = %d\n", cl->format.bigEndian);
489 }
490 
491 static rfbBool remmina_plugin_vnc_rfb_allocfb(rfbClient *cl)
492 {
493  TRACE_CALL(__func__);
494  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
495  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
496  gint width, height, depth, size;
497  gboolean scale;
498  cairo_surface_t *new_surface, *old_surface;
499 
500  width = cl->width;
501  height = cl->height;
502  depth = cl->format.bitsPerPixel;
503  size = width * height * (depth / 8);
504 
505  new_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
506  if (cairo_surface_status(new_surface) != CAIRO_STATUS_SUCCESS)
507  return FALSE;
508  old_surface = gpdata->rgb_buffer;
509 
510  LOCK_BUFFER(TRUE);
511 
512  remmina_plugin_service->protocol_plugin_set_width(gp, width);
513  remmina_plugin_service->protocol_plugin_set_height(gp, height);
514 
515  gpdata->rgb_buffer = new_surface;
516 
517  if (gpdata->vnc_buffer)
518  g_free(gpdata->vnc_buffer);
519  gpdata->vnc_buffer = (guchar *)g_malloc(size);
520  cl->frameBuffer = gpdata->vnc_buffer;
521 
522  UNLOCK_BUFFER(TRUE);
523 
524  if (old_surface)
525  cairo_surface_destroy(old_surface);
526 
529 
530  /* Notify window of change so that scroll border can be hidden or shown if needed */
531  remmina_plugin_service->protocol_plugin_desktop_resize(gp);
532 
533  /* Refresh the client’s updateRect - bug in xvncclient */
534  cl->updateRect.w = width;
535  cl->updateRect.h = height;
536 
537  return TRUE;
538 }
539 
540 static gint remmina_plugin_vnc_bits(gint n)
541 {
542  TRACE_CALL(__func__);
543  gint b = 0;
544 
545  while (n) {
546  b++;
547  n >>= 1;
548  }
549  return b ? b : 1;
550 }
551 
553 {
554  TRACE_CALL(__func__);
555  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
556  gint x, y, w, h;
557 
558  if (GTK_IS_WIDGET(gp) && gpdata->connected) {
559  LOCK_BUFFER(FALSE);
560  x = gpdata->queuedraw_x;
561  y = gpdata->queuedraw_y;
562  w = gpdata->queuedraw_w;
563  h = gpdata->queuedraw_h;
564  gpdata->queuedraw_handler = 0;
565  UNLOCK_BUFFER(FALSE);
566 
567  gtk_widget_queue_draw_area(GTK_WIDGET(gp), x, y, w, h);
568  }
569  return FALSE;
570 }
571 
572 static void remmina_plugin_vnc_queue_draw_area(RemminaProtocolWidget *gp, gint x, gint y, gint w, gint h)
573 {
574  TRACE_CALL(__func__);
575  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
576  gint nx2, ny2, ox2, oy2;
577 
578  LOCK_BUFFER(TRUE);
579  if (gpdata->queuedraw_handler) {
580  nx2 = x + w;
581  ny2 = y + h;
582  ox2 = gpdata->queuedraw_x + gpdata->queuedraw_w;
583  oy2 = gpdata->queuedraw_y + gpdata->queuedraw_h;
584  gpdata->queuedraw_x = MIN(gpdata->queuedraw_x, x);
585  gpdata->queuedraw_y = MIN(gpdata->queuedraw_y, y);
586  gpdata->queuedraw_w = MAX(ox2, nx2) - gpdata->queuedraw_x;
587  gpdata->queuedraw_h = MAX(oy2, ny2) - gpdata->queuedraw_y;
588  } else {
589  gpdata->queuedraw_x = x;
590  gpdata->queuedraw_y = y;
591  gpdata->queuedraw_w = w;
592  gpdata->queuedraw_h = h;
593  gpdata->queuedraw_handler = IDLE_ADD((GSourceFunc)remmina_plugin_vnc_queue_draw_area_real, gp);
594  }
595  UNLOCK_BUFFER(TRUE);
596 }
597 
598 static void remmina_plugin_vnc_rfb_fill_buffer(rfbClient *cl, guchar *dest, gint dest_rowstride, guchar *src,
599  gint src_rowstride, guchar *mask, gint w, gint h)
600 {
601  TRACE_CALL(__func__);
602  guchar *srcptr;
603  gint bytesPerPixel;
604  guint32 src_pixel;
605  gint ix, iy;
606  gint i;
607  guchar c;
608  gint rs, gs, bs, rm, gm, bm, rl, gl, bl, rr, gr, br;
609  gint r;
610  guint32 *destptr;
611 
612  union {
613  struct {
614  guchar a, r, g, b;
615  } colors;
616  guint32 argb;
617  } dst_pixel;
618 
619  bytesPerPixel = cl->format.bitsPerPixel / 8;
620  switch (cl->format.bitsPerPixel) {
621  case 32:
622  /* The following codes fill in the Alpha channel swap red/green value */
623  for (iy = 0; iy < h; iy++) {
624  destptr = (guint32 *)(dest + iy * dest_rowstride);
625  srcptr = src + iy * src_rowstride;
626  for (ix = 0; ix < w; ix++) {
627  if (!mask || *mask++) {
628  dst_pixel.colors.a = 0xff;
629  dst_pixel.colors.r = *(srcptr + 2);
630  dst_pixel.colors.g = *(srcptr + 1);
631  dst_pixel.colors.b = *srcptr;
632  *destptr++ = ntohl(dst_pixel.argb);
633  } else {
634  *destptr++ = 0;
635  }
636  srcptr += 4;
637  }
638  }
639  break;
640  default:
641  rm = cl->format.redMax;
642  gm = cl->format.greenMax;
643  bm = cl->format.blueMax;
644  rr = remmina_plugin_vnc_bits(rm);
645  gr = remmina_plugin_vnc_bits(gm);
646  br = remmina_plugin_vnc_bits(bm);
647  rl = 8 - rr;
648  gl = 8 - gr;
649  bl = 8 - br;
650  rs = cl->format.redShift;
651  gs = cl->format.greenShift;
652  bs = cl->format.blueShift;
653  for (iy = 0; iy < h; iy++) {
654  destptr = (guint32 *)(dest + iy * dest_rowstride);
655  srcptr = src + iy * src_rowstride;
656  for (ix = 0; ix < w; ix++) {
657  src_pixel = 0;
658  for (i = 0; i < bytesPerPixel; i++)
659  src_pixel += (*srcptr++) << (8 * i);
660 
661  if (!mask || *mask++) {
662  dst_pixel.colors.a = 0xff;
663  c = (guchar)((src_pixel >> rs) & rm) << rl;
664  for (r = rr; r < 8; r *= 2)
665  c |= c >> r;
666  dst_pixel.colors.r = c;
667  c = (guchar)((src_pixel >> gs) & gm) << gl;
668  for (r = gr; r < 8; r *= 2)
669  c |= c >> r;
670  dst_pixel.colors.g = c;
671  c = (guchar)((src_pixel >> bs) & bm) << bl;
672  for (r = br; r < 8; r *= 2)
673  c |= c >> r;
674  dst_pixel.colors.b = c;
675  *destptr++ = ntohl(dst_pixel.argb);
676  } else {
677  *destptr++ = 0;
678  }
679  }
680  }
681  break;
682  }
683 }
684 
685 static void remmina_plugin_vnc_rfb_updatefb(rfbClient *cl, int x, int y, int w, int h)
686 {
687  TRACE_CALL(__func__);
688  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
689  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
690  gint bytesPerPixel;
691  gint rowstride;
692  gint width;
693 
694  LOCK_BUFFER(TRUE);
695 
696  if (w >= 1 || h >= 1) {
697  width = remmina_plugin_service->protocol_plugin_get_width(gp);
698  bytesPerPixel = cl->format.bitsPerPixel / 8;
699  rowstride = cairo_image_surface_get_stride(gpdata->rgb_buffer);
700  cairo_surface_flush(gpdata->rgb_buffer);
701  remmina_plugin_vnc_rfb_fill_buffer(cl, cairo_image_surface_get_data(gpdata->rgb_buffer) + y * rowstride + x * 4,
702  rowstride, gpdata->vnc_buffer + ((y * width + x) * bytesPerPixel), width * bytesPerPixel, NULL,
703  w, h);
704  cairo_surface_mark_dirty(gpdata->rgb_buffer);
705  }
706 
708  remmina_plugin_vnc_scale_area(gp, &x, &y, &w, &h);
709 
710  UNLOCK_BUFFER(TRUE);
711 
712  remmina_plugin_vnc_queue_draw_area(gp, x, y, w, h);
713 }
714 
715 static void remmina_plugin_vnc_rfb_finished(rfbClient *cl) __attribute__ ((unused));
716 static void remmina_plugin_vnc_rfb_finished(rfbClient *cl)
717 {
718  TRACE_CALL(__func__);
719  REMMINA_PLUGIN_DEBUG("FinishedFrameBufferUpdate");
720 }
721 
722 static void remmina_plugin_vnc_rfb_led_state(rfbClient *cl, int value, int pad)
723 {
724  TRACE_CALL(__func__);
725  REMMINA_PLUGIN_DEBUG("Led state - value: %d, pad: %d", value, pad);
726 }
727 
729 {
730  TRACE_CALL(__func__);
731  RemminaProtocolWidget *gp = param->gp;
732  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
733  GDateTime *t;
734  glong diff;
735  const char *cur_charset;
736  gchar *text;
737  gsize br, bw;
738 
739  if (GTK_IS_WIDGET(gp) && gpdata->connected) {
740  t = g_date_time_new_now_utc();
741  diff = g_date_time_difference(t, gpdata->clipboard_timer) / 100000; // tenth of second
742  if (diff >= 10) {
743  g_date_time_unref(gpdata->clipboard_timer);
744  gpdata->clipboard_timer = t;
745  /* Convert text from VNC latin-1 to current GTK charset (usually UTF-8) */
746  g_get_charset(&cur_charset);
747  text = g_convert_with_fallback(param->text, param->textlen, cur_charset, "ISO-8859-1", "?", &br, &bw, NULL);
748  gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), text, bw);
749  g_free(text);
750  } else {
751  g_date_time_unref(t);
752  }
753  }
754  g_free(param->text);
755  g_free(param);
756  return FALSE;
757 }
758 
759 static void remmina_plugin_vnc_rfb_cuttext(rfbClient *cl, const char *text, int textlen)
760 {
761  TRACE_CALL(__func__);
763 
764  param = g_new(RemminaPluginVncCuttextParam, 1);
765  param->gp = (RemminaProtocolWidget *)rfbClientGetClientData(cl, NULL);
766  param->text = g_malloc(textlen);
767  memcpy(param->text, text, textlen);
768  param->textlen = textlen;
769  IDLE_ADD((GSourceFunc)remmina_plugin_vnc_queue_cuttext, param);
770 }
771 
772 static char *
774 {
775  TRACE_CALL(__func__);
776  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
777  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
778  RemminaFile *remminafile;
779  gchar *pwd = NULL;
780 
781  gpdata->auth_called = TRUE;
782  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
783 
784  if (gpdata->auth_first)
785  pwd = g_strdup(remmina_plugin_service->file_get_string(remminafile, "password"));
786  if (!pwd) {
787  gboolean save;
788  gint ret;
789  gboolean disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
790  ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
791  (disablepasswordstoring ? 0 : REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD),
792  _("Enter VNC password"),
793  NULL,
794  remmina_plugin_service->file_get_string(remminafile, "password"),
795  NULL,
796  NULL);
797  if (ret != GTK_RESPONSE_OK) {
798  gpdata->connected = FALSE;
799  return NULL;
800  }
801  pwd = remmina_plugin_service->protocol_plugin_init_get_password(gp);
802  save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
803  if (save)
804  remmina_plugin_service->file_set_string(remminafile, "password", pwd);
805  else
806  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
807  }
808  return pwd;
809 }
810 
811 static rfbCredential *
812 remmina_plugin_vnc_rfb_credential(rfbClient *cl, int credentialType)
813 {
814  TRACE_CALL(__func__);
815  rfbCredential *cred;
816  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
817  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
818  RemminaFile *remminafile;
819  gint ret;
820  gchar *s1, *s2;
821  gboolean disablepasswordstoring;
822 
823  gpdata->auth_called = TRUE;
824  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
825 
826  cred = g_new0(rfbCredential, 1);
827 
828  switch (credentialType) {
829  case rfbCredentialTypeUser:
830 
831  s1 = g_strdup(remmina_plugin_service->file_get_string(remminafile, "username"));
832 
833  s2 = g_strdup(remmina_plugin_service->file_get_string(remminafile, "password"));
834 
835  if (gpdata->auth_first && s1 && s2) {
836  cred->userCredential.username = s1;
837  cred->userCredential.password = s2;
838  } else {
839  g_free(s1);
840  g_free(s2);
841 
842  disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
843  ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
845  _("Enter VNC authentication credentials"),
846  remmina_plugin_service->file_get_string(remminafile, "username"),
847  remmina_plugin_service->file_get_string(remminafile, "password"),
848  NULL,
849  NULL);
850  if (ret == GTK_RESPONSE_OK) {
851  gboolean save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
852  cred->userCredential.username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
853  cred->userCredential.password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
854  if (save) {
855  remmina_plugin_service->file_set_string(remminafile, "username", cred->userCredential.username);
856  remmina_plugin_service->file_set_string(remminafile, "password", cred->userCredential.password);
857  } else {
858  remmina_plugin_service->file_set_string(remminafile, "username", NULL);
859  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
860  }
861  } else {
862  g_free(cred);
863  cred = NULL;
864  gpdata->connected = FALSE;
865  }
866  }
867  break;
868 
869  case rfbCredentialTypeX509:
870  if (gpdata->auth_first &&
871  remmina_plugin_service->file_get_string(remminafile, "cacert")) {
872  cred->x509Credential.x509CACertFile = g_strdup(remmina_plugin_service->file_get_string(remminafile, "cacert"));
873  cred->x509Credential.x509CACrlFile = g_strdup(remmina_plugin_service->file_get_string(remminafile, "cacrl"));
874  cred->x509Credential.x509ClientCertFile = g_strdup(remmina_plugin_service->file_get_string(remminafile, "clientcert"));
875  cred->x509Credential.x509ClientKeyFile = g_strdup(remmina_plugin_service->file_get_string(remminafile, "clientkey"));
876  } else {
877  ret = remmina_plugin_service->protocol_plugin_init_authx509(gp);
878 
879  if (ret == GTK_RESPONSE_OK) {
880  cred->x509Credential.x509CACertFile = remmina_plugin_service->protocol_plugin_init_get_cacert(gp);
881  cred->x509Credential.x509CACrlFile = remmina_plugin_service->protocol_plugin_init_get_cacrl(gp);
882  cred->x509Credential.x509ClientCertFile = remmina_plugin_service->protocol_plugin_init_get_clientcert(gp);
883  cred->x509Credential.x509ClientKeyFile = remmina_plugin_service->protocol_plugin_init_get_clientkey(gp);
884  } else {
885  g_free(cred);
886  cred = NULL;
887  gpdata->connected = FALSE;
888  }
889  }
890  break;
891 
892  default:
893  g_free(cred);
894  cred = NULL;
895  break;
896  }
897  return cred;
898 }
899 
900 static void remmina_plugin_vnc_rfb_cursor_shape(rfbClient *cl, int xhot, int yhot, int width, int height, int bytesPerPixel)
901 {
902  TRACE_CALL(__func__);
903  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
904  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
905 
906  if (!gtk_widget_get_window(GTK_WIDGET(gp)))
907  return;
908 
909  if (width && height) {
910  gint stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
911  guchar *data = g_malloc(stride * height);
912  remmina_plugin_vnc_rfb_fill_buffer(cl, data, stride, cl->rcSource,
913  width * cl->format.bitsPerPixel / 8, cl->rcMask, width, height);
914  cairo_surface_t *surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, stride);
915  if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
916  g_free(data);
917  return;
918  }
919  if (cairo_surface_set_user_data(surface, NULL, NULL, g_free) != CAIRO_STATUS_SUCCESS) {
920  g_free(data);
921  return;
922  }
923 
924  LOCK_BUFFER(TRUE);
925  remmina_plugin_vnc_queuecursor(gp, surface, xhot, yhot);
926  UNLOCK_BUFFER(TRUE);
927  }
928 }
929 
930 static void remmina_plugin_vnc_rfb_bell(rfbClient *cl)
931 {
932  TRACE_CALL(__func__);
933  REMMINA_PLUGIN_DEBUG("Bell message received");
935  RemminaFile *remminafile;
936  GdkWindow *window;
937 
938  gp = (RemminaProtocolWidget *)(rfbClientGetClientData(cl, NULL));
939  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
940 
941  if (remmina_plugin_service->file_get_int(remminafile, "disableserverbell", FALSE))
942  return;
943 
944  window = gtk_widget_get_window(GTK_WIDGET(gp));
945 
946  if (window)
947  gdk_window_beep(window);
948  REMMINA_PLUGIN_DEBUG("Beep emitted");
949 }
950 
951 /* Translate known VNC messages. It’s for intltool only, not for gcc */
952 #ifdef __DO_NOT_COMPILE_ME__
953 N_("Unable to connect to VNC server")
954 N_("Couldn’t convert '%s' to host address")
955 N_("VNC connection failed: %s")
956 N_("Your connection has been rejected.")
957 #endif
958 
959 #define MAX_ERROR_LENGTH 1000
960 static gchar vnc_error[MAX_ERROR_LENGTH + 1];
961 static gboolean vnc_encryption_disable_requested;
962 
963 static void remmina_plugin_vnc_rfb_output(const char *format, ...)
964 {
965  TRACE_CALL(__func__);
966  gchar *f, *p, *ff;
967 
968  if (!rfbEnableClientLogging)
969  return;
970 
971  va_list args;
972  va_start(args, format);
973  /* eliminate the last \n */
974  f = g_strdup(format);
975  if (f[strlen(f) - 1] == '\n') f[strlen(f) - 1] = '\0';
976 
977  if (g_strcmp0(f, "VNC connection failed: %s") == 0) {
978  p = va_arg(args, gchar *);
979  g_snprintf(vnc_error, MAX_ERROR_LENGTH, _(f), _(p));
980  } else if (g_strcmp0(f, "The VNC server requested an unknown authentication method. %s") == 0) {
981  p = va_arg(args, gchar *);
982  if (vnc_encryption_disable_requested) {
983  ff = g_strconcat(_("The VNC server requested an unknown authentication method. %s"),
984  ". ",
985  _("Please retry after turning on encryption for this profile."),
986  NULL);
987  g_snprintf(vnc_error, MAX_ERROR_LENGTH, ff, p);
988  g_free(ff);
989  } else {
990  g_snprintf(vnc_error, MAX_ERROR_LENGTH, _(f), p);
991  }
992  } else {
993  g_vsnprintf(vnc_error, MAX_ERROR_LENGTH, _(f), args);
994  }
995  g_free(f);
996  va_end(args);
997 
998  REMMINA_PLUGIN_DEBUG("VNC returned: %s", vnc_error);
999 }
1000 
1002 {
1003  TRACE_CALL(__func__);
1004  gchar *ptr;
1005 
1006  /* Need to add a line-feed for UltraVNC */
1007  ptr = g_strdup_printf("%s\n", text);
1009  g_free(ptr);
1010 }
1011 
1013 {
1014  TRACE_CALL(__func__);
1016 }
1017 
1018 /* Send CTRL+ALT+DEL keys keystrokes to the plugin drawing_area widget */
1020 {
1021  TRACE_CALL(__func__);
1022  guint keys[] = { GDK_KEY_Control_L, GDK_KEY_Alt_L, GDK_KEY_Delete };
1023  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1024 
1025  remmina_plugin_service->protocol_plugin_send_keys_signals(gpdata->drawing_area,
1026  keys, G_N_ELEMENTS(keys), GDK_KEY_PRESS | GDK_KEY_RELEASE);
1027 }
1028 
1030 {
1031  TRACE_CALL(__func__);
1032  remmina_plugin_service->protocol_plugin_chat_close(gp);
1033  return FALSE;
1034 }
1035 
1037 {
1038  TRACE_CALL(__func__);
1039  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1040  rfbClient *cl;
1041 
1042  cl = (rfbClient *)gpdata->client;
1043 
1044  remmina_plugin_service->protocol_plugin_chat_open(gp, cl->desktopName, remmina_plugin_vnc_chat_on_send,
1047  return FALSE;
1048 }
1049 
1050 static void remmina_plugin_vnc_rfb_chat(rfbClient *cl, int value, char *text)
1051 {
1052  TRACE_CALL(__func__);
1054 
1055  gp = (RemminaProtocolWidget *)(rfbClientGetClientData(cl, NULL));
1056  switch (value) {
1057  case rfbTextChatOpen:
1058  IDLE_ADD((GSourceFunc)remmina_plugin_vnc_open_chat, gp);
1059  break;
1060  case rfbTextChatClose:
1061  /* Do nothing… but wait for the next rfbTextChatFinished signal */
1062  break;
1063  case rfbTextChatFinished:
1064  IDLE_ADD((GSourceFunc)remmina_plugin_vnc_close_chat, gp);
1065  break;
1066  default:
1067  /* value is the text length */
1068  remmina_plugin_service->protocol_plugin_chat_receive(gp, text);
1069  break;
1070  }
1071 }
1072 
1074 {
1075  TRACE_CALL(__func__);
1076  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1077  fd_set fds;
1078 
1083  gpdata->listen_sock = ListenAtTcpPort(cl->listenPort);
1084  if (gpdata->listen_sock < 0)
1085  return FALSE;
1086 
1087  remmina_plugin_service->protocol_plugin_init_show_listen(gp, cl->listenPort);
1088 
1089  remmina_plugin_service->protocol_plugin_start_reverse_tunnel(gp, cl->listenPort);
1090 
1091  FD_ZERO(&fds);
1092  if (gpdata->listen_sock >= 0)
1093  FD_SET(gpdata->listen_sock, &fds);
1094 
1095  select(gpdata->listen_sock + 1, &fds, NULL, NULL, NULL);
1096 
1097  if (!FD_ISSET(gpdata->listen_sock, &fds)) {
1098  close(gpdata->listen_sock);
1099  gpdata->listen_sock = -1;
1100  return FALSE;
1101  }
1102 
1103  if (FD_ISSET(gpdata->listen_sock, &fds))
1104  cl->sock = AcceptTcpConnection(gpdata->listen_sock);
1105  if (cl->sock >= 0) {
1106  close(gpdata->listen_sock);
1107  gpdata->listen_sock = -1;
1108  }
1109  if (cl->sock < 0 || !SetNonBlocking(cl->sock))
1110  return FALSE;
1111 
1112  return TRUE;
1113 }
1114 
1115 
1117 {
1118  TRACE_CALL(__func__);
1119  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1120  gint ret;
1121  gint i;
1122  rfbClient *cl;
1123  fd_set fds;
1124  struct timeval timeout;
1125 
1126  if (!gpdata->connected) {
1127  gpdata->running = FALSE;
1128  return FALSE;
1129  }
1130 
1131  cl = (rfbClient *)gpdata->client;
1132 
1133  /*
1134  * Do not explicitly wait while data is on the buffer, see:
1135  * - https://jira.glyptodon.com/browse/GUAC-1056
1136  * - https://jira.glyptodon.com/browse/GUAC-1056?focusedCommentId=14348&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-14348
1137  * - https://github.com/apache/guacamole-server/blob/67680bd2d51e7949453f0f7ffc7f4234a1136715/src/protocols/vnc/vnc.c#L155
1138  */
1139  if (cl->buffered)
1140  goto handle_buffered;
1141 
1142  timeout.tv_sec = 10;
1143  timeout.tv_usec = 0;
1144  FD_ZERO(&fds);
1145  FD_SET(cl->sock, &fds);
1146  FD_SET(gpdata->vnc_event_pipe[0], &fds);
1147  ret = select(MAX(cl->sock, gpdata->vnc_event_pipe[0]) + 1, &fds, NULL, NULL, &timeout);
1148 
1149  /* Sometimes it returns <0 when opening a modal dialog in other window. Absolutely weird */
1150  /* So we continue looping anyway */
1151  if (ret <= 0)
1152  return TRUE;
1153 
1154  if (FD_ISSET(gpdata->vnc_event_pipe[0], &fds))
1156  if (FD_ISSET(cl->sock, &fds)) {
1157  i = WaitForMessage(cl, 500);
1158  if (i < 0)
1159  return TRUE;
1160 handle_buffered:
1161  if (!HandleRFBServerMessage(cl)) {
1162  gpdata->running = FALSE;
1163  if (gpdata->connected && !remmina_plugin_service->protocol_plugin_is_closed(gp))
1164  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
1165  return FALSE;
1166  }
1167  }
1168 
1169  return TRUE;
1170 }
1171 
1173 {
1174  TRACE_CALL(__func__);
1175  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1176  RemminaFile *remminafile;
1177  rfbClient *cl = NULL;
1178  gchar *host;
1179  gchar *s = NULL;
1180 
1181  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1182  gpdata->running = TRUE;
1183 
1184  rfbClientLog = rfbClientErr = remmina_plugin_vnc_rfb_output;
1185 
1186  gint colordepth = remmina_plugin_service->file_get_int(remminafile, "colordepth", 32);
1187  gint quality = remmina_plugin_service->file_get_int(remminafile, "quality", 9);
1188 
1189  while (gpdata->connected) {
1190  gpdata->auth_called = FALSE;
1191 
1192  host = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, VNC_DEFAULT_PORT, TRUE);
1193 
1194  if (host == NULL) {
1195  REMMINA_PLUGIN_DEBUG("host is null");
1196  gpdata->connected = FALSE;
1197  break;
1198  }
1199 
1200  /* int bitsPerSample,int samplesPerPixel, int bytesPerPixel */
1201  switch (colordepth) {
1202  case 8:
1203  cl = rfbGetClient(2, 3, 1);
1204  break;
1205  case 15:
1206  case 16:
1207  cl = rfbGetClient(5, 3, 2);
1208  break;
1209  case 24:
1210  cl = rfbGetClient(6, 3, 3);
1211  break;
1212  case 32:
1213  default:
1214  cl = rfbGetClient(8, 3, 4);
1215  break;
1216  }
1217  REMMINA_PLUGIN_DEBUG("Color depth: %d", colordepth);
1218  cl->MallocFrameBuffer = remmina_plugin_vnc_rfb_allocfb;
1219  cl->canHandleNewFBSize = TRUE;
1220  cl->GetPassword = remmina_plugin_vnc_rfb_password;
1221  cl->GetCredential = remmina_plugin_vnc_rfb_credential;
1222  cl->GotFrameBufferUpdate = remmina_plugin_vnc_rfb_updatefb;
1233  cl->HandleKeyboardLedState = remmina_plugin_vnc_rfb_led_state;
1234  cl->GotXCutText = (
1235  remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE) ?
1237  cl->GotCursorShape = remmina_plugin_vnc_rfb_cursor_shape;
1238  cl->Bell = remmina_plugin_vnc_rfb_bell;
1239  cl->HandleTextChat = remmina_plugin_vnc_rfb_chat;
1245  rfbClientSetClientData(cl, NULL, gp);
1246 
1247  if (host[0] == '\0') {
1248  cl->serverHost = g_strdup(host);
1249  cl->listenSpecified = TRUE;
1250  if (remmina_plugin_service->file_get_int(remminafile, "ssh_tunnel_enabled", FALSE))
1251  /* When we use reverse tunnel, the local port does not really matter.
1252  * Hardcode a default port just in case the remote port is customized
1253  * to a privilege port then we will have problem listening. */
1254  cl->listenPort = 5500;
1255  else
1256  cl->listenPort = remmina_plugin_service->file_get_int(remminafile, "listenport", 5500);
1257 
1259  } else {
1260  if (strstr(host, "unix://") == host) {
1261  cl->serverHost = g_strdup(host + strlen("unix://"));
1262  cl->serverPort = 0;
1263  } else {
1264  remmina_plugin_service->get_server_port(host, VNC_DEFAULT_PORT, &s, &cl->serverPort);
1265  cl->serverHost = g_strdup(s);
1266  g_free(s);
1267  /* Support short-form (:0, :1) */
1268  if (cl->serverPort < 100)
1269  cl->serverPort += VNC_DEFAULT_PORT;
1270  }
1271  }
1272  g_free(host);
1273  host = NULL;
1274 
1275  if (cl->serverHost && strstr(cl->serverHost, "unix://") != cl->serverHost && remmina_plugin_service->file_get_string(remminafile, "proxy")) {
1276  remmina_plugin_service->get_server_port(
1277  remmina_plugin_service->file_get_string(remminafile, "server"),
1278  VNC_DEFAULT_PORT,
1279  &cl->destHost,
1280  &cl->destPort);
1281  remmina_plugin_service->get_server_port(
1282  remmina_plugin_service->file_get_string(remminafile, "proxy"),
1283  VNC_DEFAULT_PORT,
1284  &cl->serverHost,
1285  &cl->serverPort);
1286  REMMINA_PLUGIN_DEBUG("cl->serverHost: %s", cl->serverHost);
1287  REMMINA_PLUGIN_DEBUG("cl->serverPort: %d", cl->serverPort);
1288  REMMINA_PLUGIN_DEBUG("cl->destHost: %s", cl->destHost);
1289  REMMINA_PLUGIN_DEBUG("cl->destPort: %d", cl->destPort);
1290  }
1291 
1292  cl->appData.useRemoteCursor = (
1293  remmina_plugin_service->file_get_int(remminafile, "showcursor", FALSE) ? FALSE : TRUE);
1294 
1295  remmina_plugin_vnc_update_quality(cl, quality);
1296  remmina_plugin_vnc_update_colordepth(cl, colordepth);
1297  if ((cl->format.depth == 8) && (quality == 9))
1298  cl->appData.encodingsString = "copyrect zlib hextile raw";
1299  else if ((cl->format.depth == 8) && (quality == 2))
1300  cl->appData.encodingsString = "zrle ultra copyrect hextile zlib corre rre raw";
1301  else if ((cl->format.depth == 8) && (quality == 1))
1302  cl->appData.encodingsString = "zrle ultra copyrect hextile zlib corre rre raw";
1303  else if ((cl->format.depth == 8) && (quality == 0))
1304  cl->appData.encodingsString = "zrle ultra copyrect hextile zlib corre rre raw";
1305  SetFormatAndEncodings(cl);
1306 
1307  if (remmina_plugin_service->file_get_int(remminafile, "disableencryption", FALSE)) {
1308  vnc_encryption_disable_requested = TRUE;
1309  SetClientAuthSchemes(cl, remmina_plugin_vnc_no_encrypt_auth_types, -1);
1310  } else {
1311  vnc_encryption_disable_requested = FALSE;
1312  }
1313 
1314  if (rfbInitClient(cl, NULL, NULL)) {
1315  REMMINA_PLUGIN_DEBUG("Client initialization successfull");
1316  break;
1317  } else {
1318  REMMINA_PLUGIN_DEBUG("Client initialization failed");
1319  }
1320 
1321  /* If the authentication is not called, it has to be a fatal error and must quit */
1322  if (!gpdata->auth_called) {
1323  REMMINA_PLUGIN_DEBUG("Client not authenticated");
1324  gpdata->connected = FALSE;
1325  break;
1326  }
1327 
1328  /* vnc4server reports "already in use" after authentication. Workaround here */
1329  if (strstr(vnc_error, "The server is already in use")) {
1330  gpdata->connected = FALSE;
1331  gpdata->auth_called = FALSE;
1332  break;
1333  }
1334  /* Don't assume authentication failed for known network-related errors in
1335  libvncclient/sockets.c. */
1336  if (strstr(vnc_error, "read (") || strstr(vnc_error, "select\n") ||
1337  strstr(vnc_error, "write\n") || strstr(vnc_error, "Connection timed out")) {
1338  gpdata->connected = FALSE;
1339  gpdata->auth_called = FALSE;
1340  break;
1341  }
1342 
1343  /* Otherwise, it’s a password error. Try to clear saved password if any */
1344  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
1345 
1346  if (!gpdata->connected)
1347  break;
1348 
1349  remmina_plugin_service->protocol_plugin_init_show_retry(gp);
1350 
1351  /* It’s safer to sleep a while before reconnect */
1352  sleep(2);
1353 
1354  gpdata->auth_first = FALSE;
1355  }
1356 
1357  if (!gpdata->connected) {
1358  REMMINA_PLUGIN_DEBUG("Client not connected with error: %s", vnc_error);
1359  if (cl && !gpdata->auth_called && !(remmina_plugin_service->protocol_plugin_has_error(gp)))
1360  remmina_plugin_service->protocol_plugin_set_error(gp, "%s", vnc_error);
1361  gpdata->running = FALSE;
1362 
1363  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
1364 
1365  return FALSE;
1366  }
1367 
1368  REMMINA_PLUGIN_DEBUG("Client connected");
1369  remmina_plugin_service->protocol_plugin_init_save_cred(gp);
1370 
1371  gpdata->client = cl;
1372 
1373  remmina_plugin_service->protocol_plugin_signal_connection_opened(gp);
1374 
1375  if (remmina_plugin_service->file_get_int(remminafile, "disableserverinput", FALSE))
1376  PermitServerInput(cl, 1);
1377 
1378  if (gpdata->thread) {
1379  while (remmina_plugin_vnc_main_loop(gp)) {
1380  }
1381  gpdata->running = FALSE;
1382  } else {
1383  IDLE_ADD((GSourceFunc)remmina_plugin_vnc_main_loop, gp);
1384  }
1385 
1386  return FALSE;
1387 }
1388 
1389 
1390 static gpointer
1392 {
1393  TRACE_CALL(__func__);
1394  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
1395 
1396  CANCEL_ASYNC
1398  return NULL;
1399 }
1400 
1401 
1403 {
1404  GtkAllocation widget_allocation;
1406 
1408  gtk_widget_get_allocation(widget, &widget_allocation);
1409  result.x = x * remmina_plugin_service->protocol_plugin_get_width(gp) / widget_allocation.width;
1410  result.y = y * remmina_plugin_service->protocol_plugin_get_height(gp) / widget_allocation.height;
1411  } else {
1412  result.x = x;
1413  result.y = y;
1414  }
1415 
1416  return result;
1417 }
1418 
1419 static gboolean remmina_plugin_vnc_on_motion(GtkWidget *widget, GdkEventMotion *event, RemminaProtocolWidget *gp)
1420 {
1421  TRACE_CALL(__func__);
1422  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1423  RemminaFile *remminafile;
1424  RemminaPluginVncCoordinates coordinates;
1425 
1426  if (!gpdata->connected || !gpdata->client)
1427  return FALSE;
1428  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1429  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1430  return FALSE;
1431 
1432  coordinates = remmina_plugin_vnc_scale_coordinates(widget, gp, event->x, event->y);
1433  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_POINTER, GINT_TO_POINTER(coordinates.x), GINT_TO_POINTER(coordinates.y),
1434  GINT_TO_POINTER(gpdata->button_mask));
1435  return TRUE;
1436 }
1437 
1438 static gboolean remmina_plugin_vnc_on_button(GtkWidget *widget, GdkEventButton *event, RemminaProtocolWidget *gp)
1439 {
1440  TRACE_CALL(__func__);
1441  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1442  RemminaFile *remminafile;
1443  RemminaPluginVncCoordinates coordinates;
1444  gint mask;
1445 
1446  if (!gpdata->connected || !gpdata->client)
1447  return FALSE;
1448  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1449  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1450  return FALSE;
1451 
1452  /* We only accept 3 buttons */
1453  if (event->button < 1 || event->button > 3)
1454  return FALSE;
1455  /* We bypass 2button-press and 3button-press events */
1456  if (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE)
1457  return TRUE;
1458 
1459  mask = (1 << (event->button - 1));
1460  gpdata->button_mask = (event->type == GDK_BUTTON_PRESS ? (gpdata->button_mask | mask) :
1461  (gpdata->button_mask & (0xff - mask)));
1462 
1463  coordinates = remmina_plugin_vnc_scale_coordinates(widget, gp, event->x, event->y);
1464  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_POINTER, GINT_TO_POINTER(coordinates.x), GINT_TO_POINTER(coordinates.y),
1465  GINT_TO_POINTER(gpdata->button_mask));
1466  return TRUE;
1467 }
1468 
1469 static gint delta_to_mask(float delta, float *accum, gint mask_plus, gint mask_minus)
1470 {
1471  *accum += delta;
1472  if (*accum >= 1.0) {
1473  *accum = 0.0;
1474  return mask_plus;
1475  } else if (*accum <= -1.0) {
1476  *accum = 0.0;
1477  return mask_minus;
1478  }
1479  return 0;
1480 }
1481 
1482 static gboolean remmina_plugin_vnc_on_scroll(GtkWidget *widget, GdkEventScroll *event, RemminaProtocolWidget *gp)
1483 {
1484  TRACE_CALL(__func__);
1485  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1486  RemminaFile *remminafile;
1487  RemminaPluginVncCoordinates coordinates;
1488  gint mask;
1489 
1490  if (!gpdata->connected || !gpdata->client)
1491  return FALSE;
1492  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1493  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1494  return FALSE;
1495 
1496  switch (event->direction) {
1497  case GDK_SCROLL_UP:
1498  mask = (1 << 3);
1499  gpdata->scroll_y_accumulator = 0;
1500  break;
1501  case GDK_SCROLL_DOWN:
1502  mask = (1 << 4);
1503  gpdata->scroll_y_accumulator = 0;
1504  break;
1505  case GDK_SCROLL_LEFT:
1506  mask = (1 << 5);
1507  gpdata->scroll_x_accumulator = 0;
1508  break;
1509  case GDK_SCROLL_RIGHT:
1510  mask = (1 << 6);
1511  gpdata->scroll_x_accumulator = 0;
1512  break;
1513 #if GTK_CHECK_VERSION(3, 4, 0)
1514  case GDK_SCROLL_SMOOTH:
1515  /* RFB does not seems to support SMOOTH scroll, so we accumulate GTK delta requested
1516  * up to 1.0 and then send a normal RFB wheel scroll when the accumulator reaches 1.0 */
1517  mask = delta_to_mask(event->delta_y, &(gpdata->scroll_y_accumulator), (1 << 4), (1 << 3));
1518  mask |= delta_to_mask(event->delta_x, &(gpdata->scroll_x_accumulator), (1 << 6), (1 << 5));
1519  if (!mask)
1520  return FALSE;
1521  break;
1522 #endif
1523  default:
1524  return FALSE;
1525  }
1526 
1527  coordinates = remmina_plugin_vnc_scale_coordinates(widget, gp, event->x, event->y);
1528  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_POINTER, GINT_TO_POINTER(coordinates.x), GINT_TO_POINTER(coordinates.y),
1529  GINT_TO_POINTER(mask | gpdata->button_mask));
1530  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_POINTER, GINT_TO_POINTER(coordinates.x), GINT_TO_POINTER(coordinates.y),
1531  GINT_TO_POINTER(gpdata->button_mask));
1532 
1533  return TRUE;
1534 }
1535 
1537 {
1538  TRACE_CALL(__func__);
1539  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1540  RemminaKeyVal *k;
1541  gint i;
1542 
1543  if (!gpdata)
1544  return;
1545 
1546  if (keycode == 0) {
1547  /* Send all release key events for previously pressed keys */
1548  for (i = 0; i < gpdata->pressed_keys->len; i++) {
1549  k = g_ptr_array_index(gpdata->pressed_keys, i);
1551  GINT_TO_POINTER(FALSE), NULL);
1552  g_free(k);
1553  }
1554  g_ptr_array_set_size(gpdata->pressed_keys, 0);
1555  } else {
1556  /* Unregister the keycode only */
1557  for (i = 0; i < gpdata->pressed_keys->len; i++) {
1558  k = g_ptr_array_index(gpdata->pressed_keys, i);
1559  if (k->keycode == keycode) {
1560  g_free(k);
1561  g_ptr_array_remove_index_fast(gpdata->pressed_keys, i);
1562  break;
1563  }
1564  }
1565  }
1566 }
1567 
1568 static gboolean remmina_plugin_vnc_on_key(GtkWidget *widget, GdkEventKey *event, RemminaProtocolWidget *gp)
1569 {
1570  TRACE_CALL(__func__);
1571  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1572  RemminaFile *remminafile;
1573  RemminaKeyVal *k;
1574  guint event_keyval;
1575  guint keyval;
1576 
1577  if (!gpdata->connected || !gpdata->client)
1578  return FALSE;
1579  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1580  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1581  return FALSE;
1582 
1583  gpdata->scroll_x_accumulator = 0;
1584  gpdata->scroll_y_accumulator = 0;
1585 
1586  /* When sending key release, try first to find out a previously sent keyval
1587  * to workaround bugs like https://bugs.freedesktop.org/show_bug.cgi?id=7430 */
1588 
1589  event_keyval = event->keyval;
1590  if (event->type == GDK_KEY_RELEASE) {
1591  for (int i = 0; i < gpdata->pressed_keys->len; i++) {
1592  k = g_ptr_array_index(gpdata->pressed_keys, i);
1593  if (k->keycode == event->hardware_keycode) {
1594  event_keyval = k->keyval;
1595  break;
1596  }
1597  }
1598  }
1599 
1600  keyval = remmina_plugin_service->pref_keymap_get_keyval(remmina_plugin_service->file_get_string(remminafile, "keymap"),
1601  event_keyval);
1602 
1603  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_KEY, GUINT_TO_POINTER(keyval),
1604  GINT_TO_POINTER(event->type == GDK_KEY_PRESS ? TRUE : FALSE), NULL);
1605 
1606  /* Register/unregister the pressed key */
1607  if (event->type == GDK_KEY_PRESS) {
1608  k = g_new(RemminaKeyVal, 1);
1609  k->keyval = keyval;
1610  k->keycode = event->hardware_keycode;
1611  g_ptr_array_add(gpdata->pressed_keys, k);
1612  } else {
1613  remmina_plugin_vnc_release_key(gp, event->hardware_keycode);
1614  }
1615  return TRUE;
1616 }
1617 
1618 static void remmina_plugin_vnc_on_cuttext_request(GtkClipboard *clipboard, const gchar *text, RemminaProtocolWidget *gp)
1619 {
1620  TRACE_CALL(__func__);
1621  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1622  GDateTime *t;
1623  glong diff;
1624  gsize br, bw;
1625  gchar *latin1_text;
1626  const char *cur_charset;
1627 
1628  if (text) {
1629  /* A timer (1 second) to avoid clipboard "loopback": text cut out from VNC won’t paste back into VNC */
1630  t = g_date_time_new_now_utc();
1631  diff = g_date_time_difference(t, gpdata->clipboard_timer) / 100000; // tenth of second
1632  if (diff < 10)
1633  return;
1634  g_date_time_unref(gpdata->clipboard_timer);
1635  gpdata->clipboard_timer = t;
1636  /* Convert text from current charset to latin-1 before sending to remote server.
1637  * See RFC6143 7.5.6 */
1638  g_get_charset(&cur_charset);
1639  latin1_text = g_convert_with_fallback(text, -1, "ISO-8859-1", cur_charset, "?", &br, &bw, NULL);
1640  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_CUTTEXT, (gpointer)latin1_text, NULL, NULL);
1641  g_free(latin1_text);
1642  }
1643 }
1644 
1645 static void remmina_plugin_vnc_on_cuttext(GtkClipboard *clipboard, GdkEvent *event, RemminaProtocolWidget *gp)
1646 {
1647  TRACE_CALL(__func__);
1648  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1649  RemminaFile *remminafile;
1650 
1651  if (!gpdata->connected || !gpdata->client)
1652  return;
1653  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1654  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1655  return;
1656 
1657  gtk_clipboard_request_text(clipboard, (GtkClipboardTextReceivedFunc)remmina_plugin_vnc_on_cuttext_request, gp);
1658 }
1659 
1661 {
1662  TRACE_CALL(__func__);
1663  RemminaFile *remminafile;
1664  GdkCursor *cursor;
1665  GdkPixbuf *pixbuf;
1666 
1667  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1668 
1669  if (remmina_plugin_service->file_get_int(remminafile, "showcursor", FALSE)) {
1670  /* Hide local cursor (show a small dot instead) */
1671  pixbuf = gdk_pixbuf_new_from_xpm_data(dot_cursor_xpm);
1672  cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), pixbuf, dot_cursor_x_hot, dot_cursor_y_hot);
1673  g_object_unref(pixbuf);
1674  gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(gp)), cursor);
1675  g_object_unref(cursor);
1676  }
1677 }
1678 
1679 /******************************************************************************************/
1680 
1682 {
1683  TRACE_CALL(__func__);
1684  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1685  RemminaFile *remminafile;
1686 
1687  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1688 
1689  gpdata->connected = TRUE;
1690  gchar *server;
1691  gint port;
1692  const gchar* raw_server;
1693 
1694  remmina_plugin_service->protocol_plugin_register_hostkey(gp, gpdata->drawing_area);
1695 
1696  g_signal_connect(G_OBJECT(gp), "realize", G_CALLBACK(remmina_plugin_vnc_on_realize), NULL);
1697  g_signal_connect(G_OBJECT(gpdata->drawing_area), "motion-notify-event", G_CALLBACK(remmina_plugin_vnc_on_motion), gp);
1698  g_signal_connect(G_OBJECT(gpdata->drawing_area), "button-press-event", G_CALLBACK(remmina_plugin_vnc_on_button), gp);
1699  g_signal_connect(G_OBJECT(gpdata->drawing_area), "button-release-event", G_CALLBACK(remmina_plugin_vnc_on_button), gp);
1700  g_signal_connect(G_OBJECT(gpdata->drawing_area), "scroll-event", G_CALLBACK(remmina_plugin_vnc_on_scroll), gp);
1701  g_signal_connect(G_OBJECT(gpdata->drawing_area), "key-press-event", G_CALLBACK(remmina_plugin_vnc_on_key), gp);
1702  g_signal_connect(G_OBJECT(gpdata->drawing_area), "key-release-event", G_CALLBACK(remmina_plugin_vnc_on_key), gp);
1703 
1704  if (!remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE))
1705  gpdata->clipboard_handler = g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)),
1706  "owner-change", G_CALLBACK(remmina_plugin_vnc_on_cuttext), gp);
1707 
1708 
1709  if (pthread_create(&gpdata->thread, NULL, remmina_plugin_vnc_main_thread, gp)) {
1710  /* I don’t think this will ever happen… */
1711  g_print("Could not initialize pthread. Falling back to non-thread mode…\n");
1712  g_timeout_add(0, (GSourceFunc)remmina_plugin_vnc_main, gp);
1713  gpdata->thread = 0;
1714  }
1715 
1716  raw_server = remmina_plugin_service->file_get_string(remminafile, "server");
1717 
1718  if (raw_server && strstr(raw_server, "unix://") == raw_server) {
1719  REMMINA_PLUGIN_AUDIT(_("Connected to %s via VNC"), raw_server);
1720  } else {
1721  remmina_plugin_service->get_server_port(raw_server,
1722  VNC_DEFAULT_PORT,
1723  &server,
1724  &port);
1725 
1726  REMMINA_PLUGIN_AUDIT(_("Connected to %s:%d via VNC"), server, port);
1727  g_free(server), server = NULL;
1728  }
1729 #if LIBVNCSERVER_CHECK_VERSION_VERSION(0, 9, 14)
1730  remmina_plugin_service->protocol_plugin_unlock_dynres(gp);
1731 #endif
1732  return TRUE;
1733 }
1734 
1736 {
1737  TRACE_CALL(__func__);
1738  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1739 
1740  gchar *server;
1741  gint port;
1742 
1743  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1744  remmina_plugin_service->get_server_port(remmina_plugin_service->file_get_string(remminafile, "server"),
1745  VNC_DEFAULT_PORT,
1746  &server,
1747  &port);
1748 
1749  REMMINA_PLUGIN_AUDIT(_("Disconnected from %s:%d via VNC"), server, port);
1750  g_free(server), server = NULL;
1751 
1752  /* wait until the running attribute is set to false by the VNC thread */
1753  if (gpdata->running)
1754  return TRUE;
1755 
1756  /* unregister the clipboard monitor */
1757  if (gpdata->clipboard_handler) {
1758  g_signal_handler_disconnect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)), gpdata->clipboard_handler);
1759  gpdata->clipboard_handler = 0;
1760  }
1761 
1762  if (gpdata->queuecursor_handler) {
1763  g_source_remove(gpdata->queuecursor_handler);
1764  gpdata->queuecursor_handler = 0;
1765  }
1766  if (gpdata->queuecursor_surface) {
1767  cairo_surface_destroy(gpdata->queuecursor_surface);
1768  gpdata->queuecursor_surface = NULL;
1769  }
1770 
1771  if (gpdata->queuedraw_handler) {
1772  g_source_remove(gpdata->queuedraw_handler);
1773  gpdata->queuedraw_handler = 0;
1774  }
1775  if (gpdata->listen_sock >= 0)
1776  close(gpdata->listen_sock);
1777  if (gpdata->client) {
1778  rfbClientCleanup((rfbClient *)gpdata->client);
1779  gpdata->client = NULL;
1780  }
1781  if (gpdata->rgb_buffer) {
1782  cairo_surface_destroy(gpdata->rgb_buffer);
1783  gpdata->rgb_buffer = NULL;
1784  }
1785  if (gpdata->vnc_buffer) {
1786  g_free(gpdata->vnc_buffer);
1787  gpdata->vnc_buffer = NULL;
1788  }
1789  g_ptr_array_free(gpdata->pressed_keys, TRUE);
1790  g_date_time_unref(gpdata->clipboard_timer);
1792  g_queue_free(gpdata->vnc_event_queue);
1793  pthread_mutex_destroy(&gpdata->vnc_event_queue_mutex);
1794  close(gpdata->vnc_event_pipe[0]);
1795  close(gpdata->vnc_event_pipe[1]);
1796 
1797 
1798  pthread_mutex_destroy(&gpdata->buffer_mutex);
1799  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
1800 
1801  return FALSE;
1802 }
1803 
1805 {
1806  TRACE_CALL(__func__);
1807  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1808 
1809  gpdata->connected = FALSE;
1810 
1811  if (gpdata->thread) {
1812  pthread_cancel(gpdata->thread);
1813  if (gpdata->thread) pthread_join(gpdata->thread, NULL);
1814  gpdata->running = FALSE;
1816  } else {
1817  g_timeout_add(200, (GSourceFunc)remmina_plugin_vnc_close_connection_timeout, gp);
1818  }
1819 
1820  return FALSE;
1821 }
1822 
1824 {
1825  TRACE_CALL(__func__);
1826  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1827  switch (feature->id) {
1828  case REMMINA_PLUGIN_VNC_FEATURE_PREF_DISABLESERVERINPUT:
1829  return SupportsClient2Server((rfbClient *)(gpdata->client), rfbSetServerInput) ? TRUE : FALSE;
1830  case REMMINA_PLUGIN_VNC_FEATURE_TOOL_CHAT:
1831  return SupportsClient2Server((rfbClient *)(gpdata->client), rfbTextChat) ? TRUE : FALSE;
1832  default:
1833  return TRUE;
1834  }
1835 }
1836 
1838 {
1839  TRACE_CALL(__func__);
1840  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1841  RemminaFile *remminafile;
1842  rfbClient* client;
1843  uint8_t previous_bpp;
1844  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1845  switch (feature->id) {
1846  case REMMINA_PLUGIN_VNC_FEATURE_PREF_QUALITY:
1847  remmina_plugin_vnc_update_quality((rfbClient *)(gpdata->client),
1848  remmina_plugin_service->file_get_int(remminafile, "quality", 9));
1849  remmina_plugin_vnc_update_colordepth((rfbClient *)(gpdata->client),
1850  remmina_plugin_service->file_get_int(remminafile, "colordepth", 32));
1851  SetFormatAndEncodings((rfbClient *)(gpdata->client));
1852  break;
1853  case REMMINA_PLUGIN_VNC_FEATURE_PREF_COLOR:
1854  client = (rfbClient *)(gpdata->client);
1855  previous_bpp = client->format.bitsPerPixel;
1857  remmina_plugin_service->file_get_int(remminafile, "colordepth", 32));
1858  SetFormatAndEncodings(client);
1859  //Need to clear away old and reallocate if we're increasing bpp
1860  if (client->format.bitsPerPixel > previous_bpp){
1861  remmina_plugin_vnc_rfb_allocfb((rfbClient *)(gpdata->client));
1862  SendFramebufferUpdateRequest((rfbClient *)(gpdata->client), 0, 0,
1863  remmina_plugin_service->protocol_plugin_get_width(gp),
1864  remmina_plugin_service->protocol_plugin_get_height(gp), FALSE);
1865  }
1866  break;
1867  case REMMINA_PLUGIN_VNC_FEATURE_PREF_VIEWONLY:
1868  break;
1869  case REMMINA_PLUGIN_VNC_FEATURE_PREF_DISABLESERVERINPUT:
1870  PermitServerInput((rfbClient *)(gpdata->client),
1871  remmina_plugin_service->file_get_int(remminafile, "disableserverinput", FALSE) ? 1 : 0);
1872  break;
1873  case REMMINA_PLUGIN_VNC_FEATURE_UNFOCUS:
1875  break;
1876  case REMMINA_PLUGIN_VNC_FEATURE_SCALE:
1877  remmina_plugin_vnc_update_scale(gp, remmina_plugin_service->file_get_int(remminafile, "scale", FALSE));
1878  break;
1879  case REMMINA_PLUGIN_VNC_FEATURE_TOOL_REFRESH:
1880  SendFramebufferUpdateRequest((rfbClient *)(gpdata->client), 0, 0,
1881  remmina_plugin_service->protocol_plugin_get_width(gp),
1882  remmina_plugin_service->protocol_plugin_get_height(gp), FALSE);
1883  break;
1884  case REMMINA_PLUGIN_VNC_FEATURE_TOOL_CHAT:
1886  break;
1887  case REMMINA_PLUGIN_VNC_FEATURE_TOOL_SENDCTRLALTDEL:
1889  break;
1890  default:
1891  break;
1892  }
1893 }
1894 
1895 /* Send a keystroke to the plugin window */
1896 static void remmina_plugin_vnc_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
1897 {
1898  TRACE_CALL(__func__);
1899  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1900 
1901  remmina_plugin_service->protocol_plugin_send_keys_signals(gpdata->drawing_area,
1902  keystrokes, keylen, GDK_KEY_PRESS | GDK_KEY_RELEASE);
1903  return;
1904 }
1905 
1906 #if LIBVNCSERVER_CHECK_VERSION_VERSION(0, 9, 14)
1907 static gboolean remmina_plugin_vnc_on_size_allocate(GtkWidget *widget, GtkAllocation *alloc, RemminaProtocolWidget *gp)
1908 {
1909  TRACE_CALL(__func__);
1910  RemminaScaleMode scale_mode = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
1911  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1912 
1913  if (scale_mode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES){
1914  char str[1024];
1915  sprintf(str, "DEBUG: %d x %d", alloc->width, alloc->height);
1916  TRACE_CALL(str);
1917  if (gpdata->client){
1918  SendExtDesktopSize(gpdata->client, alloc->width, alloc->height);
1919  }
1920  }
1921  return TRUE;
1922 }
1923 #endif
1924 
1925 static gboolean remmina_plugin_vnc_on_draw(GtkWidget *widget, cairo_t *context, RemminaProtocolWidget *gp)
1926 {
1927  TRACE_CALL(__func__);
1928  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1929  cairo_surface_t *surface;
1930  gint width, height;
1931  GtkAllocation widget_allocation;
1932 
1933  LOCK_BUFFER(FALSE);
1934 
1935  surface = gpdata->rgb_buffer;
1936  if (!surface) {
1937  UNLOCK_BUFFER(FALSE);
1938  return FALSE;
1939  }
1940 
1941  width = remmina_plugin_service->protocol_plugin_get_width(gp);
1942  height = remmina_plugin_service->protocol_plugin_get_height(gp);
1943 
1945  gtk_widget_get_allocation(widget, &widget_allocation);
1946  cairo_scale(context,
1947  (double)widget_allocation.width / width,
1948  (double)widget_allocation.height / height);
1949  }
1950 
1951  cairo_rectangle(context, 0, 0, width, height);
1952  cairo_set_source_surface(context, surface, 0, 0);
1953  cairo_fill(context);
1954 
1955  UNLOCK_BUFFER(FALSE);
1956  return TRUE;
1957 }
1958 
1960 {
1961  TRACE_CALL(__func__);
1962  RemminaPluginVncData *gpdata;
1963  gint flags;
1964  gdouble aspect_ratio;
1965 
1966  gpdata = g_new0(RemminaPluginVncData, 1);
1967  g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
1968 
1969  gboolean disable_smooth_scrolling = FALSE;
1970  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
1971 
1972  disable_smooth_scrolling = remmina_plugin_service->file_get_int(remminafile, "disablesmoothscrolling", FALSE);
1973  REMMINA_PLUGIN_DEBUG("Disable smooth scrolling is set to %d", disable_smooth_scrolling);
1974 
1975  gpdata->drawing_area = gtk_drawing_area_new();
1976  gtk_widget_show(gpdata->drawing_area);
1977 
1978  aspect_ratio = remmina_plugin_service->file_get_double(remminafile, "aspect_ratio", 0);
1979  if (aspect_ratio > 0){
1980  GtkWidget* aspectframe = gtk_aspect_frame_new(NULL, 0, 0, aspect_ratio, FALSE);
1981 
1982  gtk_frame_set_shadow_type(GTK_FRAME(aspectframe), GTK_SHADOW_NONE);
1983  gtk_widget_show(aspectframe);
1984  gtk_container_add(GTK_CONTAINER(aspectframe), gpdata->drawing_area);
1985  gtk_container_add(GTK_CONTAINER(gp), aspectframe);
1986  }
1987  else{
1988  gtk_container_add(GTK_CONTAINER(gp), gpdata->drawing_area);
1989  }
1990 
1991  gtk_widget_add_events(
1992  gpdata->drawing_area,
1993  GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK
1994  | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK
1995  | GDK_KEY_RELEASE_MASK
1996  | GDK_SCROLL_MASK);
1997  gtk_widget_set_can_focus(gpdata->drawing_area, TRUE);
1998 
1999  if (!disable_smooth_scrolling) {
2000  REMMINA_PLUGIN_DEBUG("Adding GDK_SMOOTH_SCROLL_MASK");
2001  gtk_widget_add_events(gpdata->drawing_area, GDK_SMOOTH_SCROLL_MASK);
2002  }
2003 
2004 
2005  g_signal_connect(G_OBJECT(gpdata->drawing_area), "draw", G_CALLBACK(remmina_plugin_vnc_on_draw), gp);
2006 #if LIBVNCSERVER_CHECK_VERSION_VERSION(0, 9, 14)
2007  g_signal_connect(G_OBJECT(gpdata->drawing_area), "size-allocate", G_CALLBACK(remmina_plugin_vnc_on_size_allocate), gp);
2008 #endif
2009  gpdata->auth_first = TRUE;
2010  gpdata->clipboard_timer = g_date_time_new_now_utc();
2011  gpdata->listen_sock = -1;
2012  gpdata->pressed_keys = g_ptr_array_new();
2013  gpdata->vnc_event_queue = g_queue_new();
2014  pthread_mutex_init(&gpdata->vnc_event_queue_mutex, NULL);
2015  if (pipe(gpdata->vnc_event_pipe)) {
2016  g_print("Error creating pipes.\n");
2017  gpdata->vnc_event_pipe[0] = 0;
2018  gpdata->vnc_event_pipe[1] = 0;
2019  }
2020  flags = fcntl(gpdata->vnc_event_pipe[0], F_GETFL, 0);
2021  fcntl(gpdata->vnc_event_pipe[0], F_SETFL, flags | O_NONBLOCK);
2022 
2023  pthread_mutex_init(&gpdata->buffer_mutex, NULL);
2024 }
2025 
2026 /* Array of key/value pairs for color depths */
2027 static gpointer colordepth_list[] =
2028 {
2029  "32", N_("True colour (32 bpp)"),
2030  "16", N_("High colour (16 bpp)"),
2031  "8", N_("256 colours (8 bpp)"),
2032  NULL
2033 };
2034 
2035 /* Array of key/value pairs for quality selection */
2036 static gpointer quality_list[] =
2037 {
2038  "2", N_("Good"),
2039  "9", N_("Best (slowest)"),
2040  "1", N_("Medium"),
2041  "0", N_("Poor (fastest)"),
2042  NULL
2043 };
2044 
2045 static gchar repeater_tooltip[] =
2046  N_("Connect to VNC using a repeater:\n"
2047  " • The server field must contain the repeater ID, e.g. ID:123456789\n"
2048  " • The repeater field have to be set to the repeater IP and port, like:\n"
2049  " 10.10.10.12:5901\n"
2050  " • From the remote VNC server, you will connect to\n"
2051  " the repeater, e.g. with x11vnc:\n"
2052  " x11vnc -connect repeater=ID:123456789+10.10.10.12:5500");
2053 
2054 static gchar vnciport_tooltip[] =
2055  N_("Listening for remote VNC connection:\n"
2056  " • The “Listen on port” field is the port Remmina will listen to,\n"
2057  " e.g. 8888\n"
2058  " • From the remote VNC server, you will connect to\n"
2059  " Remmina, e.g. with x11vnc:\n"
2060  " x11vnc -display :0 -connect 192.168.1.36:8888");
2061 
2062 static gchar aspect_ratio_tooltip[] =
2063  N_("Lock the aspect ratio when dynamic resolution is enabled:\n"
2064  "\n"
2065  " • The aspect ratio should be entered as a decimal number, e.g. 1.777\n"
2066  " • 16:9 corresponds roughly to 1.7777, 4:3 corresponds roughly to 1.333\n"
2067  " • The default value of 0 does not enforce any aspect ratio");
2068 
2069 static gchar vncencodings_tooltip[] =
2070  N_("Overriding the pre-set VNC encoding quality:\n"
2071  "\n"
2072  " • “Poor (fastest)” sets encoding to “copyrect zlib hextile raw”\n"
2073  " • “Medium” sets encoding to “tight zrle ultra copyrect hextile zlib corre rre raw”\n"
2074  " • “Good” sets encoding to “tight zrle ultra copyrect hextile zlib corre rre raw”\n"
2075  " • “Best (slowest)” sets encoding to “copyrect zrle ultra zlib hextile corre rre raw”");
2076 
2077 /* Array of RemminaProtocolSetting for basic settings.
2078  * Each item is composed by:
2079  * a) RemminaProtocolSettingType for setting type
2080  * b) Setting name
2081  * c) Setting description
2082  * d) Compact disposition
2083  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
2084  * f) Setting tooltip
2085  * g) Validation data pointer, will be passed to the validation callback method.
2086  * h) Validation callback method (Can be NULL. Every entry will be valid then.)
2087  * use following prototype:
2088  * gboolean mysetting_validator_method(gpointer key, gpointer value,
2089  * gpointer validator_data);
2090  * gpointer key is a gchar* containing the setting's name,
2091  * gpointer value contains the value which should be validated,
2092  * gpointer validator_data contains your passed data.
2093  */
2095 {
2096  { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, "_rfb._tcp", NULL, NULL, NULL },
2097  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "proxy", N_("Repeater"), FALSE, NULL, repeater_tooltip, NULL, NULL },
2098  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL, NULL, NULL },
2099  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("User password"), FALSE, NULL, NULL, NULL, NULL },
2100  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "colordepth", N_("Colour depth"), FALSE, colordepth_list, NULL, NULL, NULL },
2101  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "quality", N_("Quality"), FALSE, quality_list, NULL, NULL, NULL },
2102  { REMMINA_PROTOCOL_SETTING_TYPE_KEYMAP, "keymap", NULL, FALSE, NULL, NULL, NULL, NULL },
2103  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL }
2104 };
2105 
2106 // Same as above.
2108 {
2109  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "listenport", N_("Listen on port"), FALSE, NULL, vnciport_tooltip, NULL, NULL},
2110  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL, NULL, NULL},
2111  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("User password"), FALSE, NULL, NULL, NULL, NULL},
2112  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "colordepth", N_("Colour depth"), FALSE, colordepth_list, NULL, NULL, NULL},
2113  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "quality", N_("Quality"), FALSE, quality_list, NULL, NULL, NULL},
2114  { REMMINA_PROTOCOL_SETTING_TYPE_KEYMAP, "keymap", NULL, FALSE, NULL, NULL, NULL, NULL},
2115  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL}
2116 };
2117 
2118 /* Array of RemminaProtocolSetting for advanced settings.
2119  * Each item is composed by:
2120  * a) RemminaProtocolSettingType for setting type
2121  * b) Setting name
2122  * c) Setting description
2123  * d) Compact disposition
2124  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
2125  * f) Setting Tooltip
2126  */
2128 {
2129  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "encodings", N_("Override pre-set VNC encodings"), FALSE, NULL, vncencodings_tooltip },
2130  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "aspect_ratio", N_("Dynamic resolution enforced aspec ratio"), FALSE, NULL, aspect_ratio_tooltip },
2131  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "tightencoding", N_("Force tight encoding"), TRUE, NULL, N_("Enabling this may help when the remote desktop looks scrambled") },
2132  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablesmoothscrolling", N_("Disable smooth scrolling"), FALSE, NULL, NULL },
2133  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"), TRUE, NULL, NULL },
2134  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableserverbell", N_("Ignore remote bell messages"), FALSE, NULL, NULL },
2135  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableserverinput", N_("Prevent local interaction on the server"), TRUE, NULL, NULL },
2136  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "showcursor", N_("Show remote cursor"), FALSE, NULL, NULL },
2137  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", N_("Turn off clipboard sync"), TRUE, NULL, NULL },
2138  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableencryption", N_("Turn off encryption"), FALSE, NULL, NULL },
2139  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "viewonly", N_("View only"), TRUE, NULL, NULL },
2140  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
2141 };
2142 
2143 /* Array for available features.
2144  * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
2146 {
2147  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_QUALITY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_RADIO), "quality",
2148  quality_list },
2149  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_COLOR, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_RADIO), "colordepth",
2150  colordepth_list },
2151  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_VIEWONLY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "viewonly",
2152  N_("View only") },
2153  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_DISABLESERVERINPUT, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "disableserverinput",N_("Prevent local interaction on the server") },
2154  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_VNC_FEATURE_TOOL_REFRESH, N_("Refresh"), NULL, NULL },
2155  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_VNC_FEATURE_TOOL_CHAT, N_("Open Chat…"), "face-smile", NULL },
2156  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_VNC_FEATURE_TOOL_SENDCTRLALTDEL, N_("Send Ctrl+Alt+Delete"), NULL, NULL },
2157  { REMMINA_PROTOCOL_FEATURE_TYPE_SCALE, REMMINA_PLUGIN_VNC_FEATURE_SCALE, NULL, NULL, NULL },
2158  { REMMINA_PROTOCOL_FEATURE_TYPE_UNFOCUS, REMMINA_PLUGIN_VNC_FEATURE_UNFOCUS, NULL, NULL, NULL },
2159 #if LIBVNCSERVER_CHECK_VERSION_VERSION(0, 9, 14)
2160  { REMMINA_PROTOCOL_FEATURE_TYPE_DYNRESUPDATE, REMMINA_PLUGIN_VNC_FEATURE_DYNRESUPDATE, NULL, NULL, NULL },
2161 #endif
2162  { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
2163 };
2164 
2165 /* Protocol plugin definition and features */
2167 {
2169  VNC_PLUGIN_NAME, // Name
2170  VNC_PLUGIN_DESCRIPTION, // Description
2171  GETTEXT_PACKAGE, // Translation domain
2172  VNC_PLUGIN_VERSION, // Version number
2173  VNC_PLUGIN_APPICON, // Icon for normal connection
2174  VNC_PLUGIN_SSH_APPICON, // Icon for SSH connection
2175  remmina_plugin_vnc_basic_settings, // Array for basic settings
2176  remmina_plugin_vnc_advanced_settings, // Array for advanced settings
2177  REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type
2178  remmina_plugin_vnc_features, // Array for available features
2179  remmina_plugin_vnc_init, // Plugin initialization
2180  remmina_plugin_vnc_open_connection, // Plugin open connection
2181  remmina_plugin_vnc_close_connection, // Plugin close connection
2182  remmina_plugin_vnc_query_feature, // Query for available features
2183  remmina_plugin_vnc_call_feature, // Call a feature
2184  remmina_plugin_vnc_keystroke // Send a keystroke
2185 };
2186 
2187 /* Protocol plugin definition and features */
2189 {
2191  VNCI_PLUGIN_NAME, // Name
2192  VNCI_PLUGIN_DESCRIPTION, // Description
2193  GETTEXT_PACKAGE, // Translation domain
2194  VERSION, // Version number
2195  VNCI_PLUGIN_APPICON, // Icon for normal connection
2196  VNCI_PLUGIN_SSH_APPICON, // Icon for SSH connection
2197  remmina_plugin_vnci_basic_settings, // Array for basic settings
2198  remmina_plugin_vnc_advanced_settings, // Array for advanced settings
2199  REMMINA_PROTOCOL_SSH_SETTING_REVERSE_TUNNEL, // SSH settings type
2200  remmina_plugin_vnc_features, // Array for available features
2201  remmina_plugin_vnc_init, // Plugin initialization
2202  remmina_plugin_vnc_open_connection, // Plugin open connection
2203  remmina_plugin_vnc_close_connection, // Plugin close connection
2204  remmina_plugin_vnc_query_feature, // Query for available features
2205  remmina_plugin_vnc_call_feature, // Call a feature
2206  remmina_plugin_vnc_keystroke, // Send a keystroke
2207  NULL, // No screenshot support available
2208  NULL, // RCW map event
2209  NULL // RCW unmap event
2210 };
2211 
2212 G_MODULE_EXPORT gboolean
2214 {
2215  TRACE_CALL(__func__);
2216  remmina_plugin_service = service;
2217 
2218  bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
2219  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
2220 
2221  if (!service->register_plugin((RemminaPlugin *)&remmina_plugin_vnc))
2222  return FALSE;
2223 
2224  if (!service->register_plugin((RemminaPlugin *)&remmina_plugin_vnci))
2225  return FALSE;
2226 
2227  return TRUE;
2228 }
static void remmina_plugin_vnc_on_cuttext(GtkClipboard *clipboard, GdkEvent *event, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1645
static gboolean remmina_plugin_vnc_on_size_allocate(GtkWidget *widget, GtkAllocation *alloc, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1907
@@ -95,7 +95,7 @@ $(document).ready(function(){initNavTree('vnc__plugin_8c_source.html','');});
GtkWidget * drawing_area
Definition: vnc_plugin.h:90
gchar *(* protocol_plugin_init_get_cacert)(RemminaProtocolWidget *gp)
Definition: plugin.h:199
-
static gchar repeater_tooltip[]
Definition: vnc_plugin.c:2046
+
static gchar repeater_tooltip[]
Definition: vnc_plugin.c:2045
void(* protocol_plugin_chat_close)(RemminaProtocolWidget *gp)
Definition: plugin.h:210
static void remmina_plugin_vnc_on_realize(RemminaProtocolWidget *gp, gpointer data)
Definition: vnc_plugin.c:1660
@@ -105,7 +105,7 @@ $(document).ready(function(){initNavTree('vnc__plugin_8c_source.html','');});
static void remmina_plugin_vnc_rfb_finished(rfbClient *cl) __attribute__((unused))
Definition: vnc_plugin.c:716
RemminaScaleMode(* remmina_protocol_widget_get_current_scale_mode)(RemminaProtocolWidget *gp)
Definition: plugin.h:172
GtkWidget * widget
Definition: vnc_plugin.c:70
-
static void remmina_plugin_vnc_init(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1960
+
static void remmina_plugin_vnc_init(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1959
void(* protocol_plugin_set_error)(RemminaProtocolWidget *gp, const gchar *fmt,...)
Definition: plugin.h:176
@@ -115,9 +115,9 @@ $(document).ready(function(){initNavTree('vnc__plugin_8c_source.html','');});
void(* protocol_plugin_init_save_cred)(RemminaProtocolWidget *gp)
Definition: plugin.h:203
-
static const RemminaProtocolFeature remmina_plugin_vnc_features[]
Definition: vnc_plugin.c:2146
+
static const RemminaProtocolFeature remmina_plugin_vnc_features[]
Definition: vnc_plugin.c:2145
static int dot_cursor_y_hot
Definition: vnc_plugin.c:60
-
static gpointer colordepth_list[]
Definition: vnc_plugin.c:2028
+
static gpointer colordepth_list[]
Definition: vnc_plugin.c:2027
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:44
void(* protocol_plugin_init_show_retry)(RemminaProtocolWidget *gp)
Definition: plugin.h:205
@@ -152,7 +152,7 @@ $(document).ready(function(){initNavTree('vnc__plugin_8c_source.html','');});
static gboolean remmina_plugin_vnc_close_chat(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1029
static gboolean remmina_plugin_vnc_queue_draw_area_real(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:552
static const uint32_t remmina_plugin_vnc_no_encrypt_auth_types[]
Definition: vnc_plugin.c:306
-
static RemminaProtocolPlugin remmina_plugin_vnci
Definition: vnc_plugin.c:2189
+
static RemminaProtocolPlugin remmina_plugin_vnci
Definition: vnc_plugin.c:2188
gboolean(* protocol_plugin_is_closed)(RemminaProtocolWidget *gp)
Definition: plugin.h:177
static void remmina_plugin_vnc_event_push(RemminaProtocolWidget *gp, gint event_type, gpointer p1, gpointer p2, gpointer p3)
Definition: vnc_plugin.c:136
@@ -165,12 +165,12 @@ $(document).ready(function(){initNavTree('vnc__plugin_8c_source.html','');});
static void remmina_plugin_vnc_rfb_led_state(rfbClient *cl, int value, int pad)
Definition: vnc_plugin.c:722
void(* get_server_port)(const gchar *server, gint defaultport, gchar **host, gint *port)
Definition: plugin.h:249
-
static gchar vnciport_tooltip[]
Definition: vnc_plugin.c:2055
+
static gchar vnciport_tooltip[]
Definition: vnc_plugin.c:2054
gint(* file_get_int)(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: plugin.h:222
static gint delta_to_mask(float delta, float *accum, gint mask_plus, gint mask_minus)
Definition: vnc_plugin.c:1469
static void remmina_plugin_vnc_rfb_chat(rfbClient *cl, int value, char *text)
Definition: vnc_plugin.c:1050
static char * remmina_plugin_vnc_rfb_password(rfbClient *cl)
Definition: vnc_plugin.c:773
-
static const RemminaProtocolSetting remmina_plugin_vnc_advanced_settings[]
Definition: vnc_plugin.c:2128
+
static const RemminaProtocolSetting remmina_plugin_vnc_advanced_settings[]
Definition: vnc_plugin.c:2127
void(* protocol_plugin_update_align)(RemminaProtocolWidget *gp)
Definition: plugin.h:187
void(* protocol_plugin_chat_receive)(RemminaProtocolWidget *gp, const gchar *text)
Definition: plugin.h:211
@@ -180,7 +180,7 @@ $(document).ready(function(){initNavTree('vnc__plugin_8c_source.html','');});
static void remmina_plugin_vnc_rfb_cursor_shape(rfbClient *cl, int xhot, int yhot, int width, int height, int bytesPerPixel)
Definition: vnc_plugin.c:900
-
static RemminaProtocolPlugin remmina_plugin_vnc
Definition: vnc_plugin.c:2167
+
static RemminaProtocolPlugin remmina_plugin_vnc
Definition: vnc_plugin.c:2166
guint16 keycode
Definition: vnc_plugin.c:301
@@ -220,7 +220,7 @@ $(document).ready(function(){initNavTree('vnc__plugin_8c_source.html','');});
static void remmina_plugin_vnc_on_cuttext_request(GtkClipboard *clipboard, const gchar *text, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1618
-
static gchar vncencodings_tooltip[]
Definition: vnc_plugin.c:2070
+
static gchar vncencodings_tooltip[]
Definition: vnc_plugin.c:2069
gint(* protocol_plugin_get_height)(RemminaProtocolWidget *gp)
Definition: plugin.h:170
gint(* protocol_plugin_init_auth)(RemminaProtocolWidget *gp, RemminaMessagePanelFlags pflags, const gchar *title, const gchar *default_username, const gchar *default_password, const gchar *default_domain, const gchar *password_prompt)
Definition: plugin.h:191
@@ -248,7 +248,7 @@ $(document).ready(function(){initNavTree('vnc__plugin_8c_source.html','');});
static gboolean remmina_plugin_vnc_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: vnc_plugin.c:1823
static gboolean remmina_plugin_vnc_main(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1172
-
static const RemminaProtocolSetting remmina_plugin_vnc_basic_settings[]
Definition: vnc_plugin.c:2095
+
static const RemminaProtocolSetting remmina_plugin_vnc_basic_settings[]
Definition: vnc_plugin.c:2094
struct _RemminaPluginVncEvent::@63::@65 pointer
gchar *(* protocol_plugin_init_get_username)(RemminaProtocolWidget *gp)
Definition: plugin.h:194
@@ -279,28 +279,28 @@ $(document).ready(function(){initNavTree('vnc__plugin_8c_source.html','');});
gchar *(* protocol_plugin_init_get_cacrl)(RemminaProtocolWidget *gp)
Definition: plugin.h:200
-
static gpointer quality_list[]
Definition: vnc_plugin.c:2037
+
static gpointer quality_list[]
Definition: vnc_plugin.c:2036
static gboolean remmina_plugin_vnc_on_motion(GtkWidget *widget, GdkEventMotion *event, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1419
const gchar *(* file_get_string)(RemminaFile *remminafile, const gchar *setting)
Definition: plugin.h:219
static gboolean remmina_plugin_vnc_close_connection_timeout(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1735
static gboolean remmina_plugin_vnc_incoming_connection(RemminaProtocolWidget *gp, rfbClient *cl)
Definition: vnc_plugin.c:1073
-
static gchar aspect_ratio_tooltip[]
Definition: vnc_plugin.c:2063
+
static gchar aspect_ratio_tooltip[]
Definition: vnc_plugin.c:2062
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
gboolean remmina_plugin_vnc_setcursor(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:261
gboolean(* is_main_thread)(void)
Definition: plugin.h:250
pthread_mutex_t vnc_event_queue_mutex
Definition: vnc_plugin.h:111
-
G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
Definition: vnc_plugin.c:2214
+
G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
Definition: vnc_plugin.c:2213
static gboolean remmina_plugin_vnc_on_button(GtkWidget *widget, GdkEventButton *event, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1438
void(* protocol_plugin_init_show_listen)(RemminaProtocolWidget *gp, gint port)
Definition: plugin.h:204
-
static const RemminaProtocolSetting remmina_plugin_vnci_basic_settings[]
Definition: vnc_plugin.c:2108
+
static const RemminaProtocolSetting remmina_plugin_vnci_basic_settings[]
Definition: vnc_plugin.c:2107
static void remmina_plugin_vnc_event_free(RemminaPluginVncEvent *event)
Definition: vnc_plugin.c:171
static void remmina_plugin_vnc_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
Definition: vnc_plugin.c:1896
-
static gboolean remmina_plugin_vnc_on_draw(GtkWidget *widget, cairo_t *context, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1926
+
static gboolean remmina_plugin_vnc_on_draw(GtkWidget *widget, cairo_t *context, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1925
-- cgit v1.2.3