From daceeaf9f574e468f3e8de0b7e69032f049d7c02 Mon Sep 17 00:00:00 2001 From: Alex Leitner Date: Thu, 9 May 2024 13:49:31 +0900 Subject: [PATCH] GUACAMOLE-1026: Add support for FreeRDP3. --- configure.ac | 201 ++++++++++-- src/libguac/guacamole/string.h | 33 +- src/libguac/string.c | 25 +- src/protocols/rdp/Makefile.am | 3 +- src/protocols/rdp/bitmap.c | 4 +- src/protocols/rdp/channels/cliprdr.c | 32 +- .../rdp/channels/rdpsnd/rdpsnd-messages.c | 3 +- src/protocols/rdp/color.c | 15 +- src/protocols/rdp/gdi.c | 10 +- src/protocols/rdp/glyph.c | 3 +- src/protocols/rdp/glyph.h | 8 +- src/protocols/rdp/input.c | 15 +- src/protocols/rdp/keyboard.c | 9 +- src/protocols/rdp/plugins/channels.c | 7 +- src/protocols/rdp/plugins/guacai/guacai.c | 6 +- src/protocols/rdp/pointer.c | 7 +- src/protocols/rdp/pointer.h | 8 +- src/protocols/rdp/rdp.c | 56 ++-- src/protocols/rdp/rdp.h | 14 + src/protocols/rdp/settings.c | 285 +++++++++++++++++- 20 files changed, 631 insertions(+), 113 deletions(-) diff --git a/configure.ac b/configure.ac index c0394ac8a..4fd71c8a9 100644 --- a/configure.ac +++ b/configure.ac @@ -648,11 +648,12 @@ then fi # -# FreeRDP 2 (libfreerdp2, libfreerdp-client2, and libwinpr2) +# FreeRDP (libfreerdpX, libfreerdp-clientX, and libwinprX) # -have_freerdp2=disabled -FREERDP2_PLUGIN_DIR= +freerdp_version= +have_freerdp= +FREERDP_PLUGIN_DIR= AC_ARG_WITH([rdp], [AS_HELP_STRING([--with-rdp], @@ -664,7 +665,7 @@ AC_ARG_WITH([rdp], AC_ARG_WITH(freerdp_plugin_dir, [AS_HELP_STRING([--with-freerdp-plugin-dir=], [install FreeRDP plugins to the given directory @<:@default=check@:>@]) - ],FREERDP2_PLUGIN_DIR=$withval) + ],FREERDP_PLUGIN_DIR=$withval) # Preserve CPPFLAGS so it can be restored later, following the addition of # options specific to FreeRDP tests @@ -672,21 +673,38 @@ OLDCPPFLAGS="$CPPFLAGS" if test "x$with_rdp" != "xno" then - have_freerdp2=yes + freerdp_version=3 + have_freerdp=yes + PKG_CHECK_MODULES([RDP], [freerdp3 freerdp-client3 winpr3], + [CPPFLAGS="${RDP_CFLAGS} -Werror $CPPFLAGS"] + [AS_IF([test "x${FREERDP_PLUGIN_DIR}" = "x"], + [FREERDP_PLUGIN_DIR="`$PKG_CONFIG --variable=libdir freerdp3`/freerdp3"])], + [AC_MSG_WARN([ + -------------------------------------------- + Unable to find FreeRDP3 (libfreerdp3 / libfreerdp-client3 / libwinpr3). + Checking for FreeRDP2. + --------------------------------------------]) + have_freerdp=no]) +fi + +if test "x$with_rdp" != "xno" -a "x${have_freerdp}" = "xno" +then + freerdp_version=2 + have_freerdp=yes PKG_CHECK_MODULES([RDP], [freerdp2 freerdp-client2 winpr2], [CPPFLAGS="${RDP_CFLAGS} -Werror $CPPFLAGS"] - [AS_IF([test "x${FREERDP2_PLUGIN_DIR}" = "x"], - [FREERDP2_PLUGIN_DIR="`$PKG_CONFIG --variable=libdir freerdp2`/freerdp2"])], + [AS_IF([test "x${FREERDP_PLUGIN_DIR}" = "x"], + [FREERDP_PLUGIN_DIR="`$PKG_CONFIG --variable=libdir freerdp2`/freerdp2"])], [AC_MSG_WARN([ -------------------------------------------- - Unable to find FreeRDP (libfreerdp2 / libfreerdp-client2 / libwinpr2) + Unable to find FreeRDP2 (libfreerdp2 / libfreerdp-client2 / libwinpr2) RDP will be disabled. --------------------------------------------]) - have_freerdp2=no]) + have_freerdp=no]) fi # Available color conversion functions -if test "x$have_freerdp2" = "xyes" +if test "x${have_freerdp}" = "xyes" then # FreeRDP 2.0.0-rc3 and older referred to FreeRDPConvertColor() as @@ -708,7 +726,7 @@ AC_ARG_ENABLE(allow_freerdp_snapshots, [allow building against unknown development snapshots of FreeRDP]) ],allow_freerdp_snapshots=yes) -if test "x${have_freerdp2}" = "xyes" -a "x${allow_freerdp_snapshots}" != "xyes" +if test "x${have_freerdp}" = "xyes" -a "x${allow_freerdp_snapshots}" != "xyes" then AC_MSG_CHECKING([whether FreeRDP appears to be a development version]) @@ -736,7 +754,7 @@ then fi # Variation in memory internal allocation/free behavior for bitmaps -if test "x${have_freerdp2}" = "xyes" +if test "x${have_freerdp}" = "xyes" then # FreeRDP 2.0.0-rc0 and older automatically free rdpBitmap and its @@ -760,7 +778,7 @@ then fi # Variation in memory internal allocation/free behavior for channel streams -if test "x${have_freerdp2}" = "xyes" +if test "x${have_freerdp}" = "xyes" then # FreeRDP 2.0.0-rc3 through 2.0.0-rc4 automatically free the wStream @@ -782,7 +800,7 @@ then fi # Glyph callback variants -if test "x${have_freerdp2}" = "xyes" +if test "x${have_freerdp}" = "xyes" then # FreeRDP 2.0.0-rc3 and older used UINT32 for integer parameters to all @@ -815,7 +833,7 @@ then fi # CLIPRDR callback variants -if test "x${have_freerdp2}" = "xyes" +if test "x${have_freerdp}" = "xyes" then # FreeRDP 2.0.0-rc3 and older did not use const for CLIPRDR callbacks @@ -845,7 +863,7 @@ then fi # RAIL callback variants -if test "x${have_freerdp2}" = "xyes" +if test "x${have_freerdp}" = "xyes" then # FreeRDP 2.0.0-rc3 and older did not use const for RAIL callbacks @@ -875,7 +893,7 @@ then fi # Support for receiving unannounced orders from the RDP server -if test "x${have_freerdp2}" = "xyes" +if test "x${have_freerdp}" = "xyes" then AC_CHECK_MEMBERS([rdpSettings.AllowUnanouncedOrdersFromServer],, [AC_MSG_WARN([ @@ -891,17 +909,151 @@ fi # Updated certificate verification callback (introduced with 2.0.0, not present # in 2.0.0-rc4 or earlier) -if test "x${have_freerdp2}" = "xyes" +if test "x${have_freerdp}" = "xyes" then AC_CHECK_MEMBERS([freerdp.VerifyCertificateEx],,, - [[#include ]]) + [[#include ]]) +fi + +if test "x${have_freerdp}" = "xyes" +then + AC_CHECK_DECLS([winpr_aligned_free], + [AC_DEFINE([HAVE_WINPR_ALIGNED],, + [Defined if winpr_aligned_free() and winpr_aligned_malloc() are available])],, + [#include ]) +fi + +if test "x${have_freerdp}" = "xyes" +then + AC_MSG_CHECKING([whether CLIPRDR structs have a common CLIPRDR_HEADER]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #include + int main() { + CLIPRDR_FORMAT_LIST list; + list.common.msgType = 0; + return 0; + } + ]])], + [AC_MSG_RESULT([yes])] + [AC_DEFINE([HAVE_CLIPRDR_HEADER],, + [Defined if CLIPRDR structs have a common CLIPRDR_HEADER])], + [AC_MSG_RESULT([no])]) +fi + +if test "x${have_freerdp}" = "xyes" +then + AC_CHECK_DECLS([FreeRDPReadColor], + [AC_DEFINE([USE_UPDATED_RW_COLOR_FUNCS],, + [Defined if FreeRDPReadColor() and FreeRDPWriteColor() are available])],, + [#include ]) +fi + +if test "x${have_freerdp}" = "xyes" +then + AC_CHECK_DECLS([freerdp_settings_set_pointer], + [AC_DEFINE([HAVE_SETTERS_GETTERS],, + [Defined if freerdp_settings_set_pointer is available])],, + [#include ]) +fi + +if test "x${have_freerdp}" = "xyes" +then + AC_MSG_CHECKING([whether freerdp structs have a context]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #include + int main() { + freerdp* instance = freerdp_new(); + /* We cast to void to prevent unused variable warnings */ + (void)instance->context->input; + (void)instance->context->settings; + freerdp_free(instance); + return 0; + } + ]])], + [AC_MSG_RESULT([yes])] + [AC_DEFINE([FREERDP_HAS_CONTEXT],, + [FreeRDP structs have a context])], + [AC_MSG_RESULT([no])]) +fi + +if test "x${have_freerdp}" = "xyes" +then + AC_CHECK_DECL([freerdp_shall_disconnect_context], + [AC_DEFINE([HAVE_DISCONNECT_CONTEXT],, + [Defined if 'freerdp_shall_disconnect_context' is available in FreeRDP])],, + [#include ]) +fi +if test "x${have_freerdp}" = "xyes" +then + # Check whether FreeRDP 3.x requires const for GetPluginData + AC_MSG_CHECKING([whether GetPluginData requires const for the returned args]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #include + int main() { + IDRDYNVC_ENTRY_POINTS test_entry_points; + const ADDIN_ARGV* args = test_entry_points.GetPluginData(&test_entry_points); + (void)args; + return 0; + } + ]])], + [AC_MSG_RESULT([yes])] + [AC_DEFINE([PLUGIN_DATA_CONST],, + [Defined if GetPluginData returns a pointer to a const ADDIN_ARGV])], + [AC_MSG_RESULT([no])]) +fi + +if test "x${have_freerdp}" = "xyes" +then + # Check whether glyph.New expects a const rdpGlyph* parameter + AC_MSG_CHECKING([whether glyph.New expects a const rdpGlyph* parameter]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #include + BOOL mock_glyph_new(rdpContext* context, const rdpGlyph* glyph) { + return TRUE; + } + + int main() { + rdpGlyph* glyph = (rdpGlyph*) malloc(sizeof(rdpGlyph)); + glyph->New = mock_glyph_new; + free(glyph); + return 0; + } + ]])], + [AC_MSG_RESULT([yes])] + [AC_DEFINE([RDP_GLYPH_NEW_REQUIRES_CONST],, + [Defined if glyph.New expects a const rdpGlyph* parameter])], + [AC_MSG_RESULT([no])]) +fi + +if test "x${have_freerdp}" = "xyes" +then + # Check whether pointer.Set expects a const rdpPointer* parameter + AC_MSG_CHECKING([whether pointer.Set expects a const rdpPointer* parameter]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #include + + BOOL mock_pointer_set(rdpContext* context, const rdpPointer* pointer) { + return TRUE; + } + + int main() { + rdpPointer* pointer = (rdpPointer*) malloc(sizeof(rdpPointer)); + pointer->Set = mock_pointer_set; + free(pointer); + return 0; + } + ]])], + [AC_MSG_RESULT([yes])] + [AC_DEFINE([RDP_POINTER_SET_REQUIRES_CONST],, + [Defined if pointer.Set expects a const rdpPointer* parameter])], + [AC_MSG_RESULT([no])]) fi # Restore CPPFLAGS, removing FreeRDP-specific options needed for testing CPPFLAGS="$OLDCPPFLAGS" -AC_SUBST(FREERDP2_PLUGIN_DIR) -AM_CONDITIONAL([ENABLE_RDP], [test "x${have_freerdp2}" = "xyes"]) +AC_SUBST(FREERDP_PLUGIN_DIR) +AM_CONDITIONAL([ENABLE_RDP], [test "x${have_freerdp}" = "xyes"]) # # libssh2 @@ -1237,7 +1389,7 @@ AM_COND_IF([ENABLE_SYSTEMD], [build_systemd="${systemd_dir}"], [build_systemd=no # FreeRDP plugins # -AM_COND_IF([ENABLE_RDP], [build_rdp_plugins="${FREERDP2_PLUGIN_DIR}"], [build_rdp_plugins=no]) +AM_COND_IF([ENABLE_RDP], [build_rdp_plugins="${FREERDP_PLUGIN_DIR}"], [build_rdp_plugins=no]) # # Display summary @@ -1250,7 +1402,7 @@ $PACKAGE_NAME version $PACKAGE_VERSION Library status: - freerdp2 ............ ${have_freerdp2} + freerdp${freerdp_version} ............ ${have_freerdp} pango ............... ${have_pango} libavcodec .......... ${have_libavcodec} libavformat ......... ${have_libavformat} @@ -1280,10 +1432,9 @@ $PACKAGE_NAME version $PACKAGE_VERSION guacenc .... ${build_guacenc} guaclog .... ${build_guaclog} - FreeRDP plugins: ${build_rdp_plugins} + FreeRDP${freerdp_version} plugins: ${build_rdp_plugins} Init scripts: ${build_init} Systemd units: ${build_systemd} Type \"make\" to compile $PACKAGE_NAME. " - diff --git a/src/libguac/guacamole/string.h b/src/libguac/guacamole/string.h index a94d53c29..bec28115d 100644 --- a/src/libguac/guacamole/string.h +++ b/src/libguac/guacamole/string.h @@ -131,6 +131,38 @@ size_t guac_strlcat(char* restrict dest, const char* restrict src, size_t n); */ char* guac_strnstr(const char *haystack, const char *needle, size_t len); +/** + * Duplicates up to the given number of characters from the provided string, + * returning a newly-allocated string containing the copied contents. The + * provided string must be null-terminated, and only the first 'n' characters + * will be considered for duplication, or the full string length if it is + * shorter than 'n'. The memory block for the newly-allocated string will + * include enough space for these characters, as well as for the null + * terminator. + * + * The pointer returned by guac_strndup() SHOULD be freed with a subsequent call + * to guac_mem_free(), but MAY instead be freed with a subsequent call to free(). + * + * This function behaves similarly to the POSIX strndup() function, except that + * NULL will be returned if the provided string is NULL or if memory allocation + * fails. Also, the length of the string to be duplicated will be checked to + * prevent overflow if adding space for the null terminator. + * + * @param str + * The string of which up to the first 'n' characters should be duplicated + * as a newly-allocated string. If 'n' exceeds the length of the string, + * the entire string is duplicated. + * + * @param n + * The maximum number of characters to duplicate from the given string. + * + * @return + * A newly-allocated string containing up to the first 'n' characters from + * the given string, including a terminating null byte, or NULL if the + * provided string was NULL or if memory allocation fails. + */ +char* guac_strndup(const char* str, size_t n); + /** * Duplicates the given string, returning a newly-allocated string containing * the same contents. The provided string must be null-terminated. The size of @@ -202,4 +234,3 @@ size_t guac_strljoin(char* restrict dest, const char* restrict const* elements, int nmemb, const char* restrict delim, size_t n); #endif - diff --git a/src/libguac/string.c b/src/libguac/string.c index 2a7ec2cd4..0fbf0bc46 100644 --- a/src/libguac/string.c +++ b/src/libguac/string.c @@ -115,7 +115,7 @@ char* guac_strnstr(const char *haystack, const char *needle, size_t len) { } -char* guac_strdup(const char* str) { +char* guac_strndup(const char* str, size_t n) { /* Return NULL if no string provided */ if (str == NULL) @@ -124,18 +124,30 @@ char* guac_strdup(const char* str) { /* Do not attempt to duplicate if the length is somehow magically so * obscenely large that it will not be possible to add a null terminator */ size_t length; - if (guac_mem_ckd_add(&length, strlen(str), 1)) + size_t length_to_copy = strnlen(str, n); + if (guac_mem_ckd_add(&length, length_to_copy, 1)) return NULL; - /* Otherwise just copy to a new string in same manner as strdup() */ - void* new_str = guac_mem_alloc(length); - if (new_str != NULL) - memcpy(new_str, str, length); + /* Otherwise just copy to a new string in same manner as strndup() */ + char* new_str = (char*)guac_mem_alloc(length); + if (new_str != NULL) { + memcpy(new_str, str, length_to_copy); + new_str[length_to_copy] = '\0'; + } return new_str; } +char* guac_strdup(const char* str) { + + /* Return NULL if no string provided */ + if (str == NULL) + return NULL; + + return guac_strndup(str, strlen(str)); +} + size_t guac_strljoin(char* restrict dest, const char* restrict const* elements, int nmemb, const char* restrict delim, size_t n) { @@ -159,4 +171,3 @@ size_t guac_strljoin(char* restrict dest, const char* restrict const* elements, return length; } - diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index 1c393c30d..57204f2aa 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -158,7 +158,7 @@ freerdp_LTLIBRARIES = \ libguac-common-svc-client.la \ libguacai-client.la -freerdpdir = @FREERDP2_PLUGIN_DIR@ +freerdpdir = @FREERDP_PLUGIN_DIR@ # # Common SVC plugin (shared by RDPDR, RDPSND, etc.) @@ -264,4 +264,3 @@ EXTRA_DIST = \ $(rdp_keymaps) \ keymaps/generate.pl \ plugins/generate-entry-wrappers.pl - diff --git a/src/protocols/rdp/bitmap.c b/src/protocols/rdp/bitmap.c index 05f9e214b..6faa151fb 100644 --- a/src/protocols/rdp/bitmap.c +++ b/src/protocols/rdp/bitmap.c @@ -132,8 +132,7 @@ void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap) { /* NOTE: Except in FreeRDP 2.0.0-rc0 and earlier, FreeRDP-allocated memory * for the rdpBitmap will NOT be automatically released after this free * handler is invoked, thus we must do so manually here */ - - _aligned_free(bitmap->data); + GUAC_ALIGNED_FREE(bitmap->data); free(bitmap); #endif @@ -167,4 +166,3 @@ BOOL guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL pri return TRUE; } - diff --git a/src/protocols/rdp/channels/cliprdr.c b/src/protocols/rdp/channels/cliprdr.c index 4b37d58a3..34bacf3ba 100644 --- a/src/protocols/rdp/channels/cliprdr.c +++ b/src/protocols/rdp/channels/cliprdr.c @@ -82,7 +82,13 @@ static UINT guac_rdp_cliprdr_send_format_list(CliprdrClientContext* cliprdr) { /* We support CP-1252 and UTF-16 text */ CLIPRDR_FORMAT_LIST format_list = { +#ifdef HAVE_CLIPRDR_HEADER + .common = { + .msgType = CB_FORMAT_LIST + }, +#else .msgType = CB_FORMAT_LIST, +#endif .formats = (CLIPRDR_FORMAT[]) { { .formatId = CF_TEXT }, { .formatId = CF_UNICODETEXT } @@ -298,9 +304,15 @@ static UINT guac_rdp_cliprdr_format_list(CliprdrClientContext* cliprdr, guac_client_log(client, GUAC_LOG_TRACE, "CLIPRDR: Received format list."); CLIPRDR_FORMAT_LIST_RESPONSE format_list_response = { +#ifdef HAVE_CLIPRDR_HEADER + .common = { + .msgType = CB_FORMAT_LIST_RESPONSE, + .msgFlags = CB_RESPONSE_OK + } +#else .msgFlags = CB_RESPONSE_OK +#endif }; - /* Report successful processing of format list */ pthread_mutex_lock(&(rdp_client->message_lock)); cliprdr->ClientFormatListResponse(cliprdr, &format_list_response); @@ -394,8 +406,16 @@ static UINT guac_rdp_cliprdr_format_data_request(CliprdrClientContext* cliprdr, CLIPRDR_FORMAT_DATA_RESPONSE data_response = { .requestedFormatData = (BYTE*) start, +#ifdef HAVE_CLIPRDR_HEADER + .common = { + .msgType = CB_FORMAT_DATA_RESPONSE, + .msgFlags = CB_RESPONSE_OK, + .dataLen = ((BYTE*) output) - start, + } +#else .dataLen = ((BYTE*) output) - start, .msgFlags = CB_RESPONSE_OK +#endif }; guac_client_log(client, GUAC_LOG_TRACE, "CLIPRDR: Sending format data response."); @@ -482,9 +502,16 @@ static UINT guac_rdp_cliprdr_format_data_response(CliprdrClientContext* cliprdr, } + int data_len; + #ifdef HAVE_CLIPRDR_HEADER + data_len = format_data_response->common.dataLen; + #else + data_len = format_data_response->dataLen; + #endif + /* Convert, store, and forward the clipboard data received from RDP * server */ - if (guac_iconv(remote_reader, &input, format_data_response->dataLen, + if (guac_iconv(remote_reader, &input, data_len, GUAC_WRITE_UTF8, &output, sizeof(received_data))) { int length = strnlen(received_data, sizeof(received_data)); guac_common_clipboard_reset(clipboard->clipboard, "text/plain"); @@ -712,4 +739,3 @@ int guac_rdp_clipboard_end_handler(guac_user* user, guac_stream* stream) { return 0; } - diff --git a/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c b/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c index 1aeb8df99..7c57bc5c5 100644 --- a/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c +++ b/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c @@ -204,7 +204,7 @@ void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc, Stream_Write_UINT16(output_stream, rdpsnd->format_count); /* Reposition cursor at end (necessary for message send) */ - Stream_SetPointer(output_stream, output_stream_end); + Stream_SetPosition(output_stream, output_stream_end - Stream_Buffer(output_stream)); /* Send accepted formats */ guac_rdp_common_svc_write(svc, output_stream); @@ -366,4 +366,3 @@ void guac_rdpsnd_close_handler(guac_rdp_common_svc* svc, /* Do nothing */ } - diff --git a/src/protocols/rdp/color.c b/src/protocols/rdp/color.c index 964310aff..7733b2c4c 100644 --- a/src/protocols/rdp/color.c +++ b/src/protocols/rdp/color.c @@ -56,7 +56,12 @@ UINT32 guac_rdp_convert_color(rdpContext* context, UINT32 color) { /* Convert provided color into the intermediate representation expected by * FreeRDPConvertColor() */ - UINT32 intermed = ReadColor((BYTE*) &color, src_format); + UINT32 intermed; +#ifdef USE_UPDATED_RW_COLOR_FUNCS + intermed = FreeRDPReadColor((BYTE*) &color, src_format); +#else + intermed = ReadColor((BYTE*) &color, src_format); +#endif /* Convert color from RDP source format to the native format used by Cairo, * still maintaining intermediate representation */ @@ -69,8 +74,12 @@ UINT32 guac_rdp_convert_color(rdpContext* context, UINT32 color) { /* Convert color from intermediate representation to the actual desired * format */ - WriteColor((BYTE*) &color, dst_format, intermed); +#ifdef USE_UPDATED_RW_COLOR_FUNCS + intermed = FreeRDPWriteColor((BYTE*) &color, dst_format, intermed); +#else + intermed = WriteColor((BYTE*) &color, dst_format, intermed); +#endif + return color; } - diff --git a/src/protocols/rdp/gdi.c b/src/protocols/rdp/gdi.c index 6fc311bf3..a379bc73b 100644 --- a/src/protocols/rdp/gdi.c +++ b/src/protocols/rdp/gdi.c @@ -412,7 +412,14 @@ BOOL guac_rdp_gdi_surface_frame_marker(rdpContext* context, const SURFACE_FRAME_ guac_rdp_gdi_mark_frame(context, surface_frame_marker->frameAction != SURFACECMD_FRAMEACTION_END); - if (context->settings->FrameAcknowledge > 0) + int frame_acknowledge; +#ifdef HAVE_SETTERS_GETTERS + frame_acknowledge = freerdp_settings_get_uint32(context->settings, FreeRDP_FrameAcknowledge); +#else + frame_acknowledge = context->settings->FrameAcknowledge; +#endif + + if (frame_acknowledge> 0) IFCALL(context->update->SurfaceFrameAcknowledge, context, surface_frame_marker->frameId); @@ -496,4 +503,3 @@ BOOL guac_rdp_gdi_desktop_resize(rdpContext* context) { guac_rdp_get_height(context->instance)); } - diff --git a/src/protocols/rdp/glyph.c b/src/protocols/rdp/glyph.c index 7241a385a..e0907956b 100644 --- a/src/protocols/rdp/glyph.c +++ b/src/protocols/rdp/glyph.c @@ -36,7 +36,7 @@ #define cairo_format_stride_for_width(format, width) (width*4) #endif -BOOL guac_rdp_glyph_new(rdpContext* context, const rdpGlyph* glyph) { +BOOL guac_rdp_glyph_new(rdpContext* context, GLYPH_NEW_CONST rdpGlyph* glyph) { int x, y, i; int stride; @@ -168,4 +168,3 @@ BOOL guac_rdp_glyph_enddraw(rdpContext* context, /* IGNORE */ return TRUE; } - diff --git a/src/protocols/rdp/glyph.h b/src/protocols/rdp/glyph.h index b217de02d..36b8f3a3a 100644 --- a/src/protocols/rdp/glyph.h +++ b/src/protocols/rdp/glyph.h @@ -41,6 +41,12 @@ #define GLYPH_CALLBACK_INT32 UINT32 #endif +#ifdef RDP_GLYPH_NEW_REQUIRES_CONST +#define GLYPH_NEW_CONST const +#else +#define GLYPH_NEW_CONST +#endif + /** * Guacamole-specific rdpGlyph data. */ @@ -71,7 +77,7 @@ typedef struct guac_rdp_glyph { * @return * TRUE if successful, FALSE otherwise. */ -BOOL guac_rdp_glyph_new(rdpContext* context, const rdpGlyph* glyph); +BOOL guac_rdp_glyph_new(rdpContext* context, GLYPH_NEW_CONST rdpGlyph* glyph); /** * Draws a previously-cached glyph at the given coordinates within the current diff --git a/src/protocols/rdp/input.c b/src/protocols/rdp/input.c index 458c12e71..8a8d0c6ea 100644 --- a/src/protocols/rdp/input.c +++ b/src/protocols/rdp/input.c @@ -57,7 +57,8 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) { /* If button mask unchanged, just send move event */ if (mask == rdp_client->mouse_button_mask) { pthread_mutex_lock(&(rdp_client->message_lock)); - rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_MOVE, x, y); + GUAC_RDP_CONTEXT(rdp_inst)->input->MouseEvent( + GUAC_RDP_CONTEXT(rdp_inst)->input, PTR_FLAGS_MOVE, x, y); pthread_mutex_unlock(&(rdp_client->message_lock)); } @@ -80,7 +81,8 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) { if (released_mask & 0x04) flags |= PTR_FLAGS_BUTTON2; pthread_mutex_lock(&(rdp_client->message_lock)); - rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y); + GUAC_RDP_CONTEXT(rdp_inst)->input->MouseEvent( + GUAC_RDP_CONTEXT(rdp_inst)->input, flags, x, y); pthread_mutex_unlock(&(rdp_client->message_lock)); } @@ -98,7 +100,8 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) { /* Send event */ pthread_mutex_lock(&(rdp_client->message_lock)); - rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y); + GUAC_RDP_CONTEXT(rdp_inst)->input->MouseEvent( + GUAC_RDP_CONTEXT(rdp_inst)->input, flags, x, y); pthread_mutex_unlock(&(rdp_client->message_lock)); } @@ -109,14 +112,16 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) { /* Down */ if (pressed_mask & 0x08) { pthread_mutex_lock(&(rdp_client->message_lock)); - rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_WHEEL | 0x78, x, y); + GUAC_RDP_CONTEXT(rdp_inst)->input->MouseEvent( + GUAC_RDP_CONTEXT(rdp_inst)->input, PTR_FLAGS_WHEEL | 0x78, x, y); pthread_mutex_unlock(&(rdp_client->message_lock)); } /* Up */ if (pressed_mask & 0x10) { pthread_mutex_lock(&(rdp_client->message_lock)); - rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88, x, y); + GUAC_RDP_CONTEXT(rdp_inst)->input->MouseEvent( + GUAC_RDP_CONTEXT(rdp_inst)->input, PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88, x, y); pthread_mutex_unlock(&(rdp_client->message_lock)); } diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index fa93c20ee..3e749bc78 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -107,7 +107,8 @@ static void guac_rdp_send_key_event(guac_rdp_client* rdp_client, /* Send actual key */ pthread_mutex_lock(&(rdp_client->message_lock)); - rdp_inst->input->KeyboardEvent(rdp_inst->input, flags | pressed_flags, scancode); + GUAC_RDP_CONTEXT(rdp_inst)->input->KeyboardEvent( + GUAC_RDP_CONTEXT(rdp_inst)->input, flags | pressed_flags, scancode); pthread_mutex_unlock(&(rdp_client->message_lock)); } @@ -136,7 +137,8 @@ static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client, /* Send Unicode event */ pthread_mutex_lock(&(rdp_client->message_lock)); - rdp_inst->input->UnicodeKeyboardEvent(rdp_inst->input, 0, codepoint); + GUAC_RDP_CONTEXT(rdp_inst)->input->UnicodeKeyboardEvent( + GUAC_RDP_CONTEXT(rdp_inst)->input, 0, codepoint); pthread_mutex_unlock(&(rdp_client->message_lock)); } @@ -165,7 +167,8 @@ static void guac_rdp_send_synchronize_event(guac_rdp_client* rdp_client, /* Synchronize lock key states */ pthread_mutex_lock(&(rdp_client->message_lock)); - rdp_inst->input->SynchronizeEvent(rdp_inst->input, flags); + GUAC_RDP_CONTEXT(rdp_inst)->input->SynchronizeEvent( + GUAC_RDP_CONTEXT(rdp_inst)->input, flags); pthread_mutex_unlock(&(rdp_client->message_lock)); } diff --git a/src/protocols/rdp/plugins/channels.c b/src/protocols/rdp/plugins/channels.c index 342ca33b3..3ae3a8551 100644 --- a/src/protocols/rdp/plugins/channels.c +++ b/src/protocols/rdp/plugins/channels.c @@ -136,8 +136,11 @@ void guac_freerdp_dynamic_channel_collection_add(rdpSettings* settings, va_end(args); /* Register plugin with FreeRDP */ +#ifdef HAVE_SETTERS_GETTERS + freerdp_settings_set_bool(settings, FreeRDP_SupportDynamicChannels, TRUE); +#else settings->SupportDynamicChannels = TRUE; - freerdp_dynamic_channel_collection_add(settings, freerdp_args); +#endif + freerdp_dynamic_channel_collection_add(settings, freerdp_args); } - diff --git a/src/protocols/rdp/plugins/guacai/guacai.c b/src/protocols/rdp/plugins/guacai/guacai.c index 6ab633c05..2abae6524 100644 --- a/src/protocols/rdp/plugins/guacai/guacai.c +++ b/src/protocols/rdp/plugins/guacai/guacai.c @@ -283,7 +283,12 @@ static UINT guac_rdp_ai_terminated(IWTSPlugin* plugin) { UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) { /* Pull guac_client from arguments */ +#ifdef PLUGIN_DATA_CONST + const ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints); +#else ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints); +#endif + guac_client* client = (guac_client*) guac_rdp_string_to_ptr(args->argv[1]); /* Pull previously-allocated plugin */ @@ -309,4 +314,3 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) { return CHANNEL_RC_OK; } - diff --git a/src/protocols/rdp/pointer.c b/src/protocols/rdp/pointer.c index 8c024b71b..1d4a4e899 100644 --- a/src/protocols/rdp/pointer.c +++ b/src/protocols/rdp/pointer.c @@ -42,7 +42,7 @@ BOOL guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) { rdp_client->display, pointer->width, pointer->height); /* Allocate data for image */ - unsigned char* data = _aligned_malloc(pointer->width * pointer->height * 4, 16); + unsigned char* data = GUAC_ALIGNED_MALLOC(pointer->width * pointer->height * 4, 16); cairo_surface_t* surface; @@ -64,7 +64,7 @@ BOOL guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) { /* Free surface */ cairo_surface_destroy(surface); - _aligned_free(data); + GUAC_ALIGNED_FREE(data); /* Remember buffer */ ((guac_rdp_pointer*) pointer)->layer = buffer; @@ -73,7 +73,7 @@ BOOL guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) { } -BOOL guac_rdp_pointer_set(rdpContext* context, const rdpPointer* pointer) { +BOOL guac_rdp_pointer_set(rdpContext* context, POINTER_SET_CONST rdpPointer* pointer) { guac_client* client = ((rdp_freerdp_context*) context)->client; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; @@ -156,4 +156,3 @@ BOOL guac_rdp_pointer_set_default(rdpContext* context) { return TRUE; } - diff --git a/src/protocols/rdp/pointer.h b/src/protocols/rdp/pointer.h index b5fa6a236..03df2f918 100644 --- a/src/protocols/rdp/pointer.h +++ b/src/protocols/rdp/pointer.h @@ -26,6 +26,12 @@ #include #include +#ifdef RDP_POINTER_SET_REQUIRES_CONST +#define POINTER_SET_CONST const +#else +#define POINTER_SET_CONST +#endif + /** * Guacamole-specific rdpPointer data. */ @@ -71,7 +77,7 @@ BOOL guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer); * @return * TRUE if successful, FALSE otherwise. */ -BOOL guac_rdp_pointer_set(rdpContext* context, const rdpPointer* pointer); +BOOL guac_rdp_pointer_set(rdpContext* context, POINTER_SET_CONST rdpPointer* pointer); /** * Frees all Guacamole-related data associated with the given pointer, allowing diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index dff536eff..119f117d2 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -53,12 +53,6 @@ #endif #include -#include -#include -#include -#include -#include -#include #include #include #include @@ -86,7 +80,7 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { - rdpContext* context = instance->context; + rdpContext* context = GUAC_RDP_CONTEXT(instance); rdpGraphics* graphics = context->graphics; guac_client* client = ((rdp_freerdp_context*) context)->client; @@ -112,7 +106,7 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { /* Upgrade the lock to write temporarily for setting the newly allocated audio buffer */ guac_rwlock_acquire_write_lock(&(rdp_client->lock)); rdp_client->audio_input = guac_rdp_audio_buffer_alloc(client); - guac_rdp_audio_load_plugin(instance->context); + guac_rdp_audio_load_plugin(GUAC_RDP_CONTEXT(instance)); /* Downgrade the lock to allow for concurrent read access */ guac_rwlock_release_lock(&(rdp_client->lock)); @@ -178,42 +172,35 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { graphics_register_pointer(graphics, &pointer); /* Beep on receipt of Play Sound PDU */ - instance->update->PlaySound = guac_rdp_beep_play_sound; + GUAC_RDP_CONTEXT(instance)->update->PlaySound = guac_rdp_beep_play_sound; /* Automatically synchronize keyboard locks when changed server-side */ - instance->update->SetKeyboardIndicators = guac_rdp_keyboard_set_indicators; + GUAC_RDP_CONTEXT(instance)->update->SetKeyboardIndicators = guac_rdp_keyboard_set_indicators; /* Set up GDI */ - instance->update->DesktopResize = guac_rdp_gdi_desktop_resize; - instance->update->BeginPaint = guac_rdp_gdi_begin_paint; - instance->update->EndPaint = guac_rdp_gdi_end_paint; - instance->update->SetBounds = guac_rdp_gdi_set_bounds; + GUAC_RDP_CONTEXT(instance)->update->DesktopResize = guac_rdp_gdi_desktop_resize; + GUAC_RDP_CONTEXT(instance)->update->BeginPaint = guac_rdp_gdi_begin_paint; + GUAC_RDP_CONTEXT(instance)->update->EndPaint = guac_rdp_gdi_end_paint; + GUAC_RDP_CONTEXT(instance)->update->SetBounds = guac_rdp_gdi_set_bounds; - instance->update->SurfaceFrameMarker = guac_rdp_gdi_surface_frame_marker; - instance->update->altsec->FrameMarker = guac_rdp_gdi_frame_marker; + GUAC_RDP_CONTEXT(instance)->update->SurfaceFrameMarker = guac_rdp_gdi_surface_frame_marker; + GUAC_RDP_CONTEXT(instance)->update->altsec->FrameMarker = guac_rdp_gdi_frame_marker; - rdpPrimaryUpdate* primary = instance->update->primary; + rdpPrimaryUpdate* primary = GUAC_RDP_CONTEXT(instance)->update->primary; primary->DstBlt = guac_rdp_gdi_dstblt; primary->PatBlt = guac_rdp_gdi_patblt; primary->ScrBlt = guac_rdp_gdi_scrblt; primary->MemBlt = guac_rdp_gdi_memblt; primary->OpaqueRect = guac_rdp_gdi_opaquerect; - pointer_cache_register_callbacks(instance->update); - glyph_cache_register_callbacks(instance->update); - brush_cache_register_callbacks(instance->update); - bitmap_cache_register_callbacks(instance->update); - offscreen_cache_register_callbacks(instance->update); - palette_cache_register_callbacks(instance->update); - /* 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 (instance->settings->SupportDynamicChannels && + if (freerdp_settings_get_bool(GUAC_RDP_CONTEXT(instance)->settings, FreeRDP_SupportDynamicChannels) && guac_freerdp_channels_load_plugin(context, "drdynvc", - instance->settings)) { + 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."); @@ -254,7 +241,7 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { static BOOL rdp_freerdp_authenticate(freerdp* instance, char** username, char** password, char** domain) { - rdpContext* context = instance->context; + rdpContext* context = GUAC_RDP_CONTEXT(instance); 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; @@ -396,7 +383,7 @@ static DWORD rdp_freerdp_verify_certificate(freerdp* instance, const char* fingerprint, BOOL host_mismatch) { #endif - rdpContext* context = instance->context; + rdpContext* context = GUAC_RDP_CONTEXT(instance); guac_client* client = ((rdp_freerdp_context*) context)->client; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; @@ -432,7 +419,7 @@ static int rdp_guac_client_wait_for_messages(guac_client* client, freerdp* rdp_inst = rdp_client->rdp_inst; HANDLE handles[GUAC_RDP_MAX_FILE_DESCRIPTORS]; - int num_handles = freerdp_get_event_handles(rdp_inst->context, handles, + int num_handles = freerdp_get_event_handles(GUAC_RDP_CONTEXT(rdp_inst), handles, GUAC_RDP_MAX_FILE_DESCRIPTORS); /* Wait for data and construct a reasonable frame */ @@ -521,7 +508,7 @@ static int guac_rdp_handle_connection(guac_client* client) { goto fail; } - ((rdp_freerdp_context*) rdp_inst->context)->client = client; + ((rdp_freerdp_context*) GUAC_RDP_CONTEXT(rdp_inst))->client = client; /* Load keymap into client */ rdp_client->keyboard = guac_rdp_keyboard_alloc(client, @@ -579,7 +566,7 @@ static int guac_rdp_handle_connection(guac_client* client) { /* Handle any queued FreeRDP events (this may result in RDP * messages being sent) */ pthread_mutex_lock(&(rdp_client->message_lock)); - int event_result = freerdp_check_event_handles(rdp_inst->context); + int event_result = freerdp_check_event_handles(GUAC_RDP_CONTEXT(rdp_inst)); pthread_mutex_unlock(&(rdp_client->message_lock)); /* Abort if FreeRDP event handling fails */ @@ -622,7 +609,12 @@ static int guac_rdp_handle_connection(guac_client* client) { } /* Test whether the RDP server is closing the connection */ - int connection_closing = freerdp_shall_disconnect(rdp_inst); + int connection_closing; +#ifdef HAVE_DISCONNECT_CONTEXT + connection_closing = freerdp_shall_disconnect_context(rdp_inst->context); +#else + connection_closing = freerdp_shall_disconnect(rdp_inst); +#endif /* Close connection cleanly if server is disconnecting */ if (connection_closing) diff --git a/src/protocols/rdp/rdp.h b/src/protocols/rdp/rdp.h index 70668f420..4cda2c850 100644 --- a/src/protocols/rdp/rdp.h +++ b/src/protocols/rdp/rdp.h @@ -51,6 +51,20 @@ #include #include +#ifdef HAVE_WINPR_ALIGNED +#define GUAC_ALIGNED_FREE winpr_aligned_free +#define GUAC_ALIGNED_MALLOC winpr_aligned_malloc +#else +#define GUAC_ALIGNED_FREE _aligned_free +#define GUAC_ALIGNED_MALLOC _aligned_malloc +#endif + +#ifdef FREERDP_HAS_CONTEXT +#define GUAC_RDP_CONTEXT(rdp_instance) ((rdp_instance)->context) +#else +#define GUAC_RDP_CONTEXT(rdp_instance) ((rdp_instance)) +#endif + /** * RDP-specific client data. */ diff --git a/src/protocols/rdp/settings.c b/src/protocols/rdp/settings.c index 4b02b3eb1..62d019303 100644 --- a/src/protocols/rdp/settings.c +++ b/src/protocols/rdp/settings.c @@ -21,6 +21,7 @@ #include "common/defaults.h" #include "common/string.h" #include "config.h" +#include "rdp.h" #include "resolution.h" #include "settings.h" @@ -1394,18 +1395,6 @@ void guac_rdp_settings_free(guac_rdp_settings* settings) { } -int guac_rdp_get_width(freerdp* rdp) { - return rdp->settings->DesktopWidth; -} - -int guac_rdp_get_height(freerdp* rdp) { - return rdp->settings->DesktopHeight; -} - -int guac_rdp_get_depth(freerdp* rdp) { - return rdp->settings->ColorDepth; -} - /** * Given the settings structure of the Guacamole RDP client, calculates the * standard performance flag value to send to the RDP server. The value of @@ -1451,11 +1440,279 @@ static int guac_rdp_get_performance_flags(guac_rdp_settings* guac_settings) { } +int guac_rdp_get_width(freerdp* rdp) { +#ifdef HAVE_SETTERS_GETTERS + return freerdp_settings_get_uint32(rdp->context->settings, FreeRDP_DesktopWidth); +#else + return rdp->settings->DesktopWidth; +#endif +} + +int guac_rdp_get_height(freerdp* rdp) { +#ifdef HAVE_SETTERS_GETTERS + return freerdp_settings_get_uint32(rdp->context->settings, FreeRDP_DesktopHeight); +#else + return rdp->settings->DesktopHeight; +#endif +} + +int guac_rdp_get_depth(freerdp* rdp) { +#ifdef HAVE_SETTERS_GETTERS + return freerdp_settings_get_uint32(rdp->context->settings, FreeRDP_ColorDepth); +#else + return rdp->settings->ColorDepth; +#endif +} + void guac_rdp_push_settings(guac_client* client, guac_rdp_settings* guac_settings, freerdp* rdp) { - rdpSettings* rdp_settings = rdp->settings; + rdpSettings* rdp_settings = GUAC_RDP_CONTEXT(rdp)->settings; + +#ifdef HAVE_SETTERS_GETTERS + /* Authentication */ + freerdp_settings_set_string(rdp_settings, FreeRDP_Domain, guac_strdup(guac_settings->domain)); + freerdp_settings_set_string(rdp_settings, FreeRDP_Username, guac_strdup(guac_settings->username)); + freerdp_settings_set_string(rdp_settings, FreeRDP_Password, guac_strdup(guac_settings->password)); + + /* Connection */ + freerdp_settings_set_string(rdp_settings, FreeRDP_ServerHostname, guac_strdup(guac_settings->hostname)); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_ServerPort, guac_settings->port); + + /* Session */ + + freerdp_settings_set_uint32(rdp_settings, FreeRDP_DesktopWidth, guac_settings->width); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_DesktopHeight, guac_settings->height); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_ColorDepth, guac_settings->color_depth); + freerdp_settings_set_string(rdp_settings, FreeRDP_AlternateShell, guac_strdup(guac_settings->initial_program)); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_KeyboardLayout, guac_settings->server_layout->freerdp_keyboard_layout); + + + /* Performance flags */ + /* Explicitly set flag value */ + freerdp_settings_set_uint32(rdp_settings, FreeRDP_PerformanceFlags, guac_rdp_get_performance_flags(guac_settings)); + + /* Always request frame markers */ + freerdp_settings_set_bool(rdp_settings, FreeRDP_FrameMarkerCommandEnabled, TRUE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_SurfaceFrameMarkerEnabled, TRUE); + + /* Enable RemoteFX / Graphics Pipeline */ + if (guac_settings->enable_gfx) { + + freerdp_settings_set_bool(rdp_settings, FreeRDP_SupportGraphicsPipeline, TRUE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_RemoteFxCodec, TRUE); + + if (freerdp_settings_get_uint32(rdp_settings, FreeRDP_ColorDepth) != RDP_GFX_REQUIRED_DEPTH) { + guac_client_log(client, GUAC_LOG_WARNING, "Ignoring requested " + "color depth of %i bpp, as the RDP Graphics Pipeline " + "requires %i bpp.", freerdp_settings_get_uint32(rdp_settings, FreeRDP_ColorDepth), RDP_GFX_REQUIRED_DEPTH); + } + + /* Required for RemoteFX / Graphics Pipeline */ + freerdp_settings_set_bool(rdp_settings, FreeRDP_FastPathOutput, TRUE); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_ColorDepth, RDP_GFX_REQUIRED_DEPTH); + freerdp_settings_set_bool(rdp_settings, FreeRDP_SoftwareGdi, TRUE); + + } + + /* Set individual flags - some FreeRDP versions overwrite the above */ + freerdp_settings_set_bool(rdp_settings, FreeRDP_AllowFontSmoothing, guac_settings->font_smoothing_enabled); + freerdp_settings_set_bool(rdp_settings, FreeRDP_DisableWallpaper, guac_settings->wallpaper_enabled); + freerdp_settings_set_bool(rdp_settings, FreeRDP_DisableFullWindowDrag, guac_settings->full_window_drag_enabled); + freerdp_settings_set_bool(rdp_settings, FreeRDP_DisableMenuAnims, guac_settings->menu_animations_enabled); + freerdp_settings_set_bool(rdp_settings, FreeRDP_DisableThemes, guac_settings->theming_enabled); + freerdp_settings_set_bool(rdp_settings, FreeRDP_AllowDesktopComposition, guac_settings->desktop_composition_enabled); + + /* Client name */ + if (guac_settings->client_name != NULL) { + freerdp_settings_set_string(rdp_settings, FreeRDP_ClientHostname, + guac_strndup(guac_settings->client_name, RDP_CLIENT_HOSTNAME_SIZE)); + } + + /* Console */ + freerdp_settings_set_bool(rdp_settings, FreeRDP_ConsoleSession, guac_settings->console); + freerdp_settings_set_bool(rdp_settings, FreeRDP_RemoteConsoleAudio, guac_settings->console_audio); + + /* Audio */ + freerdp_settings_set_bool(rdp_settings, FreeRDP_AudioPlayback, guac_settings->audio_enabled); + + /* Audio capture */ + freerdp_settings_set_bool(rdp_settings, FreeRDP_AudioCapture, guac_settings->enable_audio_input); + + /* Display Update channel */ + freerdp_settings_set_bool(rdp_settings, FreeRDP_SupportDisplayControl, + (guac_settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE)); + + /* Timezone redirection */ + if (guac_settings->timezone) { + if (setenv("TZ", guac_settings->timezone, 1)) { + guac_client_log(client, GUAC_LOG_WARNING, + "Unable to forward timezone: TZ environment variable " + "could not be set: %s", strerror(errno)); + } + } + + /* Device redirection */ + freerdp_settings_set_bool(rdp_settings, FreeRDP_DeviceRedirection, + (guac_settings->audio_enabled || guac_settings->drive_enabled || guac_settings->printing_enabled)); + + /* Security */ + switch (guac_settings->security_mode) { + + /* Legacy RDP encryption */ + case GUAC_SECURITY_RDP: + freerdp_settings_set_bool(rdp_settings, FreeRDP_RdpSecurity, TRUE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_TlsSecurity, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_NlaSecurity, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_ExtSecurity, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_UseRdpSecurityLayer, TRUE); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_EncryptionLevel, + ENCRYPTION_LEVEL_CLIENT_COMPATIBLE); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_EncryptionMethods, + ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS); + break; + + /* TLS encryption */ + case GUAC_SECURITY_TLS: + freerdp_settings_set_bool(rdp_settings, FreeRDP_RdpSecurity, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_TlsSecurity, TRUE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_NlaSecurity, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_ExtSecurity, FALSE); + break; + + /* Network level authentication */ + case GUAC_SECURITY_NLA: + freerdp_settings_set_bool(rdp_settings, FreeRDP_RdpSecurity, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_TlsSecurity, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_NlaSecurity, TRUE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_ExtSecurity, FALSE); + break; + + /* Extended network level authentication */ + case GUAC_SECURITY_EXTENDED_NLA: + freerdp_settings_set_bool(rdp_settings, FreeRDP_RdpSecurity, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_TlsSecurity, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_NlaSecurity, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_ExtSecurity, TRUE); + break; + + /* Hyper-V "VMConnect" negotiation mode */ + case GUAC_SECURITY_VMCONNECT: + freerdp_settings_set_bool(rdp_settings, FreeRDP_RdpSecurity, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_TlsSecurity, TRUE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_NlaSecurity, TRUE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_ExtSecurity, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_VmConnectMode, TRUE); + break; + + /* All security types */ + case GUAC_SECURITY_ANY: + freerdp_settings_set_bool(rdp_settings, FreeRDP_RdpSecurity, TRUE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_TlsSecurity, TRUE); + + /* Explicitly disable NLA if FIPS mode is enabled - it won't work */ + if (guac_fips_enabled()) { + + guac_client_log(client, GUAC_LOG_INFO, + "FIPS mode is enabled. Excluding NLA security mode from security negotiation " + "(see: https://github.com/FreeRDP/FreeRDP/issues/3412)."); + freerdp_settings_set_bool(rdp_settings, FreeRDP_NlaSecurity, FALSE); + + } + + /* NLA mode is allowed if FIPS is not enabled */ + else + freerdp_settings_set_bool(rdp_settings, FreeRDP_NlaSecurity, TRUE); + + freerdp_settings_set_bool(rdp_settings, FreeRDP_ExtSecurity, FALSE); + break; + + } + + /* Security */ + freerdp_settings_set_bool(rdp_settings, FreeRDP_Authentication, !guac_settings->disable_authentication); + freerdp_settings_set_bool(rdp_settings, FreeRDP_IgnoreCertificate, guac_settings->ignore_certificate); + freerdp_settings_set_bool(rdp_settings, FreeRDP_AutoAcceptCertificate, guac_settings->certificate_tofu); + if (guac_settings->certificate_fingerprints != NULL) + freerdp_settings_set_string(rdp_settings, FreeRDP_CertificateAcceptedFingerprints, + guac_strdup(guac_settings->certificate_fingerprints)); + + + /* RemoteApp */ + if (guac_settings->remote_app != NULL) { + freerdp_settings_set_bool(rdp_settings, FreeRDP_Workarea, TRUE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_RemoteApplicationMode, TRUE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_RemoteAppLanguageBarSupported, TRUE); + freerdp_settings_set_string(rdp_settings, FreeRDP_RemoteApplicationProgram, guac_strdup(guac_settings->remote_app)); + freerdp_settings_set_string(rdp_settings, FreeRDP_ShellWorkingDirectory, guac_strdup(guac_settings->remote_app_dir)); + freerdp_settings_set_string(rdp_settings, FreeRDP_RemoteApplicationCmdLine, guac_strdup(guac_settings->remote_app_args)); + } + + /* Preconnection ID */ + if (guac_settings->preconnection_id != -1) { + freerdp_settings_set_bool(rdp_settings, FreeRDP_NegotiateSecurityLayer, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_SendPreconnectionPdu, TRUE); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_PreconnectionId, guac_settings->preconnection_id); + } + + /* Preconnection BLOB */ + if (guac_settings->preconnection_blob != NULL) { + freerdp_settings_set_bool(rdp_settings, FreeRDP_NegotiateSecurityLayer, FALSE); + freerdp_settings_set_bool(rdp_settings, FreeRDP_SendPreconnectionPdu, TRUE); + freerdp_settings_set_string(rdp_settings, FreeRDP_PreconnectionBlob, guac_strdup(guac_settings->preconnection_blob)); + } + + /* Enable use of RD gateway if a gateway hostname is provided */ + if (guac_settings->gateway_hostname != NULL) { + + /* Enable RD gateway */ + freerdp_settings_set_bool(rdp_settings, FreeRDP_GatewayEnabled, TRUE); + /* RD gateway connection details */ + freerdp_settings_set_string(rdp_settings, FreeRDP_GatewayHostname, guac_strdup(guac_settings->gateway_hostname)); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_GatewayPort, guac_settings->gateway_port); + + /* RD gateway credentials */ + freerdp_settings_set_bool(rdp_settings, FreeRDP_GatewayUseSameCredentials, FALSE); + freerdp_settings_set_string(rdp_settings, FreeRDP_GatewayDomain, guac_strdup(guac_settings->gateway_domain)); + freerdp_settings_set_string(rdp_settings, FreeRDP_GatewayUsername, guac_strdup(guac_settings->gateway_username)); + freerdp_settings_set_string(rdp_settings, FreeRDP_GatewayPassword, guac_strdup(guac_settings->gateway_password)); + + } + + /* Store load balance info (and calculate length) if provided */ + if (guac_settings->load_balance_info != NULL) { + freerdp_settings_set_pointer(rdp_settings, FreeRDP_LoadBalanceInfo, (BYTE*) guac_strdup(guac_settings->load_balance_info)); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_LoadBalanceInfoLength, strlen(guac_settings->load_balance_info)); + } + + freerdp_settings_set_bool(rdp_settings, FreeRDP_BitmapCacheEnabled, !guac_settings->disable_bitmap_caching); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_OffscreenSupportLevel, !guac_settings->disable_offscreen_caching); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_GlyphSupportLevel, + (!guac_settings->disable_glyph_caching ? GLYPH_SUPPORT_FULL : GLYPH_SUPPORT_NONE)); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNSPECIFIED); + freerdp_settings_set_uint32(rdp_settings, FreeRDP_OsMinorType, OSMINORTYPE_UNSPECIFIED); + freerdp_settings_set_bool(rdp_settings, FreeRDP_DesktopResize, TRUE); + + /* Claim support only for specific updates, independent of FreeRDP defaults */ + BYTE* order_support = freerdp_settings_get_pointer_writable(rdp_settings, FreeRDP_OrderSupport); + if (order_support) { + ZeroMemory(order_support, GUAC_RDP_ORDER_SUPPORT_LENGTH); + order_support[NEG_DSTBLT_INDEX] = TRUE; + order_support[NEG_SCRBLT_INDEX] = TRUE; + order_support[NEG_MEMBLT_INDEX] = !guac_settings->disable_bitmap_caching; + order_support[NEG_MEMBLT_V2_INDEX] = !guac_settings->disable_bitmap_caching; + order_support[NEG_GLYPH_INDEX_INDEX] = !guac_settings->disable_glyph_caching; + order_support[NEG_FAST_INDEX_INDEX] = !guac_settings->disable_glyph_caching; + order_support[NEG_FAST_GLYPH_INDEX] = !guac_settings->disable_glyph_caching; + } + +#ifdef HAVE_RDPSETTINGS_ALLOWUNANOUNCEDORDERSFROMSERVER + /* Do not consider server use of unannounced orders to be a fatal error */ + freerdp_settings_set_bool(rdp_settings, FreeRDP_AllowUnanouncedOrdersFromServer, TRUE); +#endif + +#else /* Authentication */ rdp_settings->Domain = guac_strdup(guac_settings->domain); rdp_settings->Username = guac_strdup(guac_settings->username); @@ -1692,5 +1949,5 @@ void guac_rdp_push_settings(guac_client* client, rdp_settings->AllowUnanouncedOrdersFromServer = TRUE; #endif +#endif } -