summaryrefslogtreecommitdiffstats
path: root/plugins/rdp/rdp_plugin.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 17:06:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 17:06:32 +0000
commit2dad5357405ad33cfa792f04b3ab62a5d188841e (patch)
treeb8f8893942060fe3cfb04ac374cda96fdfc8f453 /plugins/rdp/rdp_plugin.c
parentInitial commit. (diff)
downloadremmina-3623a0bf49202deb496bc12be402ad5982c4f942.tar.xz
remmina-3623a0bf49202deb496bc12be402ad5982c4f942.zip
Adding upstream version 1.4.34+dfsg.upstream/1.4.34+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/rdp/rdp_plugin.c')
-rw-r--r--plugins/rdp/rdp_plugin.c3363
1 files changed, 3363 insertions, 0 deletions
diff --git a/plugins/rdp/rdp_plugin.c b/plugins/rdp/rdp_plugin.c
new file mode 100644
index 0000000..68ea505
--- /dev/null
+++ b/plugins/rdp/rdp_plugin.c
@@ -0,0 +1,3363 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2010-2011 Vic Lee
+ * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+ * Copyright (C) 2016-2022 Antenore Gatta, Giovanni Panozzo
+ * Copyright (C) 2022-2023 Antenore Gatta, Giovanni Panozzo, Hiroyuki Tanaka
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ *
+ */
+
+
+#define _GNU_SOURCE
+
+#include "remmina/plugin.h"
+#include "rdp_plugin.h"
+#include "rdp_event.h"
+#include "rdp_graphics.h"
+#include "rdp_file.h"
+#include "rdp_settings.h"
+#include "rdp_cliprdr.h"
+#include "rdp_monitor.h"
+#include "rdp_channels.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+#include <sys/time.h>
+#ifdef GDK_WINDOWING_X11
+#include <cairo/cairo-xlib.h>
+#else
+#include <cairo/cairo.h>
+#endif
+#include <ctype.h>
+#include <freerdp/addin.h>
+#include <freerdp/assistance.h>
+#if FREERDP_VERSION_MAJOR >= 3
+#include <freerdp/channels/rdp2tcp.h>
+#else
+#define RDP2TCP_DVC_CHANNEL_NAME "rdp2tcp"
+#endif
+#include <freerdp/client/channels.h>
+#include <freerdp/client/cliprdr.h>
+#include <freerdp/client/cmdline.h>
+#include <freerdp/constants.h>
+#include <freerdp/error.h>
+#include <freerdp/event.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/settings.h>
+#include <winpr/cmdline.h>
+#include <winpr/memory.h>
+
+#ifdef HAVE_CUPS
+#include <cups/cups.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+
+#ifdef GDK_WINDOWING_X11
+#include <X11/Xlib.h>
+#include <X11/XKBlib.h>
+#include <gdk/gdkx.h>
+#elif defined(GDK_WINDOWING_WAYLAND)
+#include <gdk/gdkwayland.h>
+#endif
+
+#if defined(__FreeBSD__)
+#include <pthread_np.h>
+#endif
+
+#include <freerdp/locale/keyboard.h>
+
+#define REMMINA_RDP_FEATURE_TOOL_REFRESH 1
+#define REMMINA_RDP_FEATURE_SCALE 2
+#define REMMINA_RDP_FEATURE_UNFOCUS 3
+#define REMMINA_RDP_FEATURE_TOOL_SENDCTRLALTDEL 4
+#define REMMINA_RDP_FEATURE_DYNRESUPDATE 5
+#define REMMINA_RDP_FEATURE_MULTIMON 6
+#define REMMINA_RDP_FEATURE_VIEWONLY 7
+
+#define REMMINA_CONNECTION_TYPE_NONE 0
+
+#if FREERDP_VERSION_MAJOR >= 3
+ #define CLPARAM const char
+#else
+ #define CLPARAM char
+#endif
+
+#if FREERDP_VERSION_MAJOR < 3
+static HANDLE freerdp_abort_event(rdpContext* context) {
+ WINPR_ASSERT(context);
+ return context->abortEvent;
+}
+
+static BOOL freerdp_settings_set_pointer_len(rdpSettings* settings, size_t id, const void* data, size_t len)
+{
+ switch(id) {
+ case FreeRDP_LoadBalanceInfo:
+ free(settings->LoadBalanceInfo);
+ settings->LoadBalanceInfo = _strdup(data);
+ settings->LoadBalanceInfoLength = len;
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static void freerdp_abort_connect_context(rdpContext* context) {
+ WINPR_ASSERT(context);
+ freerdp_abort_connect(context->instance);
+}
+#endif
+
+RemminaPluginService *remmina_plugin_service = NULL;
+
+static BOOL gfx_h264_available = FALSE;
+// keep track of last interaction time for keep alive
+static time_t last_time;
+
+/* Compatibility: these functions have been introduced with https://github.com/FreeRDP/FreeRDP/commit/8c5d96784d
+ * and are missing on older FreeRDP, so we add them here.
+ * They should be removed from here after all distributed versions of FreeRDP (libwinpr) will have
+ * CommandLineParseCommaSeparatedValuesEx() onboard.
+ *
+ * (C) Copyright goes to the FreeRDP authors.
+ */
+static CLPARAM **remmina_rdp_CommandLineParseCommaSeparatedValuesEx(const char *name, const char *list, size_t *count)
+{
+ TRACE_CALL(__func__);
+#if FREERDP_CHECK_VERSION(2, 0, 0)
+ return (CLPARAM **)CommandLineParseCommaSeparatedValuesEx(name, list, count);
+#else
+ char **p;
+ char *str;
+ size_t nArgs;
+ size_t index;
+ size_t nCommas;
+ size_t prefix, len;
+
+ nCommas = 0;
+
+ if (count == NULL)
+ return NULL;
+
+ *count = 0;
+
+ if (!list) {
+ if (name) {
+ size_t len = strlen(name);
+ p = (char **)calloc(2UL + len, sizeof(char *));
+
+ if (p) {
+ char *dst = (char *)&p[1];
+ p[0] = dst;
+ sprintf_s(dst, len + 1, "%s", name);
+ *count = 1;
+ return p;
+ }
+ }
+
+ return NULL;
+ }
+
+ {
+ const char *it = list;
+
+ while ((it = strchr(it, ',')) != NULL) {
+ it++;
+ nCommas++;
+ }
+ }
+
+ nArgs = nCommas + 1;
+
+ if (name)
+ nArgs++;
+
+ prefix = (nArgs + 1UL) * sizeof(char *);
+ len = strlen(list);
+ p = (char **)calloc(len + prefix + 1, sizeof(char *));
+
+ if (!p)
+ return NULL;
+
+ str = &((char *)p)[prefix];
+ memcpy(str, list, len);
+
+ if (name)
+ p[0] = (char *)name;
+
+ for (index = name ? 1 : 0; index < nArgs; index++) {
+ char *comma = strchr(str, ',');
+ p[index] = str;
+
+ if (comma) {
+ str = comma + 1;
+ *comma = '\0';
+ }
+ }
+
+ *count = nArgs;
+ return p;
+#endif
+}
+
+static CLPARAM **remmina_rdp_CommandLineParseCommaSeparatedValues(const char *list, size_t *count)
+{
+ TRACE_CALL(__func__);
+ return remmina_rdp_CommandLineParseCommaSeparatedValuesEx(NULL, list, count);
+}
+
+/*
+ * End of CommandLineParseCommaSeparatedValuesEx() compatibility and copyright
+ */
+static BOOL rf_process_event_queue(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ UINT16 flags;
+ rdpInput *input;
+ rfContext *rfi = GET_PLUGIN_DATA(gp);
+ RemminaPluginRdpEvent *event;
+ DISPLAY_CONTROL_MONITOR_LAYOUT *dcml;
+ CLIPRDR_FORMAT_DATA_RESPONSE response = { 0 };
+ RemminaFile *remminafile;
+
+ if (rfi->event_queue == NULL)
+ return true;
+
+ input = rfi->clientContext.context.input;
+
+ remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ while ((event = (RemminaPluginRdpEvent *)g_async_queue_try_pop(rfi->event_queue)) != NULL) {
+ time(&last_time); //update last user interaction time
+ switch (event->type) {
+ case REMMINA_RDP_EVENT_TYPE_SCANCODE:
+
+ if (event->key_event.extended1){
+ flags = KBD_FLAGS_EXTENDED1;
+ }
+ else{
+ flags = event->key_event.extended ? KBD_FLAGS_EXTENDED : 0;
+ }
+ flags |= event->key_event.up ? KBD_FLAGS_RELEASE : KBD_FLAGS_DOWN;
+ input->KeyboardEvent(input, flags, event->key_event.key_code);
+ break;
+
+ case REMMINA_RDP_EVENT_TYPE_SCANCODE_UNICODE:
+ /*
+ * TS_UNICODE_KEYBOARD_EVENT RDP message, see https://msdn.microsoft.com/en-us/library/cc240585.aspx
+ */
+ flags = event->key_event.up ? KBD_FLAGS_RELEASE : KBD_FLAGS_DOWN;
+ input->UnicodeKeyboardEvent(input, flags, event->key_event.unicode_code);
+ break;
+
+ case REMMINA_RDP_EVENT_TYPE_MOUSE:
+ if (event->mouse_event.extended)
+ input->ExtendedMouseEvent(input, event->mouse_event.flags,
+ event->mouse_event.x, event->mouse_event.y);
+ else
+ input->MouseEvent(input, event->mouse_event.flags,
+ event->mouse_event.x, event->mouse_event.y);
+ break;
+
+ case REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_LIST:
+ rfi->clipboard.context->ClientFormatList(rfi->clipboard.context, event->clipboard_formatlist.pFormatList);
+ free(event->clipboard_formatlist.pFormatList);
+ break;
+
+ case REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_DATA_RESPONSE:
+ {
+ UINT32 msgFlags = (event->clipboard_formatdataresponse.data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
+#if FREERDP_VERSION_MAJOR >= 3
+ response.common.msgFlags = msgFlags;
+ response.common.dataLen = event->clipboard_formatdataresponse.size;
+#else
+ response.msgFlags = msgFlags;
+ response.dataLen = event->clipboard_formatdataresponse.size;
+#endif
+ response.requestedFormatData = event->clipboard_formatdataresponse.data;
+ rfi->clipboard.context->ClientFormatDataResponse(rfi->clipboard.context, &response);
+ }
+ break;
+
+ case REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_DATA_REQUEST:
+ REMMINA_PLUGIN_DEBUG("Sending client FormatDataRequest to server");
+ gettimeofday(&(rfi->clipboard.clientformatdatarequest_tv), NULL);
+ rfi->clipboard.context->ClientFormatDataRequest(rfi->clipboard.context, event->clipboard_formatdatarequest.pFormatDataRequest);
+ free(event->clipboard_formatdatarequest.pFormatDataRequest);
+ break;
+
+ case REMMINA_RDP_EVENT_TYPE_SEND_MONITOR_LAYOUT:
+ if (remmina_plugin_service->file_get_int(remminafile, "multimon", FALSE)) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_UseMultimon, TRUE);
+ /* TODO Add an option for this */
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_ForceMultimon, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_Fullscreen, TRUE);
+ /* got some crashes with g_malloc0, to be investigated */
+ dcml = calloc(freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_MonitorCount), sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
+ REMMINA_PLUGIN_DEBUG("REMMINA_RDP_EVENT_TYPE_SEND_MONITOR_LAYOUT:");
+ if (!dcml)
+ break;
+
+ const rdpMonitor *base = freerdp_settings_get_pointer(rfi->clientContext.context.settings, FreeRDP_MonitorDefArray);
+ for (gint i = 0; i < freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_MonitorCount); ++i) {
+ const rdpMonitor *current = &base[i];
+ REMMINA_PLUGIN_DEBUG("Sending display layout for monitor n° %d", i);
+ dcml[i].Flags = (current->is_primary ? DISPLAY_CONTROL_MONITOR_PRIMARY : 0);
+ REMMINA_PLUGIN_DEBUG("Monitor %d is primary: %d", i, dcml[i].Flags);
+ dcml[i].Left = current->x;
+ REMMINA_PLUGIN_DEBUG("Monitor %d x: %d", i, dcml[i].Left);
+ dcml[i].Top = current->y;
+ REMMINA_PLUGIN_DEBUG("Monitor %d y: %d", i, dcml[i].Top);
+ dcml[i].Width = current->width;
+ REMMINA_PLUGIN_DEBUG("Monitor %d width: %d", i, dcml[i].Width);
+ dcml[i].Height = current->height;
+ REMMINA_PLUGIN_DEBUG("Monitor %d height: %d", i, dcml[i].Height);
+ dcml[i].PhysicalWidth = current->attributes.physicalWidth;
+ REMMINA_PLUGIN_DEBUG("Monitor %d physical width: %d", i, dcml[i].PhysicalWidth);
+ dcml[i].PhysicalHeight = current->attributes.physicalHeight;
+ REMMINA_PLUGIN_DEBUG("Monitor %d physical height: %d", i, dcml[i].PhysicalHeight);
+ if (current->attributes.orientation)
+ dcml[i].Orientation = current->attributes.orientation;
+ else
+ dcml[i].Orientation = event->monitor_layout.desktopOrientation;
+ REMMINA_PLUGIN_DEBUG("Monitor %d orientation: %d", i, dcml[i].Orientation);
+ dcml[i].DesktopScaleFactor = event->monitor_layout.desktopScaleFactor;
+ dcml[i].DeviceScaleFactor = event->monitor_layout.deviceScaleFactor;
+ }
+ rfi->dispcontext->SendMonitorLayout(rfi->dispcontext, freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_MonitorCount), dcml);
+ g_free(dcml);
+ } else {
+ dcml = g_malloc0(sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
+ if (dcml) {
+ dcml->Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
+ dcml->Width = event->monitor_layout.width;
+ dcml->Height = event->monitor_layout.height;
+ dcml->Orientation = event->monitor_layout.desktopOrientation;
+ dcml->DesktopScaleFactor = event->monitor_layout.desktopScaleFactor;
+ dcml->DeviceScaleFactor = event->monitor_layout.deviceScaleFactor;
+ rfi->dispcontext->SendMonitorLayout(rfi->dispcontext, 1, dcml);
+ g_free(dcml); \
+ }
+ }
+ break;
+ case REMMINA_RDP_EVENT_DISCONNECT:
+ /* Disconnect requested via GUI (i.e: tab destroy/close) */
+ freerdp_abort_connect_context(&rfi->clientContext.context);
+ break;
+ }
+
+ g_free(event);
+ }
+
+ return true;
+}
+
+static gboolean remmina_rdp_tunnel_init(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+
+ /* Opens the optional SSH tunnel if needed.
+ * Used also when reopening the same tunnel for a FreeRDP reconnect.
+ * Returns TRUE if all OK, and setups correct rfi->Settings values
+ * with connection and certificate parameters */
+
+ gchar *hostport;
+ gchar *s;
+ gchar *host;
+ gchar *cert_host;
+ gint cert_port;
+ gint port;
+
+ rfContext *rfi = GET_PLUGIN_DATA(gp);
+
+ REMMINA_PLUGIN_DEBUG("Tunnel init");
+ hostport = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, 3389, FALSE);
+ if (hostport == NULL)
+ return FALSE;
+
+ remmina_plugin_service->get_server_port(hostport, 3389, &host, &port);
+
+ if (host[0] == 0)
+ return FALSE;
+
+ REMMINA_PLUGIN_DEBUG("protocol_plugin_start_direct_tunnel() returned %s", hostport);
+
+ cert_host = host;
+ cert_port = port;
+
+ if (!rfi->is_reconnecting) {
+ /* settings->CertificateName and settings->ServerHostname is created
+ * only on 1st connect, not on reconnections */
+
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname, host);
+
+ if (cert_port == 3389) {
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_CertificateName, cert_host);
+ } else {
+ s = g_strdup_printf("%s:%d", cert_host, cert_port);
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_CertificateName, s);
+ g_free(s);
+ }
+ }
+
+ REMMINA_PLUGIN_DEBUG("Tunnel has been optionally initialized. Now connecting to %s:%d", host, port);
+
+ if (cert_host != host) g_free(cert_host);
+ g_free(host);
+ g_free(hostport);
+
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ServerPort, port);
+
+ return TRUE;
+}
+
+static BOOL rf_auto_reconnect(rfContext *rfi)
+{
+ TRACE_CALL(__func__);
+ rdpSettings *settings = rfi->clientContext.context.settings;
+ RemminaPluginRdpUiObject *ui;
+ time_t treconn;
+ gchar *cval;
+ gint maxattempts;
+
+ RemminaProtocolWidget *gp = rfi->protocol_widget;
+ RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ rfi->is_reconnecting = TRUE;
+ rfi->stop_reconnecting_requested = FALSE;
+
+ /* Get the value set in FreeRDP_AutoReconnectMaxRetries (20) */
+ maxattempts = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
+ REMMINA_PLUGIN_DEBUG("maxattempts from default: %d", maxattempts);
+ /* Get the value from the global preferences if any */
+ if ((cval = remmina_plugin_service->pref_get_value("rdp_reconnect_attempts")) != NULL)
+ maxattempts = atoi(cval);
+ REMMINA_PLUGIN_DEBUG("maxattempts from general preferences: %d", maxattempts);
+ /* Get the value from the profile if any, otherwise uses the value of maxattempts */
+ maxattempts = remmina_plugin_service->file_get_int(remminafile, "rdp_reconnect_attempts", maxattempts);
+ REMMINA_PLUGIN_DEBUG("maxattempts from general plugin: %d", maxattempts);
+ /* If maxattemps is <= 0, we get the value from FreeRDP_AutoReconnectMaxRetries (20) */
+ if (maxattempts <= 0)
+ maxattempts = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
+ freerdp_settings_set_uint32(settings, FreeRDP_AutoReconnectMaxRetries, maxattempts);
+ REMMINA_PLUGIN_DEBUG("maxattempts set to: %d", maxattempts);
+
+ rfi->reconnect_maxattempts = maxattempts;
+ rfi->reconnect_nattempt = 0;
+
+ /* Only auto reconnect on network disconnects. */
+ switch (freerdp_error_info(rfi->clientContext.context.instance)) {
+ case ERRINFO_GRAPHICS_SUBSYSTEM_FAILED:
+ /* Disconnected by server hitting a bug or resource limit */
+ break;
+ case ERRINFO_SUCCESS:
+ /* A network disconnect was detected */
+ break;
+ default:
+ rfi->is_reconnecting = FALSE;
+ return FALSE;
+ }
+
+ if (!freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled)) {
+ /* No auto-reconnect - just quit */
+ rfi->is_reconnecting = FALSE;
+ return FALSE;
+ }
+
+ /* A network disconnect was detected and we should try to reconnect */
+ REMMINA_PLUGIN_DEBUG("[%s] network disconnection detected, initiating reconnection attempt",
+ freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_RECONNECT_PROGRESS;
+ remmina_rdp_event_queue_ui_async(rfi->protocol_widget, ui);
+
+ /* Sleep half a second to allow:
+ * - processing of the UI event we just pushed on the queue
+ * - better network conditions
+ * Remember: We hare on a thread, so the main gui won’t lock */
+ usleep(500000);
+
+ /* Perform an auto-reconnect. */
+ while (TRUE) {
+ /* Quit retrying if max retries has been exceeded */
+ if (rfi->reconnect_nattempt++ >= rfi->reconnect_maxattempts) {
+ REMMINA_PLUGIN_DEBUG("[%s] maximum number of reconnection attempts exceeded.",
+ freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+ }
+
+ if (rfi->stop_reconnecting_requested) {
+ REMMINA_PLUGIN_DEBUG("[%s] reconnect request loop interrupted by user.",
+ freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+ }
+
+ /* Attempt the next reconnect */
+ REMMINA_PLUGIN_DEBUG("[%s] reconnection, attempt #%d of %d",
+ freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname), rfi->reconnect_nattempt, rfi->reconnect_maxattempts);
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_RECONNECT_PROGRESS;
+ remmina_rdp_event_queue_ui_async(rfi->protocol_widget, ui);
+
+ treconn = time(NULL);
+
+ /* Reconnect the SSH tunnel, if needed */
+ if (!remmina_rdp_tunnel_init(rfi->protocol_widget)) {
+ REMMINA_PLUGIN_DEBUG("[%s] unable to recreate tunnel with remmina_rdp_tunnel_init.",
+ freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ } else {
+ if (freerdp_reconnect(rfi->clientContext.context.instance)) {
+ /* Reconnection is successful */
+ REMMINA_PLUGIN_DEBUG("[%s] reconnected.", freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ rfi->is_reconnecting = FALSE;
+ return TRUE;
+ }
+ }
+
+ /* Wait until 5 secs have elapsed from last reconnect attempt, while checking for rfi->stop_reconnecting_requested */
+ while (time(NULL) - treconn < 5) {
+ if (rfi->stop_reconnecting_requested)
+ break;
+ usleep(200000); // 200ms sleep
+ }
+ }
+
+ rfi->is_reconnecting = FALSE;
+ return FALSE;
+}
+
+static BOOL rf_begin_paint(rdpContext *context)
+{
+ TRACE_CALL(__func__);
+ rdpGdi *gdi;
+
+ if (!context)
+ return FALSE;
+
+ gdi = context->gdi;
+ if (!gdi || !gdi->primary || !gdi->primary->hdc || !gdi->primary->hdc->hwnd)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL rf_end_paint(rdpContext *context)
+{
+ TRACE_CALL(__func__);
+ rdpGdi *gdi;
+ rfContext *rfi;
+ RemminaPluginRdpUiObject *ui;
+ int i, ninvalid;
+ region *reg;
+ HGDI_RGN cinvalid;
+
+ gdi = context->gdi;
+ rfi = (rfContext *)context;
+
+ if (gdi == NULL || gdi->primary == NULL || gdi->primary->hdc == NULL || gdi->primary->hdc->hwnd == NULL)
+ return TRUE;
+
+ if (gdi->primary->hdc->hwnd->invalid->null)
+ return TRUE;
+
+ if (gdi->primary->hdc->hwnd->ninvalid < 1)
+ return TRUE;
+
+ ninvalid = gdi->primary->hdc->hwnd->ninvalid;
+ cinvalid = gdi->primary->hdc->hwnd->cinvalid;
+ reg = (region *)g_malloc(sizeof(region) * ninvalid);
+ for (i = 0; i < ninvalid; i++) {
+ reg[i].x = cinvalid[i].x;
+ reg[i].y = cinvalid[i].y;
+ reg[i].w = cinvalid[i].w;
+ reg[i].h = cinvalid[i].h;
+ }
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_UPDATE_REGIONS;
+ ui->reg.ninvalid = ninvalid;
+ ui->reg.ureg = reg;
+
+ remmina_rdp_event_queue_ui_async(rfi->protocol_widget, ui);
+
+
+ gdi->primary->hdc->hwnd->invalid->null = TRUE;
+ gdi->primary->hdc->hwnd->ninvalid = 0;
+
+
+ return TRUE;
+}
+
+static BOOL rf_desktop_resize(rdpContext *context)
+{
+ TRACE_CALL(__func__);
+ rfContext *rfi;
+ RemminaProtocolWidget *gp;
+ RemminaPluginRdpUiObject *ui;
+ UINT32 w, h;
+
+ rfi = (rfContext *)context;
+ gp = rfi->protocol_widget;
+
+ w = freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopWidth);
+ h = freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopHeight);
+ remmina_plugin_service->protocol_plugin_set_width(gp, w);
+ remmina_plugin_service->protocol_plugin_set_height(gp, h);
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_EVENT;
+ ui->event.type = REMMINA_RDP_UI_EVENT_DESTROY_CAIRO_SURFACE;
+ remmina_rdp_event_queue_ui_sync_retint(gp, ui);
+
+ /* Tell libfreerdp to change its internal GDI bitmap width and heigt,
+ * this will also destroy gdi->primary_buffer, making our rfi->surface invalid */
+ gdi_resize(((rdpContext *)rfi)->gdi, w, h);
+
+ /* Call to remmina_rdp_event_update_scale(gp) on the main UI thread,
+ * this will recreate rfi->surface from gdi->primary_buffer */
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_EVENT;
+ ui->event.type = REMMINA_RDP_UI_EVENT_UPDATE_SCALE;
+ remmina_rdp_event_queue_ui_sync_retint(gp, ui);
+
+ remmina_plugin_service->protocol_plugin_desktop_resize(gp);
+
+ return TRUE;
+}
+
+static BOOL rf_play_sound(rdpContext *context, const PLAY_SOUND_UPDATE *play_sound)
+{
+ TRACE_CALL(__func__);
+ rfContext *rfi;
+ RemminaProtocolWidget *gp;
+ GdkDisplay *disp;
+
+ rfi = (rfContext *)context;
+ gp = rfi->protocol_widget;
+
+ disp = gtk_widget_get_display(GTK_WIDGET(gp));
+ gdk_display_beep(disp);
+
+ return TRUE;
+}
+
+static BOOL rf_keyboard_set_indicators(rdpContext *context, UINT16 led_flags)
+{
+ TRACE_CALL(__func__);
+ rfContext *rfi;
+ RemminaProtocolWidget *gp;
+ GdkDisplay *disp;
+
+ rfi = (rfContext *)context;
+ gp = rfi->protocol_widget;
+ disp = gtk_widget_get_display(GTK_WIDGET(gp));
+
+#ifdef GDK_WINDOWING_X11
+ if (GDK_IS_X11_DISPLAY(disp)) {
+ /* TODO: We are not on the main thread. Will X.Org complain? */
+ Display *x11_display;
+ x11_display = gdk_x11_display_get_xdisplay(disp);
+ XkbLockModifiers(x11_display, XkbUseCoreKbd,
+ LockMask | Mod2Mask,
+ ((led_flags & KBD_SYNC_CAPS_LOCK) ? LockMask : 0) |
+ ((led_flags & KBD_SYNC_NUM_LOCK) ? Mod2Mask : 0)
+ );
+
+ /* TODO: Add support to KANA_LOCK and SCROLL_LOCK */
+ }
+#endif
+
+ return TRUE;
+}
+
+static BOOL rf_keyboard_set_ime_status(rdpContext *context, UINT16 imeId, UINT32 imeState,
+ UINT32 imeConvMode)
+{
+ TRACE_CALL(__func__);
+ if (!context)
+ return FALSE;
+
+ /* Unimplemented, we ignore it */
+
+ return TRUE;
+}
+
+static BOOL remmina_rdp_pre_connect(freerdp *instance)
+{
+ TRACE_CALL(__func__);
+ rdpChannels *channels;
+ rdpSettings *settings;
+ rdpContext *context = instance->context;
+
+ settings = context->settings;
+ channels = context->channels;
+ freerdp_settings_set_uint32(settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNIX);
+ freerdp_settings_set_uint32(settings, FreeRDP_OsMinorType, OSMINORTYPE_UNSPECIFIED);
+ freerdp_settings_set_bool(settings, FreeRDP_BitmapCacheEnabled, TRUE);
+ freerdp_settings_set_uint32(settings, FreeRDP_OffscreenSupportLevel, 1);
+
+ PubSub_SubscribeChannelConnected(instance->context->pubSub,
+ remmina_rdp_OnChannelConnectedEventHandler);
+ PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
+ remmina_rdp_OnChannelDisconnectedEventHandler);
+
+ if (!freerdp_client_load_addins(channels, settings))
+ return FALSE;
+
+ return true;
+}
+
+static BOOL remmina_rdp_post_connect(freerdp *instance)
+{
+ TRACE_CALL(__func__);
+ rfContext *rfi;
+ RemminaProtocolWidget *gp;
+ RemminaPluginRdpUiObject *ui;
+ UINT32 freerdp_local_color_format;
+
+ rfi = (rfContext *)instance->context;
+ gp = rfi->protocol_widget;
+ rfi->postconnect_error = REMMINA_POSTCONNECT_ERROR_OK;
+
+ rfi->attempt_interactive_authentication = FALSE; // We authenticated!
+
+ rfi->srcBpp = freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth);
+
+ if (freerdp_settings_get_bool(rfi->clientContext.context.settings, FreeRDP_RemoteFxCodec) == FALSE)
+ rfi->sw_gdi = TRUE;
+
+ rf_register_graphics(instance->context->graphics);
+
+ REMMINA_PLUGIN_DEBUG("bpp: %d", rfi->bpp);
+ switch (rfi->bpp) {
+ case 24:
+ REMMINA_PLUGIN_DEBUG("CAIRO_FORMAT_RGB24");
+ freerdp_local_color_format = PIXEL_FORMAT_BGRX32;
+ rfi->cairo_format = CAIRO_FORMAT_RGB24;
+ break;
+ case 32:
+ /** Do not use alpha as it's not used with the desktop
+ * CAIRO_FORMAT_ARGB32
+ * See https://gitlab.com/Remmina/Remmina/-/issues/2456
+ */
+ REMMINA_PLUGIN_DEBUG("CAIRO_FORMAT_RGB24");
+ freerdp_local_color_format = PIXEL_FORMAT_BGRA32;
+ rfi->cairo_format = CAIRO_FORMAT_RGB24;
+ break;
+ default:
+ REMMINA_PLUGIN_DEBUG("CAIRO_FORMAT_RGB16_565");
+ freerdp_local_color_format = PIXEL_FORMAT_RGB16;
+ rfi->cairo_format = CAIRO_FORMAT_RGB16_565;
+ break;
+ }
+
+ if (!gdi_init(instance, freerdp_local_color_format)) {
+ rfi->postconnect_error = REMMINA_POSTCONNECT_ERROR_GDI_INIT;
+ return FALSE;
+ }
+
+ if (instance->context->codecs->h264 == NULL && freerdp_settings_get_bool(rfi->clientContext.context.settings, FreeRDP_GfxH264)) {
+ gdi_free(instance);
+ rfi->postconnect_error = REMMINA_POSTCONNECT_ERROR_NO_H264;
+ return FALSE;
+ }
+
+ // pointer_cache_register_callbacks(instance->update);
+ rdpUpdate *update = instance->context->update;
+ update->BeginPaint = rf_begin_paint;
+ update->EndPaint = rf_end_paint;
+ update->DesktopResize = rf_desktop_resize;
+
+ update->PlaySound = rf_play_sound;
+ update->SetKeyboardIndicators = rf_keyboard_set_indicators;
+ update->SetKeyboardImeStatus = rf_keyboard_set_ime_status;
+
+ remmina_rdp_clipboard_init(rfi);
+ rfi->connected = true;
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_CONNECTED;
+ remmina_rdp_event_queue_ui_async(gp, ui);
+
+ return TRUE;
+}
+
+#if !defined(FREERDP_VERSION_MAJOR) || (FREERDP_VERSION_MAJOR < 3)
+static BOOL remmina_rdp_authenticate(freerdp *instance, char **username, char **password, char **domain)
+{
+ TRACE_CALL(__func__);
+ gchar *s_username, *s_password, *s_domain;
+ gint ret;
+ rfContext *rfi;
+ RemminaProtocolWidget *gp;
+ gboolean save;
+ gboolean disablepasswordstoring;
+ RemminaFile *remminafile;
+
+ rfi = (rfContext *)instance->context;
+ gp = rfi->protocol_widget;
+ remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+ disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
+
+ ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
+ (disablepasswordstoring ? 0 : REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD) | REMMINA_MESSAGE_PANEL_FLAG_USERNAME | REMMINA_MESSAGE_PANEL_FLAG_DOMAIN,
+ _("Enter RDP authentication credentials"),
+ remmina_plugin_service->file_get_string(remminafile, "username"),
+ remmina_plugin_service->file_get_string(remminafile, "password"),
+ remmina_plugin_service->file_get_string(remminafile, "domain"),
+ NULL);
+ if (ret == GTK_RESPONSE_OK) {
+ s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
+ if (s_username) freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_Username, s_username);
+
+ s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
+ if (s_password) freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_Password, s_password);
+
+ s_domain = remmina_plugin_service->protocol_plugin_init_get_domain(gp);
+ if (s_domain) freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_Domain, s_domain);
+
+ remmina_plugin_service->file_set_string(remminafile, "username", s_username);
+ remmina_plugin_service->file_set_string(remminafile, "domain", s_domain);
+
+ save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
+ if (save) {
+ // User has requested to save credentials. We put the password
+ // into remminafile->settings. It will be saved later, on successful connection, by
+ // rcw.c
+ remmina_plugin_service->file_set_string(remminafile, "password", s_password);
+ } else {
+ remmina_plugin_service->file_set_string(remminafile, "password", NULL);
+ }
+
+
+ if (s_username) g_free(s_username);
+ if (s_password) g_free(s_password);
+ if (s_domain) g_free(s_domain);
+
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOL remmina_rdp_gw_authenticate(freerdp *instance, char **username, char **password, char **domain)
+{
+ TRACE_CALL(__func__);
+ gchar *s_username, *s_password, *s_domain;
+ gint ret;
+ rfContext *rfi;
+ RemminaProtocolWidget *gp;
+ gboolean save;
+ gboolean disablepasswordstoring;
+ gboolean basecredforgw;
+ RemminaFile *remminafile;
+
+ rfi = (rfContext *)instance->context;
+ gp = rfi->protocol_widget;
+ remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ if (!remmina_plugin_service->file_get_string(remminafile, "gateway_server"))
+ return false;
+ disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
+ basecredforgw = remmina_plugin_service->file_get_int(remminafile, "base-cred-for-gw", FALSE);
+
+ if (basecredforgw) {
+ ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
+ (disablepasswordstoring ? 0 : REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD) | REMMINA_MESSAGE_PANEL_FLAG_USERNAME | REMMINA_MESSAGE_PANEL_FLAG_DOMAIN,
+ _("Enter RDP authentication credentials"),
+ remmina_plugin_service->file_get_string(remminafile, "username"),
+ remmina_plugin_service->file_get_string(remminafile, "password"),
+ remmina_plugin_service->file_get_string(remminafile, "domain"),
+ NULL);
+ } else {
+ ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
+ (disablepasswordstoring ? 0 : REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD) | REMMINA_MESSAGE_PANEL_FLAG_USERNAME | REMMINA_MESSAGE_PANEL_FLAG_DOMAIN,
+ _("Enter RDP gateway authentication credentials"),
+ remmina_plugin_service->file_get_string(remminafile, "gateway_username"),
+ remmina_plugin_service->file_get_string(remminafile, "gateway_password"),
+ remmina_plugin_service->file_get_string(remminafile, "gateway_domain"),
+ NULL);
+ }
+
+
+ if (ret == GTK_RESPONSE_OK) {
+ s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
+ if (s_username) freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayUsername, s_username);
+
+ s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
+ if (s_password) freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayPassword, s_password);
+
+ s_domain = remmina_plugin_service->protocol_plugin_init_get_domain(gp);
+ if (s_domain) freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayDomain, s_domain);
+
+ save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
+
+ if (basecredforgw) {
+ remmina_plugin_service->file_set_string(remminafile, "username", s_username);
+ remmina_plugin_service->file_set_string(remminafile, "domain", s_domain);
+ if (save)
+ remmina_plugin_service->file_set_string(remminafile, "password", s_password);
+ else
+ remmina_plugin_service->file_set_string(remminafile, "password", NULL);
+ } else {
+ remmina_plugin_service->file_set_string(remminafile, "gateway_username", s_username);
+ remmina_plugin_service->file_set_string(remminafile, "gateway_domain", s_domain);
+ if (save)
+ remmina_plugin_service->file_set_string(remminafile, "gateway_password", s_password);
+ else
+ remmina_plugin_service->file_set_string(remminafile, "gateway_password", NULL);
+ }
+
+ if (s_username) g_free(s_username);
+ if (s_password) g_free(s_password);
+ if (s_domain) g_free(s_domain);
+
+ return true;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+#else
+static BOOL remmina_rdp_authenticate_ex(freerdp* instance, char** username, char** password,
+ char** domain, rdp_auth_reason reason)
+{
+ TRACE_CALL(__func__);
+ gchar *s_username = NULL, *s_password = NULL, *s_domain = NULL;
+ const gchar* key_user = NULL;
+ const gchar* key_domain = NULL;
+ const gchar* key_password = NULL;
+ const gchar* key_title = NULL;
+ gint ret;
+ rfContext *rfi;
+ RemminaProtocolWidget *gp;
+ gboolean save;
+ gboolean disablepasswordstoring;
+ RemminaFile *remminafile;
+ RemminaMessagePanelFlags flags = REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD | REMMINA_MESSAGE_PANEL_FLAG_USERNAME | REMMINA_MESSAGE_PANEL_FLAG_DOMAIN;
+
+ rfi = (rfContext *)instance->context;
+ gp = rfi->protocol_widget;
+ remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+ disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
+
+ FreeRDP_Settings_Keys_String cfg_key_user = FreeRDP_STRING_UNUSED;
+ FreeRDP_Settings_Keys_String cfg_key_domain = FreeRDP_STRING_UNUSED;
+ FreeRDP_Settings_Keys_String cfg_key_password = FreeRDP_STRING_UNUSED;
+ switch(reason) {
+ case AUTH_NLA:
+ case AUTH_TLS:
+ case AUTH_RDP:
+ key_title = _("Enter RDP authentication credentials");
+ key_user = "username";
+ key_domain = "domain";
+ key_password = "password";
+ cfg_key_user = FreeRDP_Username;
+ cfg_key_domain = FreeRDP_Domain;
+ cfg_key_password = FreeRDP_Password;
+ break;
+ case GW_AUTH_HTTP:
+ case GW_AUTH_RDG:
+ case GW_AUTH_RPC:
+ key_title = _("Enter RDP gateway authentication credentials");
+ key_user = "gateway_username";
+ key_domain = "gateway_domain";
+ key_password = "gateway_password";
+ cfg_key_user = FreeRDP_GatewayUsername;
+ cfg_key_domain = FreeRDP_GatewayDomain;
+ cfg_key_password = FreeRDP_GatewayPassword;
+ break;
+ case AUTH_SMARTCARD_PIN:
+ key_title = _("Enter RDP SmartCard PIN");
+ key_password = "smartcard_pin";
+ flags = 0;
+ break;
+ default:
+ // TODO: Display an error dialog informing the user that the remote requires some mechanism FreeRDP or Remmina currently do not support
+ g_fprintf(stderr, "[authentication] unsupported type %d, access denied", reason);
+ return FALSE;
+ }
+
+ if (!disablepasswordstoring)
+ flags |= REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD;
+
+ ret = remmina_plugin_service->protocol_plugin_init_auth(gp, flags,
+ key_title,
+ remmina_plugin_service->file_get_string(remminafile, key_user),
+ remmina_plugin_service->file_get_string(remminafile, key_password),
+ remmina_plugin_service->file_get_string(remminafile, disablepasswordstoring ? NULL : key_domain),
+ NULL);
+ if (ret == GTK_RESPONSE_OK) {
+ if (cfg_key_user != FreeRDP_STRING_UNUSED)
+ {
+ s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
+ if (s_username)
+ freerdp_settings_set_string(rfi->clientContext.context.settings, cfg_key_user, s_username);
+ remmina_plugin_service->file_set_string(remminafile, key_user, s_username);
+ }
+
+ if (cfg_key_password != FreeRDP_STRING_UNUSED)
+ {
+ s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
+ if (s_password)
+ freerdp_settings_set_string(rfi->clientContext.context.settings, cfg_key_password, s_password);
+ }
+
+ if (cfg_key_domain != FreeRDP_STRING_UNUSED) {
+ s_domain = remmina_plugin_service->protocol_plugin_init_get_domain(gp);
+ if (s_domain)
+ freerdp_settings_set_string(rfi->clientContext.context.settings, cfg_key_domain, s_domain);
+ remmina_plugin_service->file_set_string(remminafile, key_domain, s_domain);
+ }
+
+ save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
+ if (save) {
+ // User has requested to save credentials. We put the password
+ // into remminafile->settings. It will be saved later, on successful connection, by
+ // rcw.c
+ remmina_plugin_service->file_set_string(remminafile, key_password, s_password);
+ } else {
+ remmina_plugin_service->file_set_string(remminafile, key_password, NULL);
+ }
+
+
+ if (s_username) g_free(s_username);
+ if (s_password) g_free(s_password);
+ if (s_domain) g_free(s_domain);
+
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOL remmina_rdp_choose_smartcard(freerdp* instance, SmartcardCertInfo** cert_list, DWORD count,
+ DWORD* choice, BOOL gateway)
+{
+ // TODO: Display a simple list of smartcards/certs to choose from.
+ // See client_cli_choose_smartcard for a sample
+ return client_cli_choose_smartcard(instance, cert_list, count, choice, gateway);
+}
+
+static BOOL remmina_rdp_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token,
+ size_t count, ...)
+{
+ // TODO: Open a (currently hard coded) URL, authenticate in a webview/browser, return the access token.
+ // See client_cli_get_access_token or sdl_webview_get_access_token for implementations
+ return client_cli_get_access_token(instance, tokenType, token, count);
+}
+
+static BOOL remmina_rdp_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
+ BOOL isConsentMandatory, size_t length,
+ const WCHAR* message)
+{
+ // TODO: Present a message to the user, usually terms of service or similar
+ // See client_cli_present_gateway_message or sdl_present_gateway_message
+ return client_cli_present_gateway_message(instance, type, isDisplayMandatory, isConsentMandatory, length, message);
+}
+
+static int remmina_rdp_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
+{
+ // TODO: Display the reason for connection termination
+ // See client_cli_logon_error_info or sdl_logon_error_info
+ return client_cli_logon_error_info(instance, data, type);
+}
+
+static SSIZE_T remmina_rdp_retry_dialog(freerdp* instance, const char* what, size_t current,
+ void* userarg)
+{
+ // TODO:
+ // See client_common_retry_dialog or
+ return client_common_retry_dialog(instance, what, current, userarg);
+}
+
+static void remmina_rdp_post_final_disconnect(freerdp* instance)
+{
+ // Clean up resources allocated in PreConnect
+}
+#endif
+
+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)
+{
+ TRACE_CALL(__func__);
+ gint status;
+ rfContext *rfi;
+ RemminaProtocolWidget *gp;
+
+ rfi = (rfContext *)instance->context;
+ gp = rfi->protocol_widget;
+
+ status = remmina_plugin_service->protocol_plugin_init_certificate(gp, subject, issuer, fingerprint);
+
+ if (status == GTK_RESPONSE_OK)
+ return 1;
+
+ return 0;
+}
+
+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));
+static DWORD
+remmina_rdp_verify_certificate(freerdp *instance, const char *common_name, const char *subject,
+ const char *issuer, const char *fingerprint, BOOL host_mismatch)
+{
+ TRACE_CALL(__func__);
+ gint status;
+ rfContext *rfi;
+ RemminaProtocolWidget *gp;
+
+ rfi = (rfContext *)instance->context;
+ gp = rfi->protocol_widget;
+
+ status = remmina_plugin_service->protocol_plugin_init_certificate(gp, subject, issuer, fingerprint);
+
+ if (status == GTK_RESPONSE_OK)
+ return 1;
+
+ return 0;
+}
+
+static DWORD remmina_rdp_verify_changed_certificate_ex(freerdp *instance, const char *host, UINT16 port,
+ const char *common_name, const char *subject,
+ const char *issuer, const char *fingerprint,
+ const char *old_subject, const char *old_issuer,
+ const char *old_fingerprint, DWORD flags)
+{
+ TRACE_CALL(__func__);
+ gint status;
+ rfContext *rfi;
+ RemminaProtocolWidget *gp;
+
+ rfi = (rfContext *)instance->context;
+ gp = rfi->protocol_widget;
+
+ status = remmina_plugin_service->protocol_plugin_changed_certificate(gp, subject, issuer, fingerprint, old_fingerprint);
+
+ if (status == GTK_RESPONSE_OK)
+ return 1;
+
+ return 0;
+}
+
+static void remmina_rdp_post_disconnect(freerdp *instance)
+{
+ TRACE_CALL(__func__);
+
+ if (!instance || !instance->context)
+ return;
+
+ PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
+ remmina_rdp_OnChannelConnectedEventHandler);
+ PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
+ remmina_rdp_OnChannelDisconnectedEventHandler);
+
+ /* The remaining cleanup will be continued on main thread by complete_cleanup_on_main_thread() */
+
+ // With FreeRDP3 only resources allocated in PostConnect and later are cleaned up here.
+}
+
+static void remmina_rdp_main_loop(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ DWORD status;
+ gchar buf[100];
+ rfContext *rfi = GET_PLUGIN_DATA(gp);
+ RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+ time_t cur_time, time_diff;
+
+ int jitter_time = remmina_plugin_service->file_get_int(remminafile, "rdp_mouse_jitter", 0);
+ time(&last_time);
+#if FREERDP_VERSION_MAJOR >= 3
+ while (!freerdp_shall_disconnect_context(&rfi->clientContext.context)) {
+#else
+ while (!freerdp_shall_disconnect(rfi->clientContext.context.instance)) {
+#endif
+ // move mouse if we've been idle and option is selected
+ time(&cur_time);
+ time_diff = cur_time - last_time;
+ if (jitter_time > 0 && time_diff > jitter_time){
+ last_time = cur_time;
+ remmina_rdp_mouse_jitter(gp);
+ }
+
+ HANDLE handles[MAXIMUM_WAIT_OBJECTS] = {0};
+ DWORD nCount = freerdp_get_event_handles(&rfi->clientContext.context, &handles[0], ARRAYSIZE(handles));
+ if (rfi->event_handle)
+ handles[nCount++] = rfi->event_handle;
+
+ handles[nCount++] = freerdp_abort_event(&rfi->clientContext.context);
+
+ if (nCount == 0) {
+ fprintf(stderr, "freerdp_get_event_handles failed\n");
+ break;
+ }
+
+ status = WaitForMultipleObjects(nCount, handles, FALSE, 100);
+
+ if (status == WAIT_FAILED) {
+ fprintf(stderr, "WaitForMultipleObjects failed with %lu\n", (unsigned long)status);
+ break;
+ }
+
+ if (rfi->event_handle && WaitForSingleObject(rfi->event_handle, 0) == WAIT_OBJECT_0) {
+ if (!rf_process_event_queue(gp)) {
+ fprintf(stderr, "Could not process local keyboard/mouse event queue\n");
+ break;
+ }
+ if (read(rfi->event_pipe[0], buf, sizeof(buf))) {
+ }
+ }
+
+ /* Check if a processed event called freerdp_abort_connect() and exit if true */
+ if (WaitForSingleObject(freerdp_abort_event(&rfi->clientContext.context), 0) == WAIT_OBJECT_0)
+ /* Session disconnected by local user action */
+ break;
+
+ if (!freerdp_check_event_handles(&rfi->clientContext.context)) {
+ if (rf_auto_reconnect(rfi)) {
+ /* Reset the possible reason/error which made us doing many reconnection reattempts and continue */
+ remmina_plugin_service->protocol_plugin_set_error(gp, NULL);
+ continue;
+ }
+ if (freerdp_get_last_error(&rfi->clientContext.context) == FREERDP_ERROR_SUCCESS)
+ fprintf(stderr, "Could not check FreeRDP file descriptor\n");
+ break;
+ }
+ }
+ const gchar *host = freerdp_settings_get_string (rfi->clientContext.context.settings, FreeRDP_ServerHostname);
+ // TRANSLATORS: the placeholder may be either an IP/FQDN or a server hostname
+ REMMINA_PLUGIN_AUDIT(_("Disconnected from %s via RDP"), host);
+ freerdp_disconnect(rfi->clientContext.context.instance);
+ REMMINA_PLUGIN_DEBUG("RDP client disconnected");
+}
+
+static int remmina_rdp_load_static_channel_addin(rdpChannels *channels, rdpSettings *settings, char *name, void *data)
+{
+ TRACE_CALL(__func__);
+ PVIRTUALCHANNELENTRY entry = NULL;
+ PVIRTUALCHANNELENTRYEX entryEx = NULL;
+
+ entryEx = (PVIRTUALCHANNELENTRYEX)(void *)freerdp_load_channel_addin_entry(
+ name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC | FREERDP_ADDIN_CHANNEL_ENTRYEX);
+
+ if (!entryEx)
+ entry = freerdp_load_channel_addin_entry(name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC);
+
+ if (entryEx) {
+ if (freerdp_channels_client_load_ex(channels, settings, entryEx, data) == 0) {
+ fprintf(stderr, "loading channel %s\n", name);
+ return TRUE;
+ }
+ } else if (entry) {
+ if (freerdp_channels_client_load(channels, settings, entry, data) == 0) {
+ fprintf(stderr, "loading channel %s\n", name);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gchar *remmina_rdp_find_prdriver(char *smap, char *prn)
+{
+ char c, *p, *dr;
+ int matching;
+ size_t sz;
+
+ enum { S_WAITPR,
+ S_INPRINTER,
+ S_WAITCOLON,
+ S_WAITDRIVER,
+ S_INDRIVER,
+ S_WAITSEMICOLON } state = S_WAITPR;
+
+ matching = 0;
+ while ((c = *smap++) != 0) {
+ switch (state) {
+ case S_WAITPR:
+ if (c != '\"') return NULL;
+ state = S_INPRINTER;
+ p = prn;
+ matching = 1;
+ break;
+ case S_INPRINTER:
+ if (matching && c == *p && *p != 0) {
+ p++;
+ } else if (c == '\"') {
+ if (*p != 0)
+ matching = 0;
+ state = S_WAITCOLON;
+ } else {
+ matching = 0;
+ }
+ break;
+ case S_WAITCOLON:
+ if (c != ':')
+ return NULL;
+ state = S_WAITDRIVER;
+ break;
+ case S_WAITDRIVER:
+ if (c != '\"')
+ return NULL;
+ state = S_INDRIVER;
+ dr = smap;
+ break;
+ case S_INDRIVER:
+ if (c == '\"') {
+ if (matching)
+ goto found;
+ else
+ state = S_WAITSEMICOLON;
+ }
+ break;
+ case S_WAITSEMICOLON:
+ if (c != ';')
+ return NULL;
+ state = S_WAITPR;
+ break;
+ }
+ }
+ return NULL;
+
+found:
+ sz = smap - dr;
+ p = (char *)malloc(sz);
+ memcpy(p, dr, sz);
+ p[sz - 1] = 0;
+ return p;
+}
+
+#ifdef HAVE_CUPS
+/**
+ * Callback function used by cupsEnumDests
+ * - For each enumerated local printer tries to set the Printer Name and Driver.
+ * @return 1 if there are other printers to scan or 0 when it's done.
+ */
+static int remmina_rdp_set_printers(void *user_data, unsigned flags, cups_dest_t *dest)
+{
+ rfContext *rfi = (rfContext *)user_data;
+ RemminaProtocolWidget *gp = rfi->protocol_widget;
+
+ /** @warning printer-make-and-model is not always the same as on the Windows,
+ * therefore it fails finding to right one and it fails to add
+ * the printer.
+ *
+ * We pass NULL and we do not check for errors. The following code is
+ * how it is supposed to work. @todo Ask CUPS mailing list for help.
+ *
+ * @code
+ * const char *model = cupsGetOption("printer-make-and-model",
+ * dest->num_options,
+ * dest->options);
+ * @endcode
+ */
+
+ RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+ const gchar *s = remmina_plugin_service->file_get_string(remminafile, "printer_overrides");
+
+ RDPDR_PRINTER *printer;
+ printer = (RDPDR_PRINTER *)calloc(1, sizeof(RDPDR_PRINTER));
+
+#if FREERDP_VERSION_MAJOR >= 3
+ RDPDR_DEVICE *pdev;
+ pdev = &(printer->device);
+#else
+ RDPDR_PRINTER *pdev;
+ pdev = printer;
+#endif
+
+ pdev->Type = RDPDR_DTYP_PRINT;
+ REMMINA_PLUGIN_DEBUG("Printer Type: %d", pdev->Type);
+
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RedirectPrinters, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_DeviceRedirection, TRUE);
+
+ REMMINA_PLUGIN_DEBUG("Destination: %s", dest->name);
+ if (!(pdev->Name = _strdup(dest->name))) {
+ free(printer);
+ return 1;
+ }
+
+ REMMINA_PLUGIN_DEBUG("Printer Name: %s", pdev->Name);
+
+ if (s) {
+ gchar *d = remmina_rdp_find_prdriver(strdup(s), pdev->Name);
+ if (d) {
+ printer->DriverName = strdup(d);
+ REMMINA_PLUGIN_DEBUG("Printer DriverName set to: %s", printer->DriverName);
+ g_free(d);
+ } else {
+ /**
+ * When remmina_rdp_find_prdriver doesn't return a DriverName
+ * it means that we don't want to share that printer
+ *
+ */
+ free(pdev->Name);
+ free(printer);
+ return 1;
+ }
+ } else {
+ /* We set to a default driver*/
+ printer->DriverName = _strdup("MS Publisher Imagesetter");
+ }
+
+ REMMINA_PLUGIN_DEBUG("Printer Driver: %s", printer->DriverName);
+ if (!freerdp_device_collection_add(rfi->clientContext.context.settings, (RDPDR_DEVICE *)printer)) {
+ free(printer->DriverName);
+ free(pdev->Name);
+ free(printer);
+ return 1;
+ }
+
+ return 1;
+}
+#endif /* HAVE_CUPS */
+
+/* Send Ctrl+Alt+Del keystrokes to the plugin drawing_area widget */
+static void remmina_rdp_send_ctrlaltdel(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ guint keys[] = { GDK_KEY_Control_L, GDK_KEY_Alt_L, GDK_KEY_Delete };
+ rfContext *rfi = GET_PLUGIN_DATA(gp);
+
+ remmina_plugin_service->protocol_plugin_send_keys_signals(rfi->drawing_area,
+ keys, G_N_ELEMENTS(keys), GDK_KEY_PRESS | GDK_KEY_RELEASE);
+}
+
+static gboolean remmina_rdp_set_connection_type(rdpSettings *settings, guint32 type)
+{
+ freerdp_settings_set_uint32(settings, FreeRDP_ConnectionType, type);
+
+ if (type == CONNECTION_TYPE_MODEM) {
+ freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, TRUE);
+ } else if (type == CONNECTION_TYPE_BROADBAND_LOW) {
+ freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
+ } else if (type == CONNECTION_TYPE_SATELLITE) {
+ freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
+ } else if (type == CONNECTION_TYPE_BROADBAND_HIGH) {
+ freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
+ } else if (type == CONNECTION_TYPE_WAN) {
+ freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
+ } else if (type == CONNECTION_TYPE_LAN) {
+ freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
+ } else if (type == CONNECTION_TYPE_AUTODETECT) {
+ freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE);
+ freerdp_settings_set_bool(settings, FreeRDP_NetworkAutoDetect, TRUE);
+
+ /* Automatically activate GFX and RFX codec support */
+#ifdef WITH_GFX_H264
+ freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, gfx_h264_available);
+ freerdp_settings_set_bool(settings, FreeRDP_GfxH264, gfx_h264_available);
+#endif
+ freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE);
+ freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline, TRUE);
+ } else if (type == REMMINA_CONNECTION_TYPE_NONE) {
+ return FALSE;
+ } else {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#ifdef GDK_WINDOWING_X11
+#if FREERDP_CHECK_VERSION(2, 3, 0)
+static gchar *remmina_get_rdp_kbd_remap(const gchar *keymap)
+{
+ TRACE_CALL(__func__);
+ guint *table;
+ gchar keys[20];
+ gchar *rdp_kbd_remap = NULL;
+ gint i;
+ Display *display;
+
+ table = remmina_plugin_service->pref_keymap_get_table(keymap);
+ if (!table)
+ return rdp_kbd_remap;
+ rdp_kbd_remap = g_malloc0(512);
+ display = XOpenDisplay(0);
+ for (i = 0; table[i] > 0; i += 2) {
+ g_snprintf(keys, sizeof(keys), "0x%02x=0x%02x", freerdp_keyboard_get_rdp_scancode_from_x11_keycode(XKeysymToKeycode(display, table[i])),
+ freerdp_keyboard_get_rdp_scancode_from_x11_keycode(XKeysymToKeycode(display, table[i + 1])));
+ if (i > 0)
+ g_strlcat(rdp_kbd_remap, ",", 512);
+ g_strlcat(rdp_kbd_remap, keys, 512);
+ }
+ XCloseDisplay(display);
+
+ return rdp_kbd_remap;
+}
+#endif
+#endif
+
+static gboolean remmina_rdp_main(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ const gchar *s;
+ gchar *sm;
+ gchar *value;
+ const gchar *cs;
+ RemminaFile *remminafile;
+ rfContext *rfi = GET_PLUGIN_DATA(gp);
+ rdpChannels *channels;
+ gchar *gateway_host;
+ gint gateway_port;
+ gchar *datapath = NULL;
+ gboolean status = TRUE;
+#ifdef GDK_WINDOWING_X11
+ gchar *rdp_kbd_remap;
+#endif
+ gint i;
+
+ gint desktopOrientation, desktopScaleFactor, deviceScaleFactor;
+
+ channels = rfi->clientContext.context.channels;
+
+ remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ datapath = g_build_path("/",
+ remmina_plugin_service->file_get_user_datadir(),
+ "RDP",
+ NULL);
+ REMMINA_PLUGIN_DEBUG("RDP data path is %s", datapath);
+
+ if ((datapath != NULL) && (datapath[0] != '\0'))
+ if (access(datapath, W_OK) == 0)
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_ConfigPath, datapath);
+ g_free(datapath);
+
+ if (remmina_plugin_service->file_get_int(remminafile, "assistance_mode", 0)){
+ rdpAssistanceFile* file = freerdp_assistance_file_new();
+ if (!file){
+ REMMINA_PLUGIN_DEBUG("Could not allocate assistance file structure");
+ return FALSE;
+ }
+
+ if (remmina_plugin_service->file_get_string(remminafile, "assistance_file") == NULL ||
+ remmina_plugin_service->file_get_string(remminafile, "assistance_pass") == NULL ){
+
+ REMMINA_PLUGIN_DEBUG("Assistance file and password are not set while assistance mode is on");
+ return FALSE;
+ }
+
+ status = freerdp_assistance_parse_file(file,
+ remmina_plugin_service->file_get_string(remminafile, "assistance_file"),
+ remmina_plugin_service->file_get_string(remminafile, "assistance_pass"));
+
+ if (status < 0){
+ REMMINA_PLUGIN_DEBUG("Could not parse assistance file");
+ return FALSE;
+ }
+
+
+ if (!freerdp_assistance_populate_settings_from_assistance_file(file, rfi->clientContext.context.settings)){
+ REMMINA_PLUGIN_DEBUG("Could not populate settings from assistance file");
+ return FALSE;
+ }
+ }
+
+
+#if defined(PROXY_TYPE_IGNORE)
+ if (!remmina_plugin_service->file_get_int(remminafile, "useproxyenv", FALSE) ? TRUE : FALSE) {
+ REMMINA_PLUGIN_DEBUG("Not using system proxy settings");
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ProxyType, PROXY_TYPE_IGNORE);
+ }
+#endif
+
+ if (!remmina_rdp_tunnel_init(gp))
+ return FALSE;
+
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_AutoReconnectionEnabled, (remmina_plugin_service->file_get_int(remminafile, "disableautoreconnect", FALSE) ? FALSE : TRUE));
+ /* Disable RDP auto reconnection when SSH tunnel is enabled */
+ if (remmina_plugin_service->file_get_int(remminafile, "ssh_tunnel_enabled", FALSE))
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_AutoReconnectionEnabled, FALSE);
+
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth, remmina_plugin_service->file_get_int(remminafile, "colordepth", 99));
+
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_SoftwareGdi, TRUE);
+ REMMINA_PLUGIN_DEBUG("gfx_h264_available: %d", gfx_h264_available);
+
+ /* Avoid using H.264 modes if they are not available on libfreerdp */
+ if (!gfx_h264_available && (freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth) == 65 || freerdp_settings_get_bool(rfi->clientContext.context.settings, FreeRDP_ColorDepth == 66)))
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth, 64); // Fallback to GFX RFX
+
+ if (freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth) == 0) {
+ /* RFX (Win7)*/
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RemoteFxCodec, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_SupportGraphicsPipeline, FALSE);
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth, 32);
+ } else if (freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth) == 63) {
+ /* /gfx (RFX Progressive) (Win8) */
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth, 32);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_SupportGraphicsPipeline, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GfxH264, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GfxAVC444, FALSE);
+ } else if (freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth) == 64) {
+ /* /gfx:rfx (Win8) */
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth, 32);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RemoteFxCodec, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_SupportGraphicsPipeline, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GfxH264, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GfxAVC444, FALSE);
+ } else if (freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth) == 65) {
+ /* /gfx:avc420 (Win8.1) */
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth, 32);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_SupportGraphicsPipeline, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GfxH264, gfx_h264_available);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GfxAVC444, FALSE);
+ } else if (freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth) == 66) {
+ /* /gfx:avc444 (Win10) */
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth, 32);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_SupportGraphicsPipeline, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GfxH264, gfx_h264_available);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GfxAVC444, gfx_h264_available);
+ } else if (freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth) == 99) {
+ /* Automatic (Let the server choose its best format) */
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth, 32);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RemoteFxCodec, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_SupportGraphicsPipeline, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GfxH264, gfx_h264_available);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GfxAVC444, gfx_h264_available);
+ }
+
+ if (freerdp_settings_get_bool(rfi->clientContext.context.settings, FreeRDP_RemoteFxCodec) ||
+ freerdp_settings_get_bool(rfi->clientContext.context.settings, FreeRDP_NSCodec) ||
+ freerdp_settings_get_bool(rfi->clientContext.context.settings, FreeRDP_SupportGraphicsPipeline)) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_FastPathOutput, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_FrameMarkerCommandEnabled, TRUE);
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ColorDepth, 32);
+ rfi->bpp = 32;
+ }
+
+ gint w = remmina_plugin_service->get_profile_remote_width(gp);
+ gint h = remmina_plugin_service->get_profile_remote_height(gp);
+ /* multiple of 4 */
+ w = (w + 3) & ~0x3;
+ h = (h + 3) & ~0x3;
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopWidth, w);
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopHeight, h);
+ REMMINA_PLUGIN_DEBUG("Resolution set by the user: %dx%d", w, h);
+
+ /* Workaround for FreeRDP issue #5417: in GFX AVC modes we can't go under
+ * AVC_MIN_DESKTOP_WIDTH x AVC_MIN_DESKTOP_HEIGHT */
+ if (freerdp_settings_get_bool(rfi->clientContext.context.settings, FreeRDP_SupportGraphicsPipeline) &&
+ freerdp_settings_get_bool(rfi->clientContext.context.settings, FreeRDP_GfxH264)) {
+ if (freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopWidth) <
+ AVC_MIN_DESKTOP_WIDTH)
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopWidth,
+ AVC_MIN_DESKTOP_WIDTH);
+ if (freerdp_settings_get_uint32(rfi->clientContext.context.settings,
+ FreeRDP_DesktopHeight) <
+ AVC_MIN_DESKTOP_HEIGHT)
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopHeight,
+ AVC_MIN_DESKTOP_HEIGHT);
+ }
+
+ /* Workaround for FreeRDP issue #5119. This will make our horizontal resolution
+ * an even value, but it will add a vertical black 1 pixel line on the
+ * right of the desktop */
+ if ((freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopWidth) & 1) != 0) {
+ UINT32 tmp = freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopWidth);
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopWidth, tmp - 1);
+ }
+
+ remmina_plugin_service->protocol_plugin_set_width(gp, freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopWidth));
+ remmina_plugin_service->protocol_plugin_set_height(gp, freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopHeight));
+
+ w = freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopWidth);
+ h = freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopHeight);
+ REMMINA_PLUGIN_DEBUG("Resolution set after workarounds: %dx%d", w, h);
+
+
+ if (remmina_plugin_service->file_get_string(remminafile, "username"))
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_Username, remmina_plugin_service->file_get_string(remminafile, "username"));
+
+ if (remmina_plugin_service->file_get_string(remminafile, "domain"))
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_Domain, remmina_plugin_service->file_get_string(remminafile, "domain"));
+
+ s = remmina_plugin_service->file_get_string(remminafile, "password");
+ if (s) freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_Password, s);
+
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_AutoLogonEnabled, TRUE);
+
+ /**
+ * Proxy support
+ * Proxy settings are hidden at the moment as an advanced feature
+ */
+ gchar *proxy_type = g_strdup(remmina_plugin_service->file_get_string(remminafile, "proxy_type"));
+ gchar *proxy_username = g_strdup(remmina_plugin_service->file_get_string(remminafile, "proxy_username"));
+ gchar *proxy_password = g_strdup(remmina_plugin_service->file_get_string(remminafile, "proxy_password"));
+ gchar *proxy_hostname = g_strdup(remmina_plugin_service->file_get_string(remminafile, "proxy_hostname"));
+ gint proxy_port = remmina_plugin_service->file_get_int(remminafile, "proxy_port", 80);
+ REMMINA_PLUGIN_DEBUG("proxy_type: %s", proxy_type);
+ REMMINA_PLUGIN_DEBUG("proxy_username: %s", proxy_username);
+ REMMINA_PLUGIN_DEBUG("proxy_password: %s", proxy_password);
+ REMMINA_PLUGIN_DEBUG("proxy_hostname: %s", proxy_hostname);
+ REMMINA_PLUGIN_DEBUG("proxy_port: %d", proxy_port);
+ if (proxy_type && proxy_hostname) {
+ if (g_strcmp0(proxy_type, "no_proxy") == 0)
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ProxyType, PROXY_TYPE_IGNORE);
+ else if (g_strcmp0(proxy_type, "http") == 0)
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP);
+ else if (g_strcmp0(proxy_type, "socks5") == 0)
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ProxyType, PROXY_TYPE_SOCKS);
+ else
+ g_warning("Invalid proxy protocol, at the moment only no_proxy, HTTP and SOCKS5 are supported");
+ REMMINA_PLUGIN_DEBUG("ProxyType set to: %" PRIu32, freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_ProxyType));
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_ProxyHostname, proxy_hostname);
+ if (proxy_username)
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_ProxyUsername, proxy_username);
+ if (proxy_password)
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_ProxyPassword, proxy_password);
+ if (proxy_port)
+ freerdp_settings_set_uint16(rfi->clientContext.context.settings, FreeRDP_ProxyPort, proxy_port);
+ }
+ g_free(proxy_hostname);
+ g_free(proxy_username);
+ g_free(proxy_password);
+
+ if (remmina_plugin_service->file_get_int(remminafile, "base-cred-for-gw", FALSE)) {
+ // Reset gateway credentials
+ remmina_plugin_service->file_set_string(remminafile, "gateway_username", NULL);
+ remmina_plugin_service->file_set_string(remminafile, "gateway_domain", NULL);
+ remmina_plugin_service->file_set_string(remminafile, "gateway_password", NULL);
+ }
+
+ /* Remote Desktop Gateway server address */
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayEnabled, FALSE);
+ s = remmina_plugin_service->file_get_string(remminafile, "gateway_server");
+ if (s) {
+ cs = remmina_plugin_service->file_get_string(remminafile, "gwtransp");
+#if FREERDP_CHECK_VERSION(2, 3, 1)
+ if (remmina_plugin_service->file_get_int(remminafile, "websockets", FALSE))
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayHttpUseWebsockets, TRUE);
+ else
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayHttpUseWebsockets, FALSE);
+#endif
+ if (g_strcmp0(cs, "http") == 0) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayRpcTransport, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayHttpTransport, TRUE);
+ } else if (g_strcmp0(cs, "rpc") == 0) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayRpcTransport, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayHttpTransport, FALSE);
+ } else if (g_strcmp0(cs, "auto") == 0) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayRpcTransport, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayHttpTransport, TRUE);
+ }
+ remmina_plugin_service->get_server_port(s, 443, &gateway_host, &gateway_port);
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayHostname, gateway_host);
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_GatewayPort, gateway_port);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayEnabled, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayUseSameCredentials, TRUE);
+ }
+ /* Remote Desktop Gateway domain */
+ if (remmina_plugin_service->file_get_string(remminafile, "gateway_domain")) {
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayDomain, remmina_plugin_service->file_get_string(remminafile, "gateway_domain"));
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayUseSameCredentials, FALSE);
+ }
+ /* Remote Desktop Gateway username */
+ if (remmina_plugin_service->file_get_string(remminafile, "gateway_username")) {
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayUsername, remmina_plugin_service->file_get_string(remminafile, "gateway_username"));
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayUseSameCredentials, FALSE);
+ }
+ /* Remote Desktop Gateway password */
+ s = remmina_plugin_service->file_get_string(remminafile, "gateway_password");
+ if (s) {
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayPassword, s);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayUseSameCredentials, FALSE);
+ }
+ /* If no different credentials were provided for the Remote Desktop Gateway
+ * use the same authentication credentials for the host */
+ if (freerdp_settings_get_bool(rfi->clientContext.context.settings, FreeRDP_GatewayEnabled) && freerdp_settings_get_bool(rfi->clientContext.context.settings, FreeRDP_GatewayUseSameCredentials)) {
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayDomain, freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_Domain));
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayUsername, freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_Username));
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayPassword, freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_Password));
+ }
+ /* Remote Desktop Gateway usage */
+ if (freerdp_settings_get_bool(rfi->clientContext.context.settings, FreeRDP_GatewayEnabled))
+ freerdp_set_gateway_usage_method(rfi->clientContext.context.settings,
+ remmina_plugin_service->file_get_int(remminafile, "gateway_usage", FALSE) ? TSC_PROXY_MODE_DETECT : TSC_PROXY_MODE_DIRECT);
+
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayAccessToken,
+ remmina_plugin_service->file_get_string(remminafile, "gatewayaccesstoken"));
+
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_AuthenticationLevel, remmina_plugin_service->file_get_int(
+ remminafile, "authentication level", freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_AuthenticationLevel)));
+
+ /* Certificate ignore */
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_IgnoreCertificate, remmina_plugin_service->file_get_int(remminafile, "cert_ignore", 0));
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_OldLicenseBehaviour, remmina_plugin_service->file_get_int(remminafile, "old-license", 0));
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_AllowUnanouncedOrdersFromServer, remmina_plugin_service->file_get_int(remminafile, "relax-order-checks", 0));
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_GlyphSupportLevel, (remmina_plugin_service->file_get_int(remminafile, "glyph-cache", 0) ? GLYPH_SUPPORT_FULL : GLYPH_SUPPORT_NONE));
+
+ if ((cs = remmina_plugin_service->file_get_string(remminafile, "clientname")))
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_ClientHostname, cs);
+ else
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_ClientHostname, g_get_host_name());
+
+ /* Client Build number is optional, if not specified defaults to 0, allow for comments to appear after number */
+ if ((cs = remmina_plugin_service->file_get_string(remminafile, "clientbuild"))) {
+ if (*cs) {
+ UINT32 val = strtoul(cs, NULL, 0);
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_ClientBuild, val);
+ }
+ }
+
+
+ if (remmina_plugin_service->file_get_string(remminafile, "loadbalanceinfo")) {
+ const gchar *tmp = strdup(remmina_plugin_service->file_get_string(remminafile, "loadbalanceinfo"));
+ freerdp_settings_set_pointer_len(rfi->clientContext.context.settings, FreeRDP_LoadBalanceInfo, tmp, strlen(tmp) + 1);
+ }
+
+ if (remmina_plugin_service->file_get_string(remminafile, "exec"))
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_AlternateShell, remmina_plugin_service->file_get_string(remminafile, "exec"));
+
+ if (remmina_plugin_service->file_get_string(remminafile, "execpath"))
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_ShellWorkingDirectory, remmina_plugin_service->file_get_string(remminafile, "execpath"));
+
+ sm = g_strdup_printf("rdp_quality_%i", remmina_plugin_service->file_get_int(remminafile, "quality", DEFAULT_QUALITY_0));
+ value = remmina_plugin_service->pref_get_value(sm);
+ g_free(sm);
+
+ if (value && value[0]) {
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_PerformanceFlags, strtoul(value, NULL, 16));
+ } else {
+ switch (remmina_plugin_service->file_get_int(remminafile, "quality", DEFAULT_QUALITY_0)) {
+ case 9:
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_PerformanceFlags, DEFAULT_QUALITY_9);
+ break;
+
+ case 2:
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_PerformanceFlags, DEFAULT_QUALITY_2);
+ break;
+
+ case 1:
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_PerformanceFlags, DEFAULT_QUALITY_1);
+ break;
+
+ case 0:
+ default:
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_PerformanceFlags, DEFAULT_QUALITY_0);
+ break;
+ }
+ }
+ g_free(value);
+
+ if ((cs = remmina_plugin_service->file_get_string(remminafile, "network"))) {
+ guint32 type = 0;
+
+ if (g_strcmp0(cs, "modem") == 0)
+ type = CONNECTION_TYPE_MODEM;
+ else if (g_strcmp0(cs, "broadband") == 0)
+ type = CONNECTION_TYPE_BROADBAND_HIGH;
+ else if (g_strcmp0(cs, "broadband-low") == 0)
+ type = CONNECTION_TYPE_BROADBAND_LOW;
+ else if (g_strcmp0(cs, "broadband-high") == 0)
+ type = CONNECTION_TYPE_BROADBAND_HIGH;
+ else if (g_strcmp0(cs, "wan") == 0)
+ type = CONNECTION_TYPE_WAN;
+ else if (g_strcmp0(cs, "lan") == 0)
+ type = CONNECTION_TYPE_LAN;
+ else if ((g_strcmp0(cs, "autodetect") == 0))
+ type = CONNECTION_TYPE_AUTODETECT;
+ else if ((g_strcmp0(cs, "none") == 0))
+ type = REMMINA_CONNECTION_TYPE_NONE;
+ else
+ type = REMMINA_CONNECTION_TYPE_NONE;
+
+ if (!remmina_rdp_set_connection_type(rfi->clientContext.context.settings, type))
+ REMMINA_PLUGIN_DEBUG("Network settings not set");
+ }
+
+ /* PerformanceFlags bitmask need also to be splitted into BOOL variables
+ * like freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_DisableWallpaper, freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_AllowFontSmoothing…
+ * or freerdp_get_param_bool() function will return the wrong value
+ */
+ freerdp_performance_flags_split(rfi->clientContext.context.settings);
+
+#ifdef GDK_WINDOWING_X11
+#if FREERDP_CHECK_VERSION(2, 3, 0)
+ rdp_kbd_remap = remmina_get_rdp_kbd_remap(remmina_plugin_service->file_get_string(remminafile, "keymap"));
+ if (rdp_kbd_remap != NULL) {
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_KeyboardRemappingList, rdp_kbd_remap);
+ REMMINA_PLUGIN_DEBUG(
+ "rdp_keyboard_remapping_list: %s",
+ freerdp_settings_get_string(rfi->clientContext.context.settings,
+ FreeRDP_KeyboardRemappingList));
+ g_free(rdp_kbd_remap);
+ }
+ else {
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_KeyboardRemappingList, remmina_plugin_service->pref_get_value("rdp_kbd_remap"));
+ REMMINA_PLUGIN_DEBUG(
+ "rdp_keyboard_remapping_list: %s",
+ freerdp_settings_get_string(rfi->clientContext.context.settings,
+ FreeRDP_KeyboardRemappingList));
+ }
+#endif
+#endif
+
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_KeyboardLayout, remmina_rdp_settings_get_keyboard_layout());
+
+ if (remmina_plugin_service->file_get_int(remminafile, "console", FALSE))
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_ConsoleSession, TRUE);
+
+ if (remmina_plugin_service->file_get_int(remminafile, "restricted-admin", FALSE)) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_ConsoleSession, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RestrictedAdminModeRequired, TRUE);
+ }
+
+ if (remmina_plugin_service->file_get_string(remminafile, "pth")) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_ConsoleSession, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RestrictedAdminModeRequired, TRUE);
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_PasswordHash, remmina_plugin_service->file_get_string(remminafile, "pth"));
+ remmina_plugin_service->file_set_int(remminafile, "restricted-admin", TRUE);
+ }
+
+ cs = remmina_plugin_service->file_get_string(remminafile, "security");
+ if (g_strcmp0(cs, "rdp") == 0) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RdpSecurity, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_TlsSecurity, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_NlaSecurity, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_ExtSecurity, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_UseRdpSecurityLayer, TRUE);
+ } else if (g_strcmp0(cs, "tls") == 0) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RdpSecurity, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_TlsSecurity, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_NlaSecurity, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_ExtSecurity, FALSE);
+ } else if (g_strcmp0(cs, "nla") == 0) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RdpSecurity, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_TlsSecurity, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_NlaSecurity, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_ExtSecurity, FALSE);
+ } else if (g_strcmp0(cs, "ext") == 0) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RdpSecurity, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_TlsSecurity, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_NlaSecurity, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_ExtSecurity, TRUE);
+ } else {
+ /* This is "-nego" switch of xfreerdp */
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_NegotiateSecurityLayer, TRUE);
+ }
+
+ cs = remmina_plugin_service->file_get_string(remminafile, "tls-seclevel");
+ if (cs && g_strcmp0(cs,"")!=0) {
+ i = atoi(cs);
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_TlsSecLevel, i);
+ }
+
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_CompressionEnabled, TRUE);
+ if (remmina_plugin_service->file_get_int(remminafile, "disable_fastpath", FALSE)) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_FastPathInput, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_FastPathOutput, FALSE);
+ } else {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_FastPathInput, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_FastPathOutput, TRUE);
+ }
+
+ /* Orientation and scaling settings */
+ remmina_rdp_settings_get_orientation_scale_prefs(&desktopOrientation, &desktopScaleFactor, &deviceScaleFactor);
+
+ freerdp_settings_set_uint16(rfi->clientContext.context.settings, FreeRDP_DesktopOrientation, desktopOrientation);
+ if (desktopScaleFactor != 0 && deviceScaleFactor != 0) {
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopScaleFactor, desktopScaleFactor);
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_DeviceScaleFactor, deviceScaleFactor);
+ }
+
+ /* Try to enable "Display Control Virtual Channel Extension", needed to
+ * dynamically resize remote desktop. This will automatically open
+ * the "disp" dynamic channel, if available */
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_SupportDisplayControl, TRUE);
+
+ if (freerdp_settings_get_bool(rfi->clientContext.context.settings, FreeRDP_SupportDisplayControl)) {
+ CLPARAM *d[1];
+ int dcount;
+
+ dcount = 1;
+ d[0] = "disp";
+ freerdp_client_add_dynamic_channel(rfi->clientContext.context.settings, dcount, d);
+ }
+
+ if (freerdp_settings_get_bool(rfi->clientContext.context.settings, FreeRDP_SupportGraphicsPipeline)) {
+ CLPARAM *d[1];
+
+ int dcount;
+
+ dcount = 1;
+ d[0] = "rdpgfx";
+ freerdp_client_add_dynamic_channel(rfi->clientContext.context.settings, dcount, d);
+ }
+
+ /* Sound settings */
+ cs = remmina_plugin_service->file_get_string(remminafile, "sound");
+ if (g_strcmp0(cs, "remote") == 0) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RemoteConsoleAudio, TRUE);
+ } else if ((cs != NULL && cs[0] != '\0') && g_str_has_prefix(cs, "local")) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_AudioPlayback, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_AudioCapture, TRUE);
+ } else {
+ /* Disable sound */
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_AudioPlayback, FALSE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RemoteConsoleAudio, FALSE);
+ }
+
+ cs = remmina_plugin_service->file_get_string(remminafile, "microphone");
+ if (cs != NULL && cs[0] != '\0') {
+ if (g_strcmp0(cs, "0") == 0) {
+ REMMINA_PLUGIN_DEBUG("“microphone” was set to 0, setting to \"\"");
+ remmina_plugin_service->file_set_string(remminafile, "microphone", "");
+ } else {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_AudioCapture, TRUE);
+ REMMINA_PLUGIN_DEBUG("“microphone” set to “%s”", cs);
+ CLPARAM **p;
+ size_t count;
+
+ p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("audin", g_strdup(cs), &count);
+
+ freerdp_client_add_dynamic_channel(rfi->clientContext.context.settings, count, p);
+ g_free(p);
+ }
+ }
+
+ cs = remmina_plugin_service->file_get_string(remminafile, "audio-output");
+ if (cs != NULL && cs[0] != '\0') {
+ REMMINA_PLUGIN_DEBUG("audio output set to %s", cs);
+ CLPARAM **p;
+ size_t count;
+
+ p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("rdpsnd", g_strdup(cs), &count);
+ status = freerdp_client_add_static_channel(rfi->clientContext.context.settings, count, p);
+ if (status)
+ status = freerdp_client_add_dynamic_channel(rfi->clientContext.context.settings, count, p);
+ g_free(p);
+ }
+
+
+ cs = remmina_plugin_service->file_get_string(remminafile, "freerdp_log_level");
+ if (cs != NULL && cs[0] != '\0')
+ REMMINA_PLUGIN_DEBUG("Log level set to to %s", cs);
+ else
+ cs = g_strdup("INFO");
+ wLog *root = WLog_GetRoot();
+ WLog_SetStringLogLevel(root, cs);
+
+ cs = remmina_plugin_service->file_get_string(remminafile, "freerdp_log_filters");
+ if (cs != NULL && cs[0] != '\0') {
+ REMMINA_PLUGIN_DEBUG("Log filters set to to %s", cs);
+ WLog_AddStringLogFilters(cs);
+ } else {
+ WLog_AddStringLogFilters(NULL);
+ }
+
+
+ cs = remmina_plugin_service->file_get_string(remminafile, "usb");
+ if (cs != NULL && cs[0] != '\0') {
+ CLPARAM **p;
+ size_t count;
+ p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("urbdrc", g_strdup(cs), &count);
+ freerdp_client_add_dynamic_channel(rfi->clientContext.context.settings, count, p);
+ g_free(p);
+ }
+
+ cs = remmina_plugin_service->file_get_string(remminafile, "vc");
+ if (cs != NULL && cs[0] != '\0') {
+ CLPARAM **p;
+ size_t count;
+ p = remmina_rdp_CommandLineParseCommaSeparatedValues(g_strdup(cs), &count);
+ freerdp_client_add_static_channel(rfi->clientContext.context.settings, count, p);
+ g_free(p);
+ }
+
+ cs = remmina_plugin_service->file_get_string(remminafile, "dvc");
+ if (cs != NULL && cs[0] != '\0') {
+ CLPARAM **p;
+ size_t count;
+ p = remmina_rdp_CommandLineParseCommaSeparatedValues(g_strdup(cs), &count);
+ freerdp_client_add_dynamic_channel(rfi->clientContext.context.settings, count, p);
+ g_free(p);
+ }
+
+ cs = remmina_plugin_service->file_get_string(remminafile, RDP2TCP_DVC_CHANNEL_NAME);
+ if (cs != NULL && cs[0] != '\0') {
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_RDP2TCPArgs, cs);
+
+#if FREERDP_VERSION_MAJOR >= 3
+ char* args = freerdp_settings_get_string_writable(rfi->clientContext.context.settings, FreeRDP_RDP2TCPArgs);
+#else
+ char* args = freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_RDP2TCPArgs);
+#endif
+ REMMINA_PLUGIN_DEBUG(RDP2TCP_DVC_CHANNEL_NAME " set to %s",args);
+ remmina_rdp_load_static_channel_addin(channels, rfi->clientContext.context.settings, RDP2TCP_DVC_CHANNEL_NAME, args);
+ }
+
+ int vermaj, vermin, verrev;
+ freerdp_get_version(&vermaj, &vermin, &verrev);
+
+#if FREERDP_CHECK_VERSION(2, 1, 0)
+ cs = remmina_plugin_service->file_get_string(remminafile, "timeout");
+ if (cs != NULL && cs[0] != '\0') {
+ const gchar *endptr = NULL;
+ guint64 val = g_ascii_strtoull(cs, (gchar **)&endptr, 10);
+ if (val > 600000 || val <= 0)
+ val = 600000;
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_TcpAckTimeout, (UINT32)val);
+ }
+#endif
+
+ if (remmina_plugin_service->file_get_int(remminafile, "preferipv6", FALSE) ? TRUE : FALSE)
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_PreferIPv6OverIPv4, TRUE);
+
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RedirectClipboard, remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE) ? FALSE : TRUE);
+
+ cs = remmina_plugin_service->file_get_string(remminafile, "sharefolder");
+ if (cs != NULL && cs[0] != '\0') {
+ gchar *ccs = g_strdup(cs);
+ REMMINA_PLUGIN_DEBUG("[Deprecated->migrating] - Old sharefolder %s to \"drive \"", ccs);
+ if (!remmina_plugin_service->file_get_string(remminafile, "drive")) {
+ remmina_plugin_service->file_set_string(remminafile, "drive", g_strdup(ccs));
+ remmina_plugin_service->file_set_string(remminafile, "sharefolder", NULL);
+ REMMINA_PLUGIN_DEBUG("[Deprecated->migrated] - drive set to %s", g_strdup(ccs));
+ }
+ g_free(ccs);
+ //CLPARAM **p;
+ //size_t count;
+ //p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("drive", g_strdup(cs), &count);
+ //status = freerdp_client_add_device_channel(rfi->clientContext.context.settings, count, p);
+ //g_free(p);
+ }
+ cs = remmina_plugin_service->file_get_string(remminafile, "drive");
+ if (cs != NULL && cs[0] != '\0') {
+ REMMINA_PLUGIN_DEBUG("Redirect directory set to %s", cs);
+ CLPARAM **p;
+ size_t count;
+
+ gchar **folders = g_strsplit(cs, ";", -1);
+ for (i = 0; folders[i] != NULL; i++) {
+ REMMINA_PLUGIN_DEBUG("Parsing folder %s", folders[i]);
+ p = remmina_rdp_CommandLineParseCommaSeparatedValuesEx("drive", g_strdup(folders[i]), &count);
+ status = freerdp_client_add_device_channel(rfi->clientContext.context.settings, count, p);
+ g_free(p);
+ }
+ g_strfreev(folders);
+ }
+
+ if (remmina_plugin_service->file_get_int(remminafile, "shareprinter", FALSE)) {
+#ifdef HAVE_CUPS
+ REMMINA_PLUGIN_DEBUG("Sharing printers");
+ const gchar *po = remmina_plugin_service->file_get_string(remminafile, "printer_overrides");
+ if (po && po[0] != 0) {
+ /* Fallback to remmina code to override print drivers */
+ if (cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, 0, 0, remmina_rdp_set_printers, rfi))
+ REMMINA_PLUGIN_DEBUG("All printers have been shared");
+ else
+ REMMINA_PLUGIN_DEBUG("Cannot share printers, are there any available?");
+ } else {
+ /* Use libfreerdp code to map all printers */
+ CLPARAM *d[1];
+ int dcount;
+ dcount = 1;
+ d[0] = "printer";
+ freerdp_client_add_device_channel(rfi->clientContext.context.settings, dcount, d);
+ }
+#endif /* HAVE_CUPS */
+ }
+
+ if (remmina_plugin_service->file_get_int(remminafile, "span", FALSE)) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_SpanMonitors, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_UseMultimon, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_Fullscreen, TRUE);
+ remmina_plugin_service->file_set_int(remminafile, "multimon", 1);
+ }
+
+ if (remmina_plugin_service->file_get_int(remminafile, "multimon", FALSE)) {
+ guint32 maxwidth = 0;
+ guint32 maxheight = 0;
+ gchar *monitorids;
+ guint32 i;
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_UseMultimon, TRUE);
+ /* TODO Add an option for this */
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_ForceMultimon, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_Fullscreen, TRUE);
+
+ gchar *monitorids_string = g_strdup(remmina_plugin_service->file_get_string(remminafile, "monitorids"));
+ /* Otherwise we get all the attached monitors
+ * monitorids may contains desktop orientation values.
+ * But before we check if there are orientation attributes
+ */
+ if (monitorids_string != NULL && monitorids_string[0] != '\0') {
+ if (g_strstr_len(monitorids_string, -1, ",") != NULL) {
+ if (g_strstr_len(monitorids_string, -1, ":") != NULL) {
+ rdpMonitor *base = (rdpMonitor *)freerdp_settings_get_pointer(rfi->clientContext.context.settings, FreeRDP_MonitorDefArray);
+ /* We have an ID and an orientation degree */
+ gchar **temp_items;
+ gchar **rot_items;
+ temp_items = g_strsplit(monitorids_string, ",", -1);
+ for (i = 0; i < g_strv_length(temp_items); i++) {
+ rot_items = g_strsplit(temp_items[i], ":", -1);
+ rdpMonitor *current = &base[atoi(rot_items[0])];
+ if (i == 0)
+ monitorids = g_strdup(rot_items[0]);
+ else
+ monitorids = g_strdup_printf("%s,%s", monitorids, rot_items[0]);
+ current->attributes.orientation = atoi(rot_items[1]);
+ REMMINA_PLUGIN_DEBUG("Monitor n %d orientation: %d", i, current->attributes.orientation);
+ }
+ } else {
+ monitorids = g_strdup(monitorids_string);
+ }
+ } else {
+ monitorids = g_strdup(monitorids_string);
+ }
+ } else {
+ monitorids = g_strdup(monitorids_string);
+ }
+ remmina_rdp_monitor_get(rfi, &monitorids, &maxwidth, &maxheight);
+ if (monitorids != NULL && monitorids[0] != '\0') {
+ UINT32 *base = (UINT32 *)freerdp_settings_get_pointer(rfi->clientContext.context.settings, FreeRDP_MonitorIds);
+ gchar **items;
+ items = g_strsplit(monitorids, ",", -1);
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_NumMonitorIds, g_strv_length(items));
+ REMMINA_PLUGIN_DEBUG("NumMonitorIds: %d", freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_NumMonitorIds));
+ for (i = 0; i < g_strv_length(items); i++) {
+ if (base != NULL){
+ UINT32 *current = &base[i];
+ *current = atoi(items[i]);
+ REMMINA_PLUGIN_DEBUG("Added monitor with ID %" PRIu32, *current);
+ }
+ }
+ g_free(monitorids);
+ g_strfreev(items);
+ }
+ if (maxwidth && maxheight) {
+ REMMINA_PLUGIN_DEBUG("Setting DesktopWidth and DesktopHeight to: %dx%d", maxwidth, maxheight);
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopWidth, maxwidth);
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopHeight, maxheight);
+ REMMINA_PLUGIN_DEBUG("DesktopWidth and DesktopHeight set to: %dx%d", freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopWidth), freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopHeight));
+ } else {
+ REMMINA_PLUGIN_DEBUG("Cannot set Desktop Size, we are using the previously set values: %dx%d", freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopWidth), freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopHeight));
+ }
+ remmina_plugin_service->protocol_plugin_set_width(gp, freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopWidth));
+ remmina_plugin_service->protocol_plugin_set_height(gp, freerdp_settings_get_uint32(rfi->clientContext.context.settings, FreeRDP_DesktopHeight));
+ }
+
+ const gchar *sn = remmina_plugin_service->file_get_string(remminafile, "smartcardname");
+ if (remmina_plugin_service->file_get_int(remminafile, "sharesmartcard", FALSE) ||
+ (sn != NULL && sn[0] != '\0')) {
+ RDPDR_SMARTCARD *smartcard;
+ smartcard = (RDPDR_SMARTCARD *)calloc(1, sizeof(RDPDR_SMARTCARD));
+
+#if FREERDP_VERSION_MAJOR >= 3
+ RDPDR_DEVICE *sdev;
+ sdev = &(smartcard->device);
+#else
+ RDPDR_SMARTCARD *sdev;
+ sdev = smartcard;
+#endif
+
+ sdev->Type = RDPDR_DTYP_SMARTCARD;
+
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_DeviceRedirection, TRUE);
+
+ if (sn != NULL && sn[0] != '\0')
+ sdev->Name = _strdup(sn);
+
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RedirectSmartCards, TRUE);
+
+ freerdp_device_collection_add(rfi->clientContext.context.settings, (RDPDR_DEVICE *)smartcard);
+ }
+
+ if (remmina_plugin_service->file_get_int(remminafile, "passwordispin", FALSE))
+ /* Option works only combined with Username and Domain, because FreeRDP
+ * doesn’t know anything about info on smart card */
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_PasswordIsSmartcardPin, TRUE);
+
+ /* /serial[:<name>[,<path>[,<driver>[,permissive]]]] */
+ if (remmina_plugin_service->file_get_int(remminafile, "shareserial", FALSE)) {
+ RDPDR_SERIAL *serial;
+ serial = (RDPDR_SERIAL *)calloc(1, sizeof(RDPDR_SERIAL));
+
+#if FREERDP_VERSION_MAJOR >= 3
+ RDPDR_DEVICE *sdev;
+ sdev = &(serial->device);
+#else
+ RDPDR_SERIAL *sdev;
+ sdev = serial;
+#endif
+
+ sdev->Type = RDPDR_DTYP_SERIAL;
+
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_DeviceRedirection, TRUE);
+
+ const gchar *sn = remmina_plugin_service->file_get_string(remminafile, "serialname");
+ if (sn != NULL && sn[0] != '\0')
+ sdev->Name = _strdup(sn);
+
+ const gchar *sd = remmina_plugin_service->file_get_string(remminafile, "serialdriver");
+ if (sd != NULL && sd[0] != '\0')
+ serial->Driver = _strdup(sd);
+
+ const gchar *sp = remmina_plugin_service->file_get_string(remminafile, "serialpath");
+ if (sp != NULL && sp[0] != '\0')
+ serial->Path = _strdup(sp);
+
+ if (remmina_plugin_service->file_get_int(remminafile, "serialpermissive", FALSE))
+ serial->Permissive = _strdup("permissive");
+
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RedirectSerialPorts, TRUE);
+
+ freerdp_device_collection_add(rfi->clientContext.context.settings, (RDPDR_DEVICE *)serial);
+ }
+
+ if (remmina_plugin_service->file_get_int(remminafile, "shareparallel", FALSE)) {
+ RDPDR_PARALLEL *parallel;
+ parallel = (RDPDR_PARALLEL *)calloc(1, sizeof(RDPDR_PARALLEL));
+
+#if FREERDP_VERSION_MAJOR >= 3
+ RDPDR_DEVICE *pdev;
+ pdev = &(parallel->device);
+#else
+ RDPDR_PARALLEL *pdev;
+ pdev = parallel;
+#endif
+
+ pdev->Type = RDPDR_DTYP_PARALLEL;
+
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_DeviceRedirection, TRUE);
+
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_RedirectParallelPorts, TRUE);
+
+ const gchar *pn = remmina_plugin_service->file_get_string(remminafile, "parallelname");
+ if (pn != NULL && pn[0] != '\0')
+ pdev->Name = _strdup(pn);
+ const gchar *dp = remmina_plugin_service->file_get_string(remminafile, "parallelpath");
+ if (dp != NULL && dp[0] != '\0')
+ parallel->Path = _strdup(dp);
+
+ freerdp_device_collection_add(rfi->clientContext.context.settings, (RDPDR_DEVICE *)parallel);
+ }
+
+ /**
+ * multitransport enables RDP8 UDP support
+ */
+ if (remmina_plugin_service->file_get_int(remminafile, "multitransport", FALSE)) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_DeviceRedirection, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_SupportMultitransport, TRUE);
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_MultitransportFlags,
+ (TRANSPORT_TYPE_UDP_FECR | TRANSPORT_TYPE_UDP_FECL | TRANSPORT_TYPE_UDP_PREFERRED));
+ } else {
+ freerdp_settings_set_uint32(rfi->clientContext.context.settings, FreeRDP_MultitransportFlags, 0);
+ }
+
+ /* If needed, force interactive authentication by deleting all authentication fields,
+ * forcing libfreerdp to call our callbacks for authentication.
+ * This usually happens from a second attempt of connection, never on the 1st one. */
+ if (rfi->attempt_interactive_authentication) {
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_Username, NULL);
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_Password, NULL);
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_Domain, NULL);
+
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayDomain, NULL);
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayUsername, NULL);
+ freerdp_settings_set_string(rfi->clientContext.context.settings, FreeRDP_GatewayPassword, NULL);
+
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_GatewayUseSameCredentials, FALSE);
+ }
+
+ gboolean orphaned;
+
+ if (!freerdp_connect(rfi->clientContext.context.instance)) {
+ orphaned = (GET_PLUGIN_DATA(rfi->protocol_widget) == NULL);
+ if (!orphaned) {
+ UINT32 e;
+
+ e = freerdp_get_last_error(&rfi->clientContext.context);
+
+ switch (e) {
+ case FREERDP_ERROR_AUTHENTICATION_FAILED:
+ case STATUS_LOGON_FAILURE: // wrong return code from FreeRDP introduced at the end of July 2016? (fixed with b86c0ba)
+#ifdef FREERDP_ERROR_CONNECT_LOGON_FAILURE
+ case FREERDP_ERROR_CONNECT_LOGON_FAILURE:
+#endif
+ /* Logon failure, will retry with interactive authentication */
+ rfi->attempt_interactive_authentication = TRUE;
+ break;
+ case STATUS_ACCOUNT_LOCKED_OUT:
+#ifdef FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT
+ case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT:
+#endif
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nAccount locked out."),
+ freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+ case STATUS_ACCOUNT_EXPIRED:
+#ifdef FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED
+ case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED:
+#endif
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nAccount expired."),
+ freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+ case STATUS_PASSWORD_EXPIRED:
+#ifdef FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED
+ case FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED:
+#endif
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nPassword expired."),
+ freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+ case STATUS_ACCOUNT_DISABLED:
+#ifdef FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED
+ case FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED:
+#endif
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nAccount disabled."),
+ freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+#ifdef FREERDP_ERROR_SERVER_INSUFFICIENT_PRIVILEGES
+ /* https://msdn.microsoft.com/en-us/library/ee392247.aspx */
+ case FREERDP_ERROR_SERVER_INSUFFICIENT_PRIVILEGES:
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nInsufficient user privileges."),
+ freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+#endif
+ case STATUS_ACCOUNT_RESTRICTION:
+#ifdef FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION
+ case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION:
+#endif
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nAccount restricted."),
+ freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+
+ case STATUS_PASSWORD_MUST_CHANGE:
+#ifdef FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE
+ case FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE:
+#endif
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not access the RDP server “%s”.\nChange user password before connecting."),
+ freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+
+ case FREERDP_ERROR_CONNECT_FAILED:
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Lost connection to the RDP server “%s”."), freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+ case FREERDP_ERROR_DNS_NAME_NOT_FOUND:
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not find the address for the RDP server “%s”."), freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+ case FREERDP_ERROR_TLS_CONNECT_FAILED:
+ remmina_plugin_service->protocol_plugin_set_error(gp,
+ _("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->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+ case FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED:
+ // TRANSLATORS: the placeholder may be either an IP/FQDN or a server hostname
+ 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->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+#ifdef FREERDP_ERROR_POST_CONNECT_FAILED
+ case FREERDP_ERROR_POST_CONNECT_FAILED:
+ /* remmina_rdp_post_connect() returned FALSE to libfreerdp. We saved the error on rfi->postconnect_error */
+ switch (rfi->postconnect_error) {
+ case REMMINA_POSTCONNECT_ERROR_OK:
+ /* We should never come here */
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Cannot connect to the RDP server “%s”."), freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+ case REMMINA_POSTCONNECT_ERROR_GDI_INIT:
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Could not start libfreerdp-gdi."));
+ break;
+ case REMMINA_POSTCONNECT_ERROR_NO_H264:
+ 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->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+ }
+ break;
+#endif
+#ifdef FREERDP_ERROR_SERVER_DENIED_CONNECTION
+ case FREERDP_ERROR_SERVER_DENIED_CONNECTION:
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("The “%s” server refused the connection."), freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+#endif
+ case 0x800759DB:
+ // E_PROXY_NAP_ACCESSDENIED https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tsgu/84cd92e4-592c-4219-95d8-18021ac654b0
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("The Remote Desktop Gateway “%s” denied the user “%s\\%s” access due to policy."),
+ freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_GatewayHostname), freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_GatewayDomain), freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_GatewayUsername));
+ break;
+
+ case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS:
+ rfi->user_cancelled = TRUE;
+ break;
+
+ default:
+ g_printf("libfreerdp returned code is %08X\n", e);
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Cannot connect to the “%s” RDP server."), freerdp_settings_get_string(rfi->clientContext.context.settings, FreeRDP_ServerHostname));
+ break;
+ }
+ }
+
+ return FALSE;
+ }
+
+ if (GET_PLUGIN_DATA(rfi->protocol_widget) == NULL) orphaned = true; else orphaned = false;
+ if (!orphaned && freerdp_get_last_error(&rfi->clientContext.context) == FREERDP_ERROR_SUCCESS && !rfi->user_cancelled)
+ remmina_rdp_main_loop(gp);
+
+ return TRUE;
+}
+
+static void rfi_uninit(rfContext *rfi)
+{
+ freerdp *instance;
+
+ instance = rfi->clientContext.context.instance;
+
+ if (rfi->remmina_plugin_thread) {
+ rfi->thread_cancelled = TRUE; // Avoid all rf_queue function to run
+ pthread_cancel(rfi->remmina_plugin_thread);
+ if (rfi->remmina_plugin_thread)
+ pthread_join(rfi->remmina_plugin_thread, NULL);
+ }
+
+ if (instance) {
+ if (rfi->connected) {
+ freerdp_abort_connect_context(&rfi->clientContext.context);
+ rfi->connected = false;
+ }
+ }
+
+ if (instance) {
+ RDP_CLIENT_ENTRY_POINTS *pEntryPoints = instance->pClientEntryPoints;
+ if (pEntryPoints)
+ IFCALL(pEntryPoints->GlobalUninit);
+ free(instance->pClientEntryPoints);
+ freerdp_context_free(instance); /* context is rfContext* rfi */
+ freerdp_free(instance); /* This implicitly frees instance->context and rfi is no longer valid */
+ }
+}
+
+static gboolean complete_cleanup_on_main_thread(gpointer data)
+{
+ TRACE_CALL(__func__);
+
+ gboolean orphaned;
+ rfContext *rfi = (rfContext *)data;
+ RemminaProtocolWidget *gp;
+
+ remmina_rdp_clipboard_free(rfi);
+
+ gdi_free(rfi->clientContext.context.instance);
+
+ gp = rfi->protocol_widget;
+ if (GET_PLUGIN_DATA(gp) == NULL) orphaned = true; else orphaned = false;
+
+ remmina_rdp_cliprdr_detach_owner(gp);
+ if (!orphaned) remmina_rdp_event_uninit(gp);
+
+ if (!orphaned) g_object_steal_data(G_OBJECT(gp), "plugin-data");
+
+ rfi_uninit(rfi);
+
+ /* Notify the RemminaProtocolWidget that we closed our connection, and the GUI interface
+ * can be removed */
+ if (!orphaned)
+ remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gpointer remmina_rdp_main_thread(gpointer data)
+{
+ TRACE_CALL(__func__);
+ RemminaProtocolWidget *gp;
+ rfContext *rfi;
+
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ CANCEL_ASYNC
+
+ gp = (RemminaProtocolWidget *)data;
+
+ rfi = GET_PLUGIN_DATA(gp);
+
+ rfi->attempt_interactive_authentication = FALSE;
+ do
+ remmina_rdp_main(gp);
+ while (!remmina_plugin_service->protocol_plugin_has_error(gp) && rfi->attempt_interactive_authentication == TRUE && !rfi->user_cancelled);
+
+ rfi->remmina_plugin_thread = 0;
+
+ /* cleanup */
+ g_idle_add(complete_cleanup_on_main_thread, (gpointer)rfi);
+
+
+ return NULL;
+}
+
+static void remmina_rdp_init(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ freerdp *instance;
+ rfContext *rfi;
+
+ instance = freerdp_new();
+ instance->PreConnect = remmina_rdp_pre_connect;
+ instance->PostConnect = remmina_rdp_post_connect;
+ instance->PostDisconnect = remmina_rdp_post_disconnect;
+ //instance->VerifyCertificate = remmina_rdp_verify_certificate;
+ instance->VerifyCertificateEx = remmina_rdp_verify_certificate_ex;
+ //instance->VerifyChangedCertificate = remmina_rdp_verify_changed_certificate;
+ instance->VerifyChangedCertificateEx = remmina_rdp_verify_changed_certificate_ex;
+#if FREERDP_VERSION_MAJOR >= 3
+ instance->AuthenticateEx = remmina_rdp_authenticate_ex;
+ instance->ChooseSmartcard = remmina_rdp_choose_smartcard;
+ instance->GetAccessToken = remmina_rdp_get_access_token;
+ instance->LoadChannels = freerdp_client_load_channels;
+ instance->PresentGatewayMessage = remmina_rdp_present_gateway_message;
+ instance->LogonErrorInfo = remmina_rdp_logon_error_info;
+ instance->RetryDialog = remmina_rdp_retry_dialog;
+ instance->PostFinalDisconnect = remmina_rdp_post_final_disconnect;
+#else
+ instance->Authenticate = remmina_rdp_authenticate;
+ instance->GatewayAuthenticate = remmina_rdp_gw_authenticate;
+#endif
+
+ instance->ContextSize = sizeof(rfContext);
+ freerdp_context_new(instance);
+ rfi = (rfContext *)instance->context;
+
+ g_object_set_data_full(G_OBJECT(gp), "plugin-data", rfi, free);
+
+ rfi->protocol_widget = gp;
+ rfi->clientContext.context.settings = instance->context->settings;
+ rfi->connected = false;
+ rfi->is_reconnecting = false;
+ rfi->stop_reconnecting_requested = false;
+ rfi->user_cancelled = FALSE;
+ rfi->last_x = 0;
+ rfi->last_y = 0;
+
+ freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0);
+
+ remmina_rdp_event_init(gp);
+}
+
+static gboolean remmina_rdp_open_connection(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ rfContext *rfi = GET_PLUGIN_DATA(gp);
+ RemminaFile *remminafile;
+ const gchar *profile_name, *p;
+ gchar thname[16], c;
+ gint nthname = 0;
+
+ rfi->scale = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
+
+ remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+
+ if (pthread_create(&rfi->remmina_plugin_thread, NULL, remmina_rdp_main_thread, gp)) {
+ remmina_plugin_service->protocol_plugin_set_error(gp, "%s",
+ "Could not start pthread.");
+
+ rfi->remmina_plugin_thread = 0;
+
+ return FALSE;
+ }
+
+ /* Generate a thread name to be used with pthread_setname_np() for debugging */
+ profile_name = remmina_plugin_service->file_get_string(remminafile, "name");
+ p = profile_name;
+ strcpy(thname, "RemmRDP:");
+ if (p) {
+ nthname = strlen(thname);
+ while ((c = *p) != 0 && nthname < sizeof(thname) - 1) {
+ if (isalnum(c))
+ thname[nthname++] = c;
+ p++;
+ }
+ } else {
+ strcat(thname, "<NONAM>");
+ nthname = strlen(thname);
+ }
+ thname[nthname] = 0;
+#if defined(__linux__)
+ pthread_setname_np(rfi->remmina_plugin_thread, thname);
+#elif defined(__FreeBSD__)
+ pthread_set_name_np(rfi->remmina_plugin_thread, thname);
+#endif
+
+ return TRUE;
+}
+
+static gboolean remmina_rdp_close_connection(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+
+ REMMINA_PLUGIN_DEBUG("Requesting to close the connection");
+ RemminaPluginRdpEvent rdp_event = { 0 };
+ rfContext *rfi = GET_PLUGIN_DATA(gp);
+
+ if (!remmina_plugin_service->is_main_thread())
+ g_warning("WARNING: %s called on a subthread, which may not work or crash Remmina.", __func__);
+
+ if (rfi && !rfi->connected) {
+ /* libfreerdp is attempting to connect, we cannot interrupt our main thread
+ * in the connect phase.
+ * So we remove "plugin-data" from gp, so our rfi remains "orphan"
+ */
+ remmina_rdp_event_uninit(gp);
+ g_object_steal_data(G_OBJECT(gp), "plugin-data");
+ remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
+ return FALSE;
+ }
+
+
+ if (rfi && rfi->clipboard.srv_clip_data_wait == SCDW_BUSY_WAIT) {
+ REMMINA_PLUGIN_DEBUG("[RDP] requesting clipboard transfer to abort");
+ /* Allow clipboard transfer from server to terminate */
+ rfi->clipboard.srv_clip_data_wait = SCDW_ABORTING;
+ usleep(100000);
+ }
+
+ if (rfi->is_reconnecting) {
+ /* Special case: window closed when attempting to reconnect */
+ rfi->stop_reconnecting_requested = TRUE;
+ return FALSE;
+ }
+
+ rdp_event.type = REMMINA_RDP_EVENT_DISCONNECT;
+ remmina_rdp_event_event_push(gp, &rdp_event);
+
+ return FALSE;
+}
+
+static gboolean remmina_rdp_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
+{
+ TRACE_CALL(__func__);
+ return TRUE;
+}
+
+static void remmina_rdp_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
+{
+ TRACE_CALL(__func__);
+ rfContext *rfi = GET_PLUGIN_DATA(gp);
+
+ switch (feature->id) {
+ case REMMINA_RDP_FEATURE_UNFOCUS:
+ remmina_rdp_event_unfocus(gp);
+ break;
+
+ case REMMINA_RDP_FEATURE_SCALE:
+ if (rfi) {
+ rfi->scale = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
+ remmina_rdp_event_update_scale(gp);
+ } else {
+ REMMINA_PLUGIN_DEBUG("Remmina RDP plugin warning: Null value for rfi by REMMINA_RDP_FEATURE_SCALE");
+ }
+ break;
+
+ case REMMINA_RDP_FEATURE_MULTIMON:
+ if (rfi) {
+ RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+ if (remmina_plugin_service->file_get_int(remminafile, "multimon", FALSE)) {
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_UseMultimon, TRUE);
+ /* TODO Add an option for this */
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_ForceMultimon, TRUE);
+ freerdp_settings_set_bool(rfi->clientContext.context.settings, FreeRDP_Fullscreen, TRUE);
+ remmina_rdp_event_send_delayed_monitor_layout(gp);
+ }
+ } else {
+ REMMINA_PLUGIN_DEBUG("Remmina RDP plugin warning: Null value for rfi by REMMINA_RDP_FEATURE_MULTIMON");
+ }
+ break;
+
+ case REMMINA_RDP_FEATURE_DYNRESUPDATE:
+ break;
+
+ case REMMINA_RDP_FEATURE_TOOL_REFRESH:
+ if (rfi)
+ gtk_widget_queue_draw_area(rfi->drawing_area, 0, 0,
+ remmina_plugin_service->protocol_plugin_get_width(gp),
+ remmina_plugin_service->protocol_plugin_get_height(gp));
+ else
+ REMMINA_PLUGIN_DEBUG("Remmina RDP plugin warning: Null value for rfi by REMMINA_RDP_FEATURE_TOOL_REFRESH");
+ break;
+
+ case REMMINA_RDP_FEATURE_TOOL_SENDCTRLALTDEL:
+ remmina_rdp_send_ctrlaltdel(gp);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Send a keystroke to the plugin window */
+static void remmina_rdp_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
+{
+ TRACE_CALL(__func__);
+ rfContext *rfi = GET_PLUGIN_DATA(gp);
+
+ remmina_plugin_service->protocol_plugin_send_keys_signals(rfi->drawing_area,
+ keystrokes, keylen, GDK_KEY_PRESS | GDK_KEY_RELEASE);
+ return;
+}
+
+static gboolean remmina_rdp_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
+{
+ rfContext *rfi = GET_PLUGIN_DATA(gp);
+ rdpGdi *gdi;
+ size_t szmem;
+
+ UINT32 bytesPerPixel;
+ UINT32 bitsPerPixel;
+
+ if (!rfi)
+ return FALSE;
+
+ gdi = ((rdpContext *)rfi)->gdi;
+
+#if FREERDP_VERSION_MAJOR >= 3
+ bytesPerPixel = FreeRDPGetBytesPerPixel(gdi->hdc->format);
+ bitsPerPixel = FreeRDPGetBitsPerPixel(gdi->hdc->format);
+#else
+ bytesPerPixel = GetBytesPerPixel(gdi->hdc->format);
+ bitsPerPixel = GetBitsPerPixel(gdi->hdc->format);
+#endif
+
+ /** @todo we should lock FreeRDP subthread to update rfi->primary_buffer, rfi->gdi and w/h,
+ * from here to memcpy, but… how ? */
+
+ szmem = gdi->width * gdi->height * bytesPerPixel;
+
+ REMMINA_PLUGIN_DEBUG("allocating %zu bytes for a full screenshot", szmem);
+ rpsd->buffer = malloc(szmem);
+ if (!rpsd->buffer) {
+ REMMINA_PLUGIN_DEBUG("could not set aside %zu bytes for a full screenshot", szmem);
+ return FALSE;
+ }
+ rpsd->width = gdi->width;
+ rpsd->height = gdi->height;
+ rpsd->bitsPerPixel = bitsPerPixel;
+ rpsd->bytesPerPixel = bytesPerPixel;
+
+ memcpy(rpsd->buffer, gdi->primary_buffer, szmem);
+
+ /* Returning TRUE instruct also the caller to deallocate rpsd->buffer */
+ return TRUE;
+}
+
+/* Array of key/value pairs for colour depths */
+static gpointer colordepth_list[] =
+{
+ /* 1st one is the default in a new install */
+ "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
+};
+
+/* Array of key/value pairs for the FreeRDP logging level */
+static gpointer log_level[] =
+{
+ "INFO", "INFO",
+ "FATAL", "FATAL",
+ "ERROR", "ERROR",
+ "WARN", "WARN",
+ "DEBUG", "DEBUG",
+ "TRACE", "TRACE",
+ "OFF", "OFF",
+ NULL
+};
+
+
+/* Array of key/value pairs for quality selection */
+static gpointer quality_list[] =
+{
+ "0", N_("Poor (fastest)"),
+ "1", N_("Medium"),
+ "2", N_("Good"),
+ "9", N_("Best (slowest)"),
+ NULL
+};
+
+/* Array of key/value pairs for quality selection */
+static gpointer network_list[] =
+{
+ "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
+};
+
+/* Array of key/value pairs for sound options */
+static gpointer sound_list[] =
+{
+ "off", N_("Off"),
+ "local", N_("Local"),
+ "remote", N_("Remote"),
+ NULL
+};
+
+/* Array of key/value pairs for security */
+static gpointer security_list[] =
+{
+ "", 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
+};
+
+/* Array of key/value pairs for mouse movement */
+static gpointer mouse_jitter_list[] =
+{
+ "No", N_("No"),
+ "60", N_("Every 1 min"),
+ "180", N_("Every 3 min"),
+ "300", N_("Every 5 min"),
+ "600", N_("Every 10 min"),
+ NULL
+};
+
+static gpointer gwtransp_list[] =
+{
+ "http", "HTTP",
+ "rpc", "RPC",
+ "auto", "Auto",
+ NULL
+};
+
+static gpointer tls_seclevel[] =
+{
+ "", N_("Default"),
+ "0", N_("0 — Windows 7 compatible"),
+ "1", N_("1"),
+ "2", N_("2"),
+ "3", N_("3"),
+ "4", N_("4"),
+ "5", N_("5"),
+ NULL
+};
+
+static gchar clientbuild_list[] =
+ N_("2600 (Windows XP), 7601 (Windows Vista/7), 9600 (Windows 8 and newer)");
+
+static gchar clientbuild_tooltip[] =
+ 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()");
+
+static gchar microphone_tooltip[] =
+ 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");
+
+static gchar audio_tooltip[] =
+ 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");
+
+
+static gchar usb_tooltip[] =
+ 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");
+
+static gchar timeout_tooltip[] =
+ 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");
+
+static gchar network_tooltip[] =
+ 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");
+
+static gchar monitorids_tooltip[] =
+ 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");
+
+static gchar drive_tooltip[] =
+ 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");
+
+/* Array of RemminaProtocolSetting for basic settings.
+ * Each item is composed by:
+ * a) RemminaProtocolSettingType for setting type
+ * b) Setting name
+ * c) Setting description
+ * d) Compact disposition
+ * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
+ * f) Setting tooltip
+ * g) Validation data pointer, will be passed to the validation callback method.
+ * h) Validation callback method (Can be NULL. Every entry will be valid then.)
+ * use following prototype:
+ * gboolean mysetting_validator_method(gpointer key, gpointer value,
+ * gpointer validator_data);
+ * gpointer key is a gchar* containing the setting's name,
+ * gpointer value contains the value which should be validated,
+ * gpointer validator_data contains your passed data.
+ */
+static const RemminaProtocolSetting remmina_rdp_basic_settings[] =
+{
+ { 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 }
+};
+
+/* Array of RemminaProtocolSetting for advanced settings.
+ * Each item is composed by:
+ * a) RemminaProtocolSettingType for setting type
+ * b) Setting name
+ * c) Setting description
+ * d) Compact disposition
+ * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
+ * f) Setting Tooltip
+ */
+static const RemminaProtocolSetting remmina_rdp_advanced_settings[] =
+{
+ { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "quality", N_("Quality"), FALSE, quality_list, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "security", N_("Security protocol negotiation"), FALSE, security_list, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "gwtransp", N_("Gateway transport type"), FALSE, gwtransp_list, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "tls-seclevel", N_("TLS Security Level"), FALSE, tls_seclevel, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "freerdp_log_level", N_("FreeRDP log level"), FALSE, log_level, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "freerdp_log_filters", N_("FreeRDP log filters"), FALSE, NULL, N_("tag:level[,tag:level[,…]]") },
+ { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "sound", N_("Audio output mode"), FALSE, sound_list, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "audio-output", N_("Redirect local audio output"), TRUE, NULL, audio_tooltip },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "microphone", N_("Redirect local microphone"), TRUE, NULL, microphone_tooltip },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "timeout", N_("Connection timeout in ms"), TRUE, NULL, timeout_tooltip },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "gateway_server", N_("Remote Desktop Gateway server"), FALSE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "gateway_username", N_("Remote Desktop Gateway username"), FALSE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "gateway_password", N_("Remote Desktop Gateway password"), FALSE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "gateway_domain", N_("Remote Desktop Gateway domain"), FALSE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "clientname", N_("Client name"), FALSE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_COMBO, "clientbuild", N_("Client build"), FALSE, clientbuild_list, clientbuild_tooltip },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "exec", N_("Start-up program"), FALSE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "execpath", N_("Start-up path"), FALSE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "loadbalanceinfo", N_("Load balance info"), FALSE, NULL, NULL },
+ // TRANSLATORS: Do not use typographic quotation marks, these must stay as "double quote", also know as “Typewriter ("programmer's") quote, ambidextrous.”
+ { 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\"") },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "usb", N_("USB device redirection"), TRUE, NULL, usb_tooltip },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "serialname", N_("Local serial name"), FALSE, NULL, N_("COM1, COM2, etc.") },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "serialdriver", N_("Local serial driver"), FALSE, NULL, N_("Serial") },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "serialpath", N_("Local serial path"), FALSE, NULL, N_("/dev/ttyS0, /dev/ttyS1, etc.") },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "parallelname", N_("Local parallel name"), FALSE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "parallelpath", N_("Local parallel device"), FALSE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "smartcardname", N_("Name of smart card"), FALSE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "dvc", N_("Dynamic virtual channel"), FALSE, NULL, N_("<channel>[,<options>]") },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "vc", N_("Static virtual channel"), FALSE, NULL, N_("<channel>[,<options>]") },
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "rdp2tcp", N_("TCP redirection"), FALSE, NULL, N_("/PATH/TO/rdp2tcp") },
+ { 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)") },
+ { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "rdp_mouse_jitter", N_("Move mouse when connection is idle"), FALSE, mouse_jitter_list, NULL },
+
+ { REMMINA_PROTOCOL_SETTING_TYPE_ASSISTANCE, "assistance_mode", N_("Attempt to connect in assistance mode"), TRUE, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "preferipv6", N_("Prefer IPv6 AAAA record over IPv4 A record"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "shareprinter", N_("Share printers"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "shareserial", N_("Share serial ports"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "serialpermissive", N_("(SELinux) permissive mode for serial ports"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "shareparallel", N_("Share parallel ports"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "sharesmartcard", N_("Share a smart card"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", N_("Turn off clipboard sync"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "cert_ignore", N_("Ignore certificate"), TRUE, NULL, NULL },
+ { 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") },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "console", N_("Attach to console (2003/2003 R2)"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disable_fastpath", N_("Turn off fast-path"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "gateway_usage", N_("Server detection using Remote Desktop Gateway"), TRUE, NULL, NULL },
+#if defined(PROXY_TYPE_IGNORE)
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "useproxyenv", N_("Use system proxy settings"), TRUE, NULL, NULL },
+#endif
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableautoreconnect", N_("Turn off automatic reconnection"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "relax-order-checks", N_("Relax order checks"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "glyph-cache", N_("Glyph cache"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "multitransport", N_("Enable multitransport protocol (UDP)"), TRUE, NULL, N_("Using the UDP protocol may improve performance") },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "base-cred-for-gw", N_("Use base credentials for gateway too"), TRUE, NULL, NULL },
+#if FREERDP_CHECK_VERSION(2, 3, 1)
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "websockets", N_("Enable Gateway websockets support"), TRUE, NULL, NULL },
+#endif
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "no-suppress", N_("Update framebuffer even when not visible"), TRUE, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
+};
+
+/* Array for available features.
+ * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
+static const RemminaProtocolFeature remmina_rdp_features[] =
+{
+ { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_RDP_FEATURE_VIEWONLY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "viewonly",
+ N_("View only") },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_RDP_FEATURE_TOOL_REFRESH, N_("Refresh"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_SCALE, REMMINA_RDP_FEATURE_SCALE, NULL, NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_DYNRESUPDATE, REMMINA_RDP_FEATURE_DYNRESUPDATE, NULL, NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_MULTIMON, REMMINA_RDP_FEATURE_MULTIMON, NULL, NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_RDP_FEATURE_TOOL_SENDCTRLALTDEL, N_("Send Ctrl+Alt+Delete"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_UNFOCUS, REMMINA_RDP_FEATURE_UNFOCUS, NULL, NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
+};
+
+/* This will be filled with version info string */
+static char remmina_plugin_rdp_version[256];
+
+/* Protocol plugin definition and features */
+static RemminaProtocolPlugin remmina_rdp =
+{
+ REMMINA_PLUGIN_TYPE_PROTOCOL, // Type
+ "RDP", // Name
+ N_("RDP - Remote Desktop Protocol"), // Description
+ GETTEXT_PACKAGE, // Translation domain
+ remmina_plugin_rdp_version, // Version number
+ "org.remmina.Remmina-rdp-symbolic", // Icon for normal connection
+ "org.remmina.Remmina-rdp-ssh-symbolic", // Icon for SSH connection
+ remmina_rdp_basic_settings, // Array for basic settings
+ remmina_rdp_advanced_settings, // Array for advanced settings
+ REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type
+ remmina_rdp_features, // Array for available features
+ remmina_rdp_init, // Plugin initialization
+ remmina_rdp_open_connection, // Plugin open connection
+ remmina_rdp_close_connection, // Plugin close connection
+ remmina_rdp_query_feature, // Query for available features
+ remmina_rdp_call_feature, // Call a feature
+ remmina_rdp_keystroke, // Send a keystroke
+ remmina_rdp_get_screenshot, // Screenshot
+ remmina_rdp_event_on_map, // RCW map event
+ remmina_rdp_event_on_unmap // RCW unmap event
+};
+
+/* File plugin definition and features */
+static RemminaFilePlugin remmina_rdpf =
+{
+ REMMINA_PLUGIN_TYPE_FILE, // Type
+ "RDPF", // Name
+ N_("RDP - RDP File Handler"), // Description
+ GETTEXT_PACKAGE, // Translation domain
+ remmina_plugin_rdp_version, // Version number
+ remmina_rdp_file_import_test, // Test import function
+ remmina_rdp_file_import, // Import function
+ remmina_rdp_file_export_test, // Test export function
+ remmina_rdp_file_export, // Export function
+ NULL
+};
+
+/* Preferences plugin definition and features */
+static RemminaPrefPlugin remmina_rdps =
+{
+ REMMINA_PLUGIN_TYPE_PREF, // Type
+ "RDPS", // Name
+ N_("RDP - Preferences"), // Description
+ GETTEXT_PACKAGE, // Translation domain
+ remmina_plugin_rdp_version, // Version number
+ "RDP", // Label
+ remmina_rdp_settings_new // Preferences body function
+};
+
+static char *buildconfig_strstr(const char *bc, const char *option)
+{
+ TRACE_CALL(__func__);
+
+ char *p, *n;
+
+ p = strcasestr(bc, option);
+ if (p == NULL)
+ return NULL;
+
+ if (p > bc && *(p - 1) > ' ')
+ return NULL;
+
+ n = p + strlen(option);
+ if (*n > ' ')
+ return NULL;
+
+ return p;
+}
+
+G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
+{
+ int vermaj, vermin, verrev;
+
+ TRACE_CALL(__func__);
+ remmina_plugin_service = service;
+
+ /* Check that we are linked to the correct version of libfreerdp */
+
+ freerdp_get_version(&vermaj, &vermin, &verrev);
+ if ((vermaj < FREERDP_REQUIRED_MAJOR) ||
+ ((vermaj == FREERDP_REQUIRED_MAJOR) && ((vermin < FREERDP_REQUIRED_MINOR) ||
+ ((vermin == FREERDP_REQUIRED_MINOR) && (verrev < FREERDP_REQUIRED_REVISION))))) {
+ g_printf("Upgrade your FreeRDP library version from %d.%d.%d to at least libfreerdp %d.%d.%d "
+ "to run the RDP plugin.\n",
+ vermaj, vermin, verrev,
+ FREERDP_REQUIRED_MAJOR, FREERDP_REQUIRED_MINOR, FREERDP_REQUIRED_REVISION);
+ return FALSE;
+ }
+
+ bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
+ bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+
+ if (!service->register_plugin((RemminaPlugin *)&remmina_rdp))
+ return FALSE;
+
+ remmina_rdpf.export_hints = _("Export connection in Windows .rdp file format");
+
+ if (!service->register_plugin((RemminaPlugin *)&remmina_rdpf))
+ return FALSE;
+
+ if (!service->register_plugin((RemminaPlugin *)&remmina_rdps))
+ return FALSE;
+
+ if (buildconfig_strstr(freerdp_get_build_config(), "WITH_GFX_H264=ON")) {
+ gfx_h264_available = TRUE;
+ REMMINA_PLUGIN_DEBUG("gfx_h264_available: %d", gfx_h264_available);
+ } else {
+ gfx_h264_available = FALSE;
+ REMMINA_PLUGIN_DEBUG("gfx_h264_available: %d", gfx_h264_available);
+ /* Remove values 65 and 66 from colordepth_list array by shifting it */
+ gpointer *src, *dst;
+ dst = src = colordepth_list;
+ while (*src) {
+ if (strcmp(*src, "66") != 0 && strcmp(*src, "65") != 0) {
+ if (dst != src) {
+ *dst = *src;
+ *(dst + 1) = *(src + 1);
+ }
+ dst += 2;
+ }
+ src += 2;
+ }
+ *dst = NULL;
+ }
+
+ snprintf(remmina_plugin_rdp_version, sizeof(remmina_plugin_rdp_version),
+ "RDP plugin: %s (Git %s), Compiled with libfreerdp %s (%s), Running with libfreerdp %s (rev %s), H.264 %s",
+ VERSION, REMMINA_GIT_REVISION,
+#if FREERDP_VERSION_MAJOR >= 3
+ FREERDP_VERSION_FULL, FREERDP_GIT_REVISION,
+#else
+ FREERDP_VERSION_FULL, GIT_REVISION,
+#endif
+ freerdp_get_version_string(),
+ freerdp_get_build_revision(),
+ gfx_h264_available ? "Yes" : "No"
+ );
+
+ remmina_rdp_settings_init();
+
+ return TRUE;
+}