From 4d5373cd7c84b7cdc2050a01e08e1a158a21747b Mon Sep 17 00:00:00 2001 From: Alex Leitner Date: Fri, 6 Sep 2024 12:55:01 -0400 Subject: [PATCH] GUACAMOLE-1026: Use LoadChannels callback method to load plugins with FreeRDP3. --- configure.ac | 24 ++++++++++ src/protocols/rdp/rdp.c | 101 +++++++++++++++++++++++++++++++--------- 2 files changed, 102 insertions(+), 23 deletions(-) diff --git a/configure.ac b/configure.ac index 18d34dcad3..fb7612f344 100644 --- a/configure.ac +++ b/configure.ac @@ -1141,6 +1141,30 @@ then [AC_MSG_RESULT([no])]) fi +if test "x${have_freerdp}" = "xyes" +then + AC_MSG_CHECKING([whether freerdp instance supports LoadChannels]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #include + + /* Mock LoadChannels function with the expected signature */ + BOOL load_channels(freerdp* instance) { + return TRUE; + } + + int main() { + freerdp* instance = freerdp_new(); + instance->LoadChannels = load_channels; + freerdp_free(instance); + return 0; + } + ]])], + [AC_MSG_RESULT([yes])] + [AC_DEFINE([RDP_INST_HAS_LOAD_CHANNELS],, + [Defined if freerdp instance supports LoadChannels])], + [AC_MSG_RESULT([no])]) +fi + # Restore CPPFLAGS, removing FreeRDP-specific options needed for testing CPPFLAGS="$OLDCPPFLAGS" diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index 4cf8e5f4fb..8080cf2979 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -79,21 +79,28 @@ #include #include -BOOL rdp_freerdp_pre_connect(freerdp* instance) { - +/** + * Initializes and loads the necessary FreeRDP plugins based on the current + * RDP session settings. This function is designed to be used as the LoadChannels + * callback within FreeRDP3, or it is called directly from our pre_connect + * function in FreeRDP2 environments. It configures various features such as + * display resizing, multi-touch support, audio input, clipboard synchronization, + * device redirection, and graphics pipeline, by loading their corresponding + * plugins if they are enabled in the session settings. + * + * @param instance + * The FreeRDP instance to be prepared, containing all context and + * settings for the session. + * + * @return + * Always TRUE. + */ +static BOOL rdp_freerdp_load_channels(freerdp* instance) { rdpContext* context = GUAC_RDP_CONTEXT(instance); - rdpGraphics* graphics = context->graphics; - guac_client* client = ((rdp_freerdp_context*) context)->client; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_rdp_settings* settings = rdp_client->settings; - /* Push desired settings to FreeRDP */ - guac_rdp_push_settings(client, settings, instance); - - /* Init FreeRDP add-in provider */ - freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0); - /* Load "disp" plugin for display update */ if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) guac_rdp_disp_load_plugin(context); @@ -125,6 +132,52 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { guac_rdpsnd_load_plugin(context); } + /* Load "rdpgfx" plugin for Graphics Pipeline Extension */ + if (settings->enable_gfx) + guac_rdp_rdpgfx_load_plugin(context); + + /* Load plugin providing Dynamic Virtual Channel support, if required */ + if (freerdp_settings_get_bool(GUAC_RDP_CONTEXT(instance)->settings, FreeRDP_SupportDynamicChannels) && + guac_freerdp_channels_load_plugin(context, "drdynvc", + GUAC_RDP_CONTEXT(instance)->settings)) { + guac_client_log(client, GUAC_LOG_WARNING, + "Failed to load drdynvc plugin. Display update and audio " + "input support will be disabled."); + } + + return TRUE; +} + +/** + * Prepares the FreeRDP instance for connection by setting up session-specific + * configurations like graphics, plugins, and RDP settings. This involves + * integrating Guacamole's custom rendering handlers (for bitmaps, glyphs, + * and pointers). If using FreeRDP2 it manually loads RDP channels. + * + * @param instance + * The FreeRDP instance to be prepared, containing all context and + * settings for the session. + * + * @return + * Returns TRUE if the pre-connection process completes successfully. + * Returns FALSE if an error occurs during the initialization of the + * FreeRDP GDI system. + */ +static BOOL rdp_freerdp_pre_connect(freerdp* instance) { + + rdpContext* context = GUAC_RDP_CONTEXT(instance); + rdpGraphics* graphics = context->graphics; + + guac_client* client = ((rdp_freerdp_context*) context)->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + guac_rdp_settings* settings = rdp_client->settings; + + /* Push desired settings to FreeRDP */ + guac_rdp_push_settings(client, settings, instance); + + /* Init FreeRDP add-in provider */ + freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0); + /* Load RAIL plugin if RemoteApp in use */ if (settings->remote_app != NULL) guac_rdp_rail_load_plugin(context); @@ -194,21 +247,15 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { primary->MemBlt = guac_rdp_gdi_memblt; primary->OpaqueRect = guac_rdp_gdi_opaquerect; - /* Load "rdpgfx" plugin for Graphics Pipeline Extension */ - if (settings->enable_gfx) - guac_rdp_rdpgfx_load_plugin(context); - - /* Load plugin providing Dynamic Virtual Channel support, if required */ - if (freerdp_settings_get_bool(GUAC_RDP_CONTEXT(instance)->settings, FreeRDP_SupportDynamicChannels) && - guac_freerdp_channels_load_plugin(context, "drdynvc", - GUAC_RDP_CONTEXT(instance)->settings)) { - guac_client_log(client, GUAC_LOG_WARNING, - "Failed to load drdynvc plugin. Display update and audio " - "input support will be disabled."); - } + /* + * If the freerdp instance does not have a LoadChannels callback for + * loading plugins we use the PreConnect callback to load plugins instead. + */ + #ifndef RDP_INST_HAS_LOAD_CHANNELS + rdp_freerdp_load_channels(instance); + #endif return TRUE; - } /** @@ -489,6 +536,14 @@ static int guac_rdp_handle_connection(guac_client* client) { /* Init client */ freerdp* rdp_inst = freerdp_new(); + + /* + * If the freerdp instance has a LoadChannels callback for loading plugins + * we use that instead of the PreConnect callback to load plugins. + */ + #ifdef RDP_INST_HAS_LOAD_CHANNELS + rdp_inst->LoadChannels = rdp_freerdp_load_channels; + #endif rdp_inst->PreConnect = rdp_freerdp_pre_connect; rdp_inst->Authenticate = rdp_freerdp_authenticate;