gnome-control-center/panels/system/remote-desktop/cc-remote-session-page.c
Ray Strode 2f66bd7ab5 system, remote-session: Stop critical if no fingerprint
The code currently assumes that if the fingerprint is non-null it's
valid, but gnome-remote-desktop can also set the fingerprint to an
empty string if there's no certificate yet.

This commit makes sure to handle both NULL and empty fingerprints.
2024-03-02 17:43:48 +00:00

757 lines
26 KiB
C

/*
* Copyright 2024 Red Hat Inc
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "cc-remote-session-page"
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <polkit/polkit.h>
#include <fcntl.h>
#include <stdio.h>
#include "cc-remote-session-page.h"
#include "cc-encryption-fingerprint-dialog.h"
#include "cc-hostname.h"
#include "cc-password-utils.h"
#include "cc-permission-infobar.h"
#include "cc-tls-certificate.h"
#include "cc-systemd-service.h"
#include "org.gnome.RemoteDesktop.h"
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#define REMOTE_DESKTOP_STORE_CREDENTIALS_TIMEOUT_S 2
#define REMOTE_SESSION_SYSTEMD_SERVICE "gnome-remote-desktop.service"
#define REMOTE_SESSION_DBUS_SERVICE "org.gnome.RemoteDesktop"
#define REMOTE_SESSION_OBJECT_PATH "/org/gnome/RemoteDesktop/Rdp/Server"
#define REMOTE_SESSION_PERMISSION "org.gnome.controlcenter.remote-session-helper"
struct _CcRemoteSessionPage {
AdwBin parent_instance;
GsdRemoteDesktopRdpServer *rdp_server;
AdwSwitchRow *remote_session_row;
GtkWidget *toast_overlay;
CcPermissionInfobar *permission_infobar;
AdwActionRow *hostname_row;
AdwActionRow *port_row;
GtkWidget *credentials_group;
GtkWidget *username_entry;
GtkWidget *password_entry;
GtkWidget *generate_password_button;
GtkWidget *verify_encryption_button;
GTlsCertificate *certificate;
CcEncryptionFingerprintDialog *fingerprint_dialog;
GCancellable *cancellable;
GPermission *permission;
char *temp_cert_dir;
guint store_credentials_id;
gboolean activating;
gboolean have_credentials;
};
G_DEFINE_TYPE (CcRemoteSessionPage, cc_remote_session_page, ADW_TYPE_BIN)
static void on_remote_session_active_changed (CcRemoteSessionPage *self);
static void enable_remote_session_service (CcRemoteSessionPage *self);
static void connect_to_remote_desktop_rdp_server (CcRemoteSessionPage *self);
static void fetch_credentials (CcRemoteSessionPage *self);
static void
add_toast (CcRemoteSessionPage *self,
const char *message)
{
adw_toast_overlay_add_toast (ADW_TOAST_OVERLAY (self->toast_overlay),
adw_toast_new (message));
}
static void
on_address_copy_clicked (CcRemoteSessionPage *self,
GtkButton *button)
{
gdk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button)),
adw_action_row_get_subtitle (self->hostname_row));
add_toast (self, _("Device address copied"));
}
static void
on_port_copy_clicked (CcRemoteSessionPage *self,
GtkButton *button)
{
gdk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button)),
adw_action_row_get_subtitle (self->port_row));
add_toast (self, _("Port number copied"));
}
static void
on_username_copy_clicked (CcRemoteSessionPage *self,
GtkButton *button)
{
GtkEditable *editable = GTK_EDITABLE (self->username_entry);
gdk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button)),
gtk_editable_get_text (editable));
add_toast (self, _("Username copied"));
}
static void
on_password_copy_clicked (CcRemoteSessionPage *self,
GtkButton *button)
{
GtkEditable *editable = GTK_EDITABLE (self->password_entry);
gdk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button)),
gtk_editable_get_text (editable));
add_toast (self, _("Password copied"));
}
static void
on_generate_password_button_clicked (CcRemoteSessionPage *self)
{
g_autofree char *new_password = cc_generate_password ();
gtk_editable_set_text (GTK_EDITABLE (self->password_entry), new_password);
}
static void
on_verify_encryption_button_clicked (CcRemoteSessionPage *self)
{
gtk_window_present (GTK_WINDOW (self->fingerprint_dialog));
}
static void
start_remote_session_row_activation (CcRemoteSessionPage *self)
{
gtk_widget_set_sensitive (GTK_WIDGET (self->remote_session_row), FALSE);
self->activating = TRUE;
}
static void
finish_remote_session_row_activation (CcRemoteSessionPage *self)
{
if (g_permission_get_allowed (self->permission))
gtk_widget_set_sensitive (GTK_WIDGET (self->remote_session_row), TRUE);
self->activating = FALSE;
}
static void
on_remote_session_enabled (GsdRemoteDesktopRdpServer *rdp_server,
GAsyncResult *result,
gpointer user_data)
{
CcRemoteSessionPage *self = user_data;
g_autoptr(GError) error = NULL;
gboolean success;
success = gsd_remote_desktop_rdp_server_call_enable_finish (rdp_server,
result,
&error);
if (!success)
{
g_warning ("Failed to enable RDP server: %s", error->message);
g_clear_error (&error);
}
finish_remote_session_row_activation (self);
}
static void
enable_remote_session_service (CcRemoteSessionPage *self)
{
g_autofree gchar *cmdline = NULL;
g_autoptr(GError) error = NULL;
gboolean success;
success = cc_enable_service (REMOTE_SESSION_SYSTEMD_SERVICE, G_BUS_TYPE_SYSTEM, &error);
if (!success)
{
g_warning ("Failed to enable gnome-remote-desktop systemd service: %s", error->message);
g_clear_error (&error);
}
gsd_remote_desktop_rdp_server_call_enable (self->rdp_server,
self->cancellable,
(GAsyncReadyCallback)
on_remote_session_enabled,
self);
}
static void
on_certificate_imported (GsdRemoteDesktopRdpServer *rdp_server,
GAsyncResult *result,
gpointer user_data)
{
CcRemoteSessionPage *self = user_data;
g_autoptr(GError) error = NULL;
gboolean success;
g_autofree char *dir = g_steal_pointer (&self->temp_cert_dir);
g_autofree char *certificate_path = g_build_filename (dir, "rdp-tls.crt", NULL);
g_autofree char *key_path = g_build_filename (dir, "rdp-tls.key", NULL);
success = gsd_remote_desktop_rdp_server_call_import_certificate_finish (rdp_server,
NULL,
result,
&error);
if (!success)
{
g_warning ("Failed to import newly generated certificates: %s", error->message);
g_clear_error (&error);
}
if (g_remove (certificate_path) != 0)
g_warning ("Failed to remove generated certificate %s", certificate_path);
if (g_remove (key_path) != 0)
g_warning ("Failed to remove generated private key %s", key_path);
if (g_remove (dir) != 0)
g_warning ("Failed to remove temporary directory %s", dir);
enable_remote_session_service (self);
}
static void
on_tls_certificate_generated (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
CcRemoteSessionPage *self = user_data;
g_autofree char *certificate_path = g_build_filename (self->temp_cert_dir, "rdp-tls.crt", NULL);
g_autofree char *key_path = g_build_filename (self->temp_cert_dir, "rdp-tls.key", NULL);
g_autoptr(GTlsCertificate) tls_certificate = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GUnixFDList) fd_list = NULL;
g_autofd int certificate_fd = -1;
g_autofd int key_fd = -1;
int certificate_fd_index = -1;
int key_fd_index = -1;
tls_certificate = bonsai_tls_certificate_new_generate_finish (res, &error);
if (!tls_certificate)
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Failed to generate TLS certificate: %s", error->message);
goto fail;
}
fd_list = g_unix_fd_list_new ();
certificate_fd = open (certificate_path, O_RDONLY);
key_fd = open (key_path, O_RDONLY);
if (certificate_fd != -1 && key_fd != -1)
{
certificate_fd_index = g_unix_fd_list_append (fd_list, certificate_fd, &error);
if (certificate_fd_index == -1)
{
g_warning ("Failed to append certificate fd to list: %s", error->message);
goto fail;
}
key_fd_index = g_unix_fd_list_append (fd_list, key_fd, &error);
if (key_fd_index == -1)
{
g_warning ("Failed to append key fd to list: %s", error->message);
goto fail;
}
gsd_remote_desktop_rdp_server_call_import_certificate (self->rdp_server,
g_variant_new ("(sh)", certificate_path, certificate_fd_index),
g_variant_new ("(sh)", key_path, key_fd_index),
fd_list,
self->cancellable,
(GAsyncReadyCallback)
on_certificate_imported,
self);
return;
}
fail:
finish_remote_session_row_activation (self);
}
static void
enable_remote_session (CcRemoteSessionPage *self)
{
g_autoptr (GKeyFile) conf_file = NULL;
const gchar *cert_path = NULL;
const gchar *key_path = NULL;
if (gsd_remote_desktop_rdp_server_get_enabled (self->rdp_server))
return;
start_remote_session_row_activation (self);
cert_path = gsd_remote_desktop_rdp_server_get_tls_cert (self->rdp_server) ?: "";
key_path = gsd_remote_desktop_rdp_server_get_tls_key (self->rdp_server) ?: "";
if (*cert_path == '\0' || *key_path == '\0')
{
g_autofree char *temp_dir = g_dir_make_tmp ("gnome-remote-desktop-XXXXXX", NULL);
g_autofree char *cert_path_tmp = NULL;
g_autofree char *key_path_tmp = NULL;
if (!temp_dir)
{
g_warning ("Failed to create temporary directory");
finish_remote_session_row_activation (self);
return;
}
cert_path_tmp = g_build_filename (temp_dir, "rdp-tls.crt", NULL);
key_path_tmp = g_build_filename (temp_dir, "rdp-tls.key", NULL);
g_set_str (&self->temp_cert_dir, temp_dir);
bonsai_tls_certificate_new_generate_async (cert_path_tmp,
key_path_tmp,
"US", "GNOME",
self->cancellable,
on_tls_certificate_generated,
self);
return;
}
enable_remote_session_service (self);
}
static void
on_remote_session_disabled (GsdRemoteDesktopRdpServer *rdp_server,
GAsyncResult *result,
gpointer user_data)
{
CcRemoteSessionPage *self = user_data;
g_autoptr(GError) error = NULL;
gboolean success;
success = gsd_remote_desktop_rdp_server_call_disable_finish (rdp_server,
result,
&error);
if (!success)
{
g_warning ("Failed to disable RDP server: %s", error->message);
g_clear_error (&error);
}
success = cc_disable_service (REMOTE_SESSION_SYSTEMD_SERVICE, G_BUS_TYPE_SYSTEM, &error);
if (!success)
{
g_warning ("Failed to disable gnome-remote-desktop systemd service: %s", error->message);
g_clear_error (&error);
}
connect_to_remote_desktop_rdp_server (self);
}
static void
disable_remote_session_service (CcRemoteSessionPage *self)
{
g_autofree gchar *cmdline = NULL;
g_autoptr(GError) error = NULL;
if (!gsd_remote_desktop_rdp_server_get_enabled (self->rdp_server))
return;
gsd_remote_desktop_rdp_server_call_disable (self->rdp_server,
self->cancellable,
(GAsyncReadyCallback)
on_remote_session_disabled,
self);
}
static void
on_remote_session_active_changed (CcRemoteSessionPage *self)
{
if (adw_switch_row_get_active (self->remote_session_row))
enable_remote_session (self);
else
disable_remote_session_service (self);
}
static gboolean
format_port_for_row (GBinding *binding,
const GValue *from_value,
GValue *to_value,
gpointer user_data)
{
int port = g_value_get_int (from_value);
if (port <= 0)
g_value_set_string (to_value, "");
else
g_value_take_string (to_value, g_strdup_printf ("%u", port));
return TRUE;
}
static void
on_set_rdp_credentials (GsdRemoteDesktopRdpServer *rdp_server,
GAsyncResult *result,
gpointer user_data)
{
CcRemoteSessionPage *self = user_data;
g_autoptr(GVariant) credentials = NULL;
g_autoptr(GError) error = NULL;
gsd_remote_desktop_rdp_server_call_set_credentials_finish (rdp_server,
result,
&error);
self->store_credentials_id = 0;
if (error)
{
g_debug ("Could not set credentials for remote session access: %s", error->message);
return;
}
/* Do a roundtrip to make sure it stuck and also so we repopulate the tls fingerprint */
fetch_credentials (self);
}
static gboolean
store_credentials_timeout (gpointer user_data)
{
CcRemoteSessionPage *self = (CcRemoteSessionPage *)user_data;
const char *username, *password;
if (!g_permission_get_allowed (self->permission))
return G_SOURCE_REMOVE;
username = gtk_editable_get_text (GTK_EDITABLE (self->username_entry));
password = gtk_editable_get_text (GTK_EDITABLE (self->password_entry));
if (username && password)
{
GVariantBuilder credentials;
g_variant_builder_init (&credentials, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add (&credentials, "{sv}", "username", g_variant_new_string (username));
g_variant_builder_add (&credentials, "{sv}", "password", g_variant_new_string (password));
gsd_remote_desktop_rdp_server_call_set_credentials (self->rdp_server,
g_variant_builder_end (&credentials),
self->cancellable,
(GAsyncReadyCallback)
on_set_rdp_credentials,
self);
}
else
{
self->store_credentials_id = 0;
}
return G_SOURCE_REMOVE;
}
static void
on_credentials_changed (CcRemoteSessionPage *self)
{
g_clear_handle_id (&self->store_credentials_id, g_source_remove);
self->store_credentials_id =
g_timeout_add_seconds (REMOTE_DESKTOP_STORE_CREDENTIALS_TIMEOUT_S,
store_credentials_timeout,
self);
}
static void
hide_password (CcRemoteSessionPage *self)
{
GtkEditable *text = gtk_editable_get_delegate (GTK_EDITABLE (self->password_entry));
gtk_text_set_visibility (GTK_TEXT (text), FALSE);
}
static void
sync_permissions (CcRemoteSessionPage *self)
{
if (!g_permission_get_allowed (self->permission))
{
hide_password (self);
g_clear_handle_id (&self->store_credentials_id, g_source_remove);
gtk_widget_set_sensitive (GTK_WIDGET (self->remote_session_row), FALSE);
gtk_widget_set_sensitive (self->credentials_group, FALSE);
}
else
{
if (!self->activating)
gtk_widget_set_sensitive (GTK_WIDGET (self->remote_session_row), TRUE);
if (self->have_credentials)
gtk_widget_set_sensitive (self->credentials_group, TRUE);
}
}
static void
cc_remote_session_page_dispose (GObject *object)
{
CcRemoteSessionPage *self = (CcRemoteSessionPage *)object;
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
g_clear_object (&self->permission);
g_clear_pointer ((GtkWindow **) &self->fingerprint_dialog, gtk_window_destroy);
g_clear_object (&self->rdp_server);
G_OBJECT_CLASS (cc_remote_session_page_parent_class)->dispose (object);
}
static void
cc_remote_session_page_class_init (CcRemoteSessionPageClass * klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = cc_remote_session_page_dispose;
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/system/remote-desktop/cc-remote-session-page.ui");
gtk_widget_class_bind_template_child (widget_class, CcRemoteSessionPage, hostname_row);
gtk_widget_class_bind_template_child (widget_class, CcRemoteSessionPage, port_row);
gtk_widget_class_bind_template_child (widget_class, CcRemoteSessionPage, toast_overlay);
gtk_widget_class_bind_template_child (widget_class, CcRemoteSessionPage, permission_infobar);
gtk_widget_class_bind_template_child (widget_class, CcRemoteSessionPage, remote_session_row);
gtk_widget_class_bind_template_child (widget_class, CcRemoteSessionPage, credentials_group);
gtk_widget_class_bind_template_child (widget_class, CcRemoteSessionPage, username_entry);
gtk_widget_class_bind_template_child (widget_class, CcRemoteSessionPage, password_entry);
gtk_widget_class_bind_template_child (widget_class, CcRemoteSessionPage, generate_password_button);
gtk_widget_class_bind_template_child (widget_class, CcRemoteSessionPage, verify_encryption_button);
gtk_widget_class_bind_template_callback (widget_class, on_address_copy_clicked);
gtk_widget_class_bind_template_callback (widget_class, on_port_copy_clicked);
gtk_widget_class_bind_template_callback (widget_class, on_username_copy_clicked);
gtk_widget_class_bind_template_callback (widget_class, on_password_copy_clicked);
gtk_widget_class_bind_template_callback (widget_class, on_generate_password_button_clicked);
gtk_widget_class_bind_template_callback (widget_class, on_verify_encryption_button_clicked);
}
static void
on_got_rdp_credentials (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
CcRemoteSessionPage *self = user_data;
gboolean got_credentials, has_fingerprint;
g_autoptr(GVariant) credentials = NULL;
g_autoptr(GError) error = NULL;
const gchar *fingerprint;
GtkNative *native;
got_credentials = gsd_remote_desktop_rdp_server_call_get_credentials_finish (self->rdp_server,
&credentials,
result,
&error);
if (error)
{
g_debug ("Could not get credentials for remote session access: %s", error->message);
return;
}
if (got_credentials)
{
const char *username = NULL;
const char *password = NULL;
self->have_credentials = TRUE;
sync_permissions (self);
g_variant_lookup (credentials, "username", "&s", &username);
if (username)
gtk_editable_set_text (GTK_EDITABLE (self->username_entry), username);
g_variant_lookup (credentials, "password", "&s", &password);
if (password)
gtk_editable_set_text (GTK_EDITABLE (self->password_entry), password);
}
/* Fetch TLS certificate fingerprint */
fingerprint = gsd_remote_desktop_rdp_server_get_tls_fingerprint (self->rdp_server);
if (fingerprint && strlen (fingerprint) > 0)
has_fingerprint = TRUE;
else
has_fingerprint = FALSE;
if (has_fingerprint)
{
self->fingerprint_dialog = g_object_new (CC_TYPE_ENCRYPTION_FINGERPRINT_DIALOG, NULL);
native = gtk_widget_get_native (GTK_WIDGET (self));
gtk_window_set_transient_for (GTK_WINDOW (self->fingerprint_dialog), GTK_WINDOW (native));
cc_encryption_fingerprint_dialog_set_fingerprint (self->fingerprint_dialog, fingerprint, ":");
}
gtk_widget_set_sensitive (self->verify_encryption_button, has_fingerprint);
}
static void
fetch_credentials (CcRemoteSessionPage *self)
{
g_autoptr(GError) error = NULL;
g_autofree gchar *username = NULL;
g_autofree gchar *password = NULL;
if (!g_permission_get_allowed (self->permission))
return;
gsd_remote_desktop_rdp_server_call_get_credentials (self->rdp_server,
self->cancellable,
(GAsyncReadyCallback)
on_got_rdp_credentials,
self);
}
static void
on_remote_desktop_rdp_server_owner_changed (CcRemoteSessionPage *self)
{
const char *name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (self->rdp_server));
gtk_widget_set_sensitive (GTK_WIDGET (self->toast_overlay), name_owner != NULL);
}
static void
on_connected_to_remote_desktop_rdp_server (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
CcRemoteSessionPage *self = user_data;
g_autoptr (GError) error = NULL;
g_clear_object (&self->rdp_server);
self->rdp_server = gsd_remote_desktop_rdp_server_proxy_new_finish (result, &error);
g_signal_connect_object (self->rdp_server,
"notify::g-name-owner",
G_CALLBACK (on_remote_desktop_rdp_server_owner_changed),
self, G_CONNECT_SWAPPED);
if (error)
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Failed to create remote desktop proxy: %s", error->message);
return;
}
g_signal_handlers_block_by_func (self->remote_session_row, on_remote_session_active_changed, self);
g_object_bind_property (self->rdp_server, "enabled", self->remote_session_row, "active", G_BINDING_SYNC_CREATE);
g_signal_handlers_unblock_by_func (self->remote_session_row, on_remote_session_active_changed, self);
g_object_bind_property_full (self->rdp_server, "port",
self->port_row, "subtitle",
G_BINDING_SYNC_CREATE,
format_port_for_row,
NULL,
NULL,
NULL);
if (g_permission_get_allowed (self->permission))
fetch_credentials (self);
g_signal_connect_object (self->permission, "notify::allowed",
G_CALLBACK (fetch_credentials),
self, G_CONNECT_SWAPPED);
}
static void
connect_to_remote_desktop_rdp_server (CcRemoteSessionPage *self)
{
g_autoptr (GError) error = NULL;
g_autoptr (GDBusConnection) connection = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, self->cancellable, &error);
if (error)
g_warning ("Could not connect to system message bus: %s", error->message);
if (!connection)
return;
gsd_remote_desktop_rdp_server_proxy_new (connection,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION,
REMOTE_SESSION_DBUS_SERVICE,
REMOTE_SESSION_OBJECT_PATH,
self->cancellable,
(GAsyncReadyCallback)
on_connected_to_remote_desktop_rdp_server,
self);
}
static void
cc_remote_session_page_init (CcRemoteSessionPage *self)
{
g_autoptr(GtkCssProvider) provider = NULL;
g_autoptr(GVariant) credentials = NULL;
g_autoptr(GError) error = NULL;
g_autofree gchar *hostname = NULL;
gtk_widget_init_template (GTK_WIDGET (self));
self->cancellable = g_cancellable_new ();
hostname = cc_hostname_get_display_hostname (cc_hostname_get_default ());
adw_action_row_set_subtitle (self->hostname_row, hostname);
g_signal_connect_swapped (self->username_entry, "notify::text",
G_CALLBACK (on_credentials_changed),
self);
g_signal_connect_swapped (self->password_entry, "notify::text",
G_CALLBACK (on_credentials_changed),
self);
g_signal_connect_object (self->remote_session_row, "notify::active",
G_CALLBACK (on_remote_session_active_changed), self,
G_CONNECT_SWAPPED);
self->permission = (GPermission*) polkit_permission_new_sync (REMOTE_SESSION_PERMISSION, NULL, self->cancellable, &error);
if (error != NULL)
{
g_warning ("Cannot create '%s' permission: %s", REMOTE_SESSION_PERMISSION, error->message);
g_clear_error (&error);
}
sync_permissions (self);
g_signal_connect_swapped (self->permission, "notify::allowed",
G_CALLBACK (sync_permissions),
self);
g_object_bind_property (self->password_entry, "sensitive",
self->generate_password_button, "sensitive",
G_BINDING_SYNC_CREATE);
cc_permission_infobar_set_permission (self->permission_infobar, self->permission);
cc_permission_infobar_set_title (self->permission_infobar, _("Some settings are locked"));
connect_to_remote_desktop_rdp_server (self);
}