Merge staging/1.1.0 changes back to master.
diff --git a/Dockerfile b/Dockerfile
index 094842c..83bce0f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -37,9 +37,9 @@
ARG BUILD_DEPENDENCIES=" \
autoconf \
automake \
+ freerdp2-dev \
gcc \
libcairo2-dev \
- libfreerdp-dev \
libjpeg62-turbo-dev \
libossp-uuid-dev \
libpango1.0-dev \
@@ -71,7 +71,7 @@
RUN ${PREFIX_DIR}/bin/list-dependencies.sh \
${PREFIX_DIR}/sbin/guacd \
${PREFIX_DIR}/lib/libguac-client-*.so \
- ${PREFIX_DIR}/lib/freerdp/guac*.so \
+ ${PREFIX_DIR}/lib/freerdp2/guac*.so \
> ${PREFIX_DIR}/DEPENDENCIES
# Use same Debian as the base for the runtime image
@@ -92,7 +92,6 @@
ARG RUNTIME_DEPENDENCIES=" \
ca-certificates \
ghostscript \
- libfreerdp-plugins-standard \
fonts-liberation \
fonts-dejavu \
xfonts-terminus"
@@ -108,7 +107,7 @@
# Link FreeRDP plugins into proper path
RUN ${PREFIX_DIR}/bin/link-freerdp-plugins.sh \
- ${PREFIX_DIR}/lib/freerdp/guac*.so
+ ${PREFIX_DIR}/lib/freerdp2/libguac*.so
# Expose the default listener port
EXPOSE 4822
diff --git a/configure.ac b/configure.ac
index 7fc7d33..5b0fbe8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -555,537 +555,143 @@
fi
#
-# FreeRDP
+# FreeRDP 2 (libfreerdp2, libfreerdp-client2, and libwinpr2)
#
-have_freerdp=disabled
-RDP_LIBS=
-WINPR_LIBS=
+have_freerdp2=disabled
AC_ARG_WITH([rdp],
[AS_HELP_STRING([--with-rdp],
[support RDP @<:@default=check@:>@])],
[],
[with_rdp=check])
+# Preserve CPPFLAGS so it can be restored later, following the addition of
+# options specific to FreeRDP tests
+OLDCPPFLAGS="$CPPFLAGS"
+
if test "x$with_rdp" != "xno"
then
- have_winpr=yes
- have_freerdp=yes
- legacy_freerdp_extensions=no
- rdpsettings_interface=unknown
- rdpsettings_audiocapture=yes
- rdpsettings_audioplayback=yes
- rdpsettings_deviceredirection=yes
- freerdp_interface=unknown
- event_interface=unknown
-
- # libfreerdp-core / libfreerdp
- AC_CHECK_LIB([freerdp-core], [freerdp_new],
- [RDP_LIBS="$RDP_LIBS -lfreerdp-core"],
- [AC_CHECK_LIB([freerdp], [freerdp_new],
- [RDP_LIBS="$RDP_LIBS -lfreerdp -lfreerdp-client"],
- [AC_MSG_WARN([
+ have_freerdp2=yes
+ PKG_CHECK_MODULES([RDP], [freerdp2 freerdp-client2 winpr2],
+ [CPPFLAGS="${RDP_CFLAGS} -Werror $CPPFLAGS"],
+ [AC_MSG_WARN([
--------------------------------------------
- Unable to find libfreerdp-core / libfreerdp
+ Unable to find FreeRDP (libfreerdp2 / libfreerdp-client2 / libwinpr2)
RDP will be disabled.
--------------------------------------------])
- have_freerdp=no])])
-fi
-
-
-# libfreerdp-cache
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_LIB([freerdp-cache], [glyph_cache_register_callbacks],
- [RDP_LIBS="$RDP_LIBS -lfreerdp-cache"])
-fi
-
-# libfreerdp-channels (1.0) / libfreerdp-client + libfreerdp-core (1.1)
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_LIB([freerdp-client], [freerdp_channels_new],
- [RDP_LIBS="$RDP_LIBS -lfreerdp-client"],
- [AC_CHECK_LIB([freerdp-channels], [freerdp_channels_new],
- [RDP_LIBS="$RDP_LIBS -lfreerdp-channels"
- legacy_freerdp_extensions=yes])],
- [-lfreerdp-core])
-fi
-
-# libfreerdp-utils
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_LIB([freerdp-utils], [svc_plugin_init],
- [RDP_LIBS="$RDP_LIBS -lfreerdp-utils"])
-fi
-
-# libfreerdp-codec
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_LIB([freerdp-codec], [freerdp_image_convert],
- [RDP_LIBS="$RDP_LIBS -lfreerdp-codec"])
+ have_freerdp2=no])
fi
# Available color conversion functions
-if test "x${have_freerdp}" = "xyes"
+if test "x$have_freerdp2" = "xyes"
then
- AC_CHECK_DECL([freerdp_convert_gdi_order_color],
- [AC_DEFINE([HAVE_FREERDP_CONVERT_GDI_ORDER_COLOR],,
- [Whether freerdp_convert_gdi_order_color() is defined])],,
- [#include <freerdp/codec/color.h>])
- AC_CHECK_DECL([freerdp_color_convert_drawing_order_color_to_gdi_color],
- [AC_DEFINE([HAVE_FREERDP_COLOR_CONVERT_DRAWING_ORDER_COLOR_TO_GDI_COLOR],,
- [Whether freerdp_color_convert_drawing_order_color_to_gdi_color() is defined])],,
- [#include <freerdp/codec/color.h>])
+ # FreeRDP 2.0.0-rc3 and older referred to FreeRDPConvertColor() as
+ # ConvertColor()
+ AC_CHECK_DECL([FreeRDPConvertColor],
+ [AC_DEFINE([HAVE_FREERDPCONVERTCOLOR],,
+ [Whether FreeRDPConvertColor() is defined])],,
+ [#include <freerdp/codec/color.h>])
+
fi
-# Check for interval polling in plugins
-if test "x${have_freerdp}" = "xyes"
+# Glyph callback variants
+if test "x${have_freerdp2}" = "xyes"
then
- AC_CHECK_MEMBERS([rdpSvcPlugin.interval_ms],,,
- [[#include <freerdp/utils/svc_plugin.h>]])
+
+ # FreeRDP 2.0.0-rc3 and older used UINT32 for integer parameters to all
+ # rdpGlyph callbacks
+ AC_MSG_CHECKING([whether rdpGlyph callbacks accept INT32 integer parameters])
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+
+ #include <freerdp/freerdp.h>
+ #include <freerdp/graphics.h>
+ #include <winpr/wtypes.h>
+
+ BOOL test_begindraw(rdpContext* context, INT32 x, INT32 y,
+ INT32 width, INT32 height, UINT32 fgcolor, UINT32 bgcolor,
+ BOOL redundant);
+
+ rdpGlyph glyph = {
+ .BeginDraw = test_begindraw
+ };
+
+ int main() {
+ return (int) glyph.BeginDraw(NULL, 0, 0, 0, 0, 0, 0, FALSE);
+ }
+
+ ]])],
+ [AC_MSG_RESULT([yes])]
+ [AC_DEFINE([FREERDP_GLYPH_CALLBACKS_ACCEPT_INT32],,
+ [Whether rdpGlyph callbacks accept INT32 integer parameters])],
+ [AC_MSG_RESULT([no])])
+
fi
-# Keyboard layout header
-if test "x${have_freerdp}" = "xyes"
+# CLIPRDR callback variants
+if test "x${have_freerdp2}" = "xyes"
then
- AC_CHECK_HEADERS([freerdp/locale/keyboard.h],,
- [AC_CHECK_HEADERS([freerdp/kbd/layouts.h],,
- [AC_MSG_WARN([
- --------------------------------------------
- Unable to find keyboard layout headers
- RDP will be disabled.
- --------------------------------------------])
- have_freerdp=no])])
+
+ # FreeRDP 2.0.0-rc3 and older did not use const for CLIPRDR callbacks
+ AC_MSG_CHECKING([whether CLIPRDR callbacks require const for their final parameter])
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+
+ #include <freerdp/client/cliprdr.h>
+ #include <winpr/wtypes.h>
+
+ UINT test_monitor_ready(CliprdrClientContext* cliprdr,
+ const CLIPRDR_MONITOR_READY* monitor_ready);
+
+ CliprdrClientContext context = {
+ .MonitorReady = test_monitor_ready
+ };
+
+ int main() {
+ return (int) context.MonitorReady(NULL, NULL);
+ }
+
+ ]])],
+ [AC_MSG_RESULT([yes])]
+ [AC_DEFINE([FREERDP_CLIPRDR_CALLBACKS_REQUIRE_CONST],,
+ [Whether CLIPRDR callbacks require const for the final parameter])],
+ [AC_MSG_RESULT([no])])
+
fi
-# New headers defining addins
-if test "x${have_freerdp}" = "xyes"
+# RAIL callback variants
+if test "x${have_freerdp2}" = "xyes"
then
- AC_CHECK_HEADERS([freerdp/addin.h freerdp/client/channels.h])
+
+ # FreeRDP 2.0.0-rc3 and older did not use const for RAIL callbacks
+ AC_MSG_CHECKING([whether RAIL callbacks require const for their final parameter])
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+
+ #include <freerdp/client/rail.h>
+ #include <winpr/wtypes.h>
+
+ UINT test_server_handshake(RailClientContext* rail,
+ const RAIL_HANDSHAKE_ORDER* handshake);
+
+ RailClientContext context = {
+ .ServerHandshake = test_server_handshake
+ };
+
+ int main() {
+ return (int) context.ServerHandshake(NULL, NULL);
+ }
+
+ ]])],
+ [AC_MSG_RESULT([yes])]
+ [AC_DEFINE([FREERDP_RAIL_CALLBACKS_REQUIRE_CONST],,
+ [Whether RAIL callbacks require const for the final parameter])],
+ [AC_MSG_RESULT([no])])
+
fi
-# Header defining cliprdr
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_HEADERS([freerdp/client/cliprdr.h],,
- [AC_CHECK_HEADERS([freerdp/plugins/cliprdr.h],,
- [AC_MSG_WARN([
- --------------------------------------------
- Unable to find cliprdr headers
- RDP will be disabled.
- --------------------------------------------])
- have_freerdp=no],
- [#include <freerdp/types.h>])],
- [#include <winpr/wtypes.h>
- #include <winpr/collections.h>])
-fi
+# Restore CPPFLAGS, removing FreeRDP-specific options needed for testing
+CPPFLAGS="$OLDCPPFLAGS"
-# Header defining display update channel
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_HEADERS([freerdp/client/disp.h],
- [AC_DEFINE([HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT],,
- [Whether FreeRDP supports the display update channel])]
- [AC_CHECK_MEMBERS([rdpSettings.SupportDisplayControl],,,
- [[#include <freerdp/freerdp.h>]])],,
- [#include <winpr/wtypes.h>
- #include <winpr/collections.h>])
-fi
-
-# Support for RDP gateways
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_MEMBERS([rdpSettings.GatewayEnabled],
- [AC_DEFINE([HAVE_FREERDP_GATEWAY_SUPPORT],,
- [Whether FreeRDP supports RDP gateways])],,
- [[#include <freerdp/freerdp.h>]])
-fi
-
-# Support for load balancing via connection brokers
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_MEMBERS([rdpSettings.LoadBalanceInfo],
- [AC_DEFINE([HAVE_FREERDP_LOAD_BALANCER_SUPPORT],,
- [Whether FreeRDP supports load balancers])],,
- [[#include <freerdp/freerdp.h>]])
-fi
-
-# Support for "PubSub" event system
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_DECL([PubSub_SubscribeChannelConnected],
- [AC_DEFINE([HAVE_FREERDP_EVENT_PUBSUB],,
- [Whether this version of FreeRDP provides the PubSub event system])],,
- [#include <freerdp/event.h>])
-fi
-
-# Addin registration variations
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_DECL([freerdp_register_addin_provider],
- [AC_DEFINE([HAVE_FREERDP_REGISTER_ADDIN_PROVIDER],,
- [Whether freerdp_register_addin_provider() is defined])],,
- [#include <freerdp/addin.h>])
-
- AC_CHECK_DECL([freerdp_channels_global_init],
- [AC_DEFINE([HAVE_FREERDP_CHANNELS_GLOBAL_INIT],,
- [Whether freerdp_channels_global_init() is defined])],,
- [#include <freerdp/channels/channels.h>])
-fi
-
-# Availability of ADDIN_ARGV structure for configuring plugins
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_TYPE([ADDIN_ARGV],
- [AC_DEFINE([HAVE_ADDIN_ARGV],,
- [Whether the ADDIN_ARGV type is available])],,
- [#include <freerdp/settings.h>])
-fi
-
-#
-# FreeRDP: WinPR
-#
-
-# Check for stream support via WinPR
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_HEADER(winpr/stream.h,,
- [have_winpr=no,
- AC_CHECK_DECL([stream_write_uint8],,
- [AC_MSG_WARN([
- --------------------------------------------
- Unable to find stream support
- RDP will be disabled.
- --------------------------------------------])
- have_freerdp=no],
- [#include <freerdp/utils/stream.h>])])
-fi
-
-# Find location of Stream_New and Stream_free
-if test "x${have_freerdp}" = "xyes" -a "x${have_winpr}" = "xyes"
-then
- AC_CHECK_LIB([winpr], [Stream_New, Stream_Free],
- [WINPR_LIBS="$WINPR_LIBS -lwinpr"],
- [AC_CHECK_LIB([winpr-utils], [Stream_New, Stream_Free],
- [WINPR_LIBS="$WINPR_LIBS -lwinpr-utils"],
- [AC_MSG_WARN([
- ------------------------------------------
- Unable to locate stream functions in winpr
- libraries. RDP will be disabled.
- ------------------------------------------])
- have_freerdp=no])])
-fi
-
-# Check for types in WinPR
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_HEADER(winpr/wtypes.h,,
- [have_winpr=no,
- AC_CHECK_HEADER(freerdp/types.h,,
- [AC_MSG_WARN([
- --------------------------------------------
- Unable to find type definitions
- RDP will be disabled.
- --------------------------------------------])
- have_freerdp=no])])
-fi
-
-if test "x${have_freerdp}" = "xyes" -a "x${have_winpr}" = "xyes"
-then
- AC_DEFINE([ENABLE_WINPR],,
- [Whether library support for WinPR types was found])
-fi
-
-#
-# FreeRDP: freerdp
-#
-
-if test "x${have_freerdp}" = "xyes"
-then
- # Check for current (as of 1.1) freerdp interface
- AC_CHECK_MEMBERS([freerdp.ContextSize],
- [freerdp_interface=stable],,
- [[#include <freerdp/freerdp.h>]])
-
- # If not current, check for legacy interface
- if test "x${freerdp_interface}" = "xunknown"
- then
- AC_CHECK_MEMBERS([freerdp.context_size],
- [freerdp_interface=legacy],,
- [[#include <freerdp/freerdp.h>]])
- fi
-
- # Set defines based on interface type, warn if unknown
- if test "x${freerdp_interface}" = "xlegacy"; then
- AC_DEFINE([LEGACY_FREERDP],,
- [Whether the older version of the FreeRDP API was found])
- elif test "x${freerdp_interface}" = "xunknown"; then
- AC_MSG_WARN([
- --------------------------------------------
- Unknown FreeRDP interface
- RDP will be disabled.
- --------------------------------------------])
- have_freerdp=no
- fi
-fi
-
-#
-# FreeRDP: rdpSettings
-#
-
-if test "x${have_freerdp}" = "xyes"
-then
- # Check for current (as of 1.1) rdpSettings interface
- AC_CHECK_MEMBERS([rdpSettings.Width,
- rdpSettings.Height,
- rdpSettings.FastPathInput,
- rdpSettings.FastPathOutput,
- rdpSettings.SendPreconnectionPdu,
- rdpSettings.OrderSupport],
- [rdpsettings_interface=stable],,
- [[#include <freerdp/freerdp.h>]])
-
- # If not current, check for legacy interface
- if test "x${rdpsettings_interface}" = "xunknown"
- then
- AC_CHECK_MEMBERS([rdpSettings.width,
- rdpSettings.height,
- rdpSettings.order_support],
- [rdpsettings_interface=legacy],,
- [[#include <freerdp/freerdp.h>]])
- fi
-
- # Set defines based on interface type, warn if unknown
- if test "x${rdpsettings_interface}" = "xlegacy"; then
- AC_DEFINE([LEGACY_RDPSETTINGS],,
- [Whether the legacy version of the rdpSettings API was found])
-
- # Legacy interface may not have AudioPlayback settings
- AC_CHECK_MEMBERS([rdpSettings.audio_playback],,
- [rdpsettings_audioplayback=no],
- [[#include <freerdp/freerdp.h>]])
-
- # Legacy interface may not have AudioCapture settings
- AC_CHECK_MEMBERS([rdpSettings.audio_capture],,
- [rdpsettings_audiocapture=no],
- [[#include <freerdp/freerdp.h>]])
-
- # Legacy interface may not have DeviceRedirection settings
- AC_CHECK_MEMBERS([rdpSettings.device_redirection],,
- [rdpsettings_deviceredirection=no],
- [[#include <freerdp/freerdp.h>]])
-
- elif test "x${rdpsettings_interface}" = "xunknown"; then
- AC_MSG_WARN([
- --------------------------------------------
- Unknown rdpSettings interface
- RDP will be disabled.
- --------------------------------------------])
- have_freerdp=no
- fi
-fi
-
-# Activate audio playback settings if present
-if test "x${have_freerdp}" = "xyes" -a "x${rdpsettings_audioplayback}" = "xyes"; then
- AC_DEFINE([HAVE_RDPSETTINGS_AUDIOPLAYBACK],,
- [Whether the rdpSettings structure has AudioPlayback settings])
-fi
-
-# Activate audio capture settings if present
-if test "x${have_freerdp}" = "xyes" -a "x${rdpsettings_audiocapture}" = "xyes"; then
- AC_DEFINE([HAVE_RDPSETTINGS_AUDIOCAPTURE],,
- [Whether the rdpSettings structure has AudioCapture settings])
-fi
-
-# Activate device redirection settings if present
-if test "x${have_freerdp}" = "xyes" -a "x${rdpsettings_deviceredirection}" = "xyes"; then
- AC_DEFINE([HAVE_RDPSETTINGS_DEVICEREDIRECTION],,
- [Whether the rdpSettings structure has DeviceRedirection settings])
-fi
-
-# Check if the type CHANNEL_ENTRY_POINTS_FREERDP exists, if not define it to CHANNEL_ENTRY_POINTS_EX
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_TYPE([CHANNEL_ENTRY_POINTS_FREERDP],,
- AC_DEFINE([CHANNEL_ENTRY_POINTS_FREERDP],[CHANNEL_ENTRY_POINTS_EX], [Type compatibility]),
- [[#include <freerdp/svc.h>]])
-fi
-
-# Check if the freerdp version header exists
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_HEADERS([freerdp/version.h])
-fi
-
-#
-# FreeRDP: rdpBitmap
-#
-
-if test "x${have_freerdp}" = "xyes"
-then
- AC_MSG_CHECKING([whether rdpBitmap.Decompress() requires the codec_id])
- AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <winpr/wtypes.h>
- #include <freerdp/freerdp.h>
- void __decompress(rdpContext* context,
- rdpBitmap* bitmap,
- UINT8* data,
- int width,
- int height,
- int bpp,
- int length,
- BOOL compressed,
- int codec_id);
- rdpBitmap b = { .Decompress = __decompress };]])],
- [AC_MSG_RESULT([yes])],
- [AC_MSG_RESULT([no])
- AC_DEFINE([LEGACY_RDPBITMAP],,
- [Whether the legacy rdpBitmap API was found])])
-fi
-
-#
-# FreeRDP: IWTSVirtualChannelCallback
-#
-
-if test "x${have_freerdp}" = "xyes"
-then
- AC_MSG_CHECKING([whether IWTSVirtualChannelCallback.OnDataReceived() uses a wStream])
- AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <winpr/wtypes.h>
- #include <freerdp/dvc.h>
- #include <freerdp/freerdp.h>
- int __data_received(
- IWTSVirtualChannelCallback* channel_callback,
- wStream* stream);
- IWTSVirtualChannelCallback cb = {
- .OnDataReceived = __data_received
- };
- int main() {
- return
- cb.OnDataReceived(NULL, NULL);
- }]])],
- [AC_MSG_RESULT([yes])],
- [AC_MSG_RESULT([no])
- AC_DEFINE([LEGACY_IWTSVIRTUALCHANNELCALLBACK],,
- [Whether the legacy IWTSVirtualChannelCallback API was found])])
-fi
-
-#
-# FreeRDP: Decompression function variants
-#
-
-# Check whether interleaved_decompress() can handle the palette
-if test "x${have_freerdp}" = "xyes"
-then
- AC_MSG_CHECKING([whether interleaved_decompress() accepts an additional palette parameter])
- AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <freerdp/codec/interleaved.h>
-
- int main() {
- BYTE* palette = NULL;
- interleaved_decompress(NULL, NULL, 0, 0, NULL, 0, 0, 0, 0, 0, 0, palette);
- }]])],
- [AC_MSG_RESULT([yes])
- AC_DEFINE([INTERLEAVED_DECOMPRESS_TAKES_PALETTE],,
- [Whether interleaved_decompress() accepts an additional palette parameter])],
- [AC_MSG_RESULT([no])])
-fi
-
-# Check whether planar_decompress() will handle flipping
-if test "x${have_freerdp}" = "xyes"
-then
- AC_MSG_CHECKING([whether planar_decompress() can flip])
- AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <freerdp/codec/planar.h>
-
- int main() {
- BOOL* flip = TRUE;
- planar_decompress(NULL, NULL, 0, NULL, 0, 0, 0, 0, 0, 0, flip);
- }]])],
- [AC_MSG_RESULT([yes])
- AC_DEFINE([PLANAR_DECOMPRESS_CAN_FLIP],,
- [Whether planar_decompress() can flip])],
- [AC_MSG_RESULT([no])])
-fi
-
-#
-# FreeRDP: rdpContext
-#
-
-# Check for rdpContext.codecs
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_MEMBERS([rdpContext.codecs],
- [AC_DEFINE([FREERDP_BITMAP_REQUIRES_ALIGNED_MALLOC],,
- [Whether this version of FreeRDP requires _aligned_malloc() for bitmap data])],,
- [[#include <freerdp/freerdp.h>]])
-fi
-
-#
-# FreeRDP: rdpPalette
-#
-
-if test "x${have_freerdp}" = "xyes"
-then
- AC_MSG_CHECKING([whether rdpPalette.entries is static])
- AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <freerdp/update.h>
- rdpPalette p;
- PALETTE_ENTRY* foo = p.entries;]])],
- [AC_MSG_RESULT([yes])],
- [AC_MSG_RESULT([no])
- AC_DEFINE([LEGACY_RDPPALETTE],,
- [Whether the legacy rdpPalette API was found])])
-fi
-
-#
-# FreeRDP: rdpPointer
-#
-
-# Check for SetDefault and SetNull members of rdpPointer
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_MEMBERS([rdpPointer.SetDefault,
- rdpPointer.SetNull],
- ,,
- [[#include <freerdp/freerdp.h>]])
-fi
-
-#
-# FreeRDP: wMessage / RDP_EVENT
-#
-
-# Check for current (as of 1.1) wMessage interface
-if test "x${have_freerdp}" = "xyes"
-then
- AC_CHECK_MEMBERS([wMessage.id],
- [event_interface=stable],,
- [[#include <winpr/collections.h>]])
-
- # If not current, check for legacy (RDP_EVENT) interface
- if test "x${event_interface}" = "xunknown"
- then
- AC_CHECK_MEMBERS([RDP_EVENT.event_class],
- [event_interface=legacy],,
- [[#include <freerdp/types.h>]])
- fi
-
- # Set defines based on interface type, warn if unknown
- if test "x${event_interface}" = "xlegacy"; then
- AC_DEFINE([LEGACY_EVENT],,
- [Whether the legacy RDP_EVENT API was found])
- elif test "x${event_interface}" = "xunknown"; then
- AC_MSG_WARN([
- --------------------------------------------
- Unknown event interface
- RDP will be disabled.
- --------------------------------------------])
- have_freerdp=no
- fi
-fi
-
-AM_CONDITIONAL([LEGACY_FREERDP_EXTENSIONS], [test "x${legacy_freerdp_extensions}" = "xyes"])
-AM_CONDITIONAL([ENABLE_WINPR], [test "x${have_winpr}" = "xyes"])
-AM_CONDITIONAL([ENABLE_RDP], [test "x${have_freerdp}" = "xyes"])
-
-AC_SUBST(RDP_LIBS)
-AC_SUBST(WINPR_LIBS)
+AM_CONDITIONAL([ENABLE_RDP], [test "x${have_freerdp2}" = "xyes"])
#
# libssh2
@@ -1426,7 +1032,7 @@
Library status:
- freerdp ............. ${have_freerdp}
+ freerdp2 ............ ${have_freerdp2}
pango ............... ${have_pango}
libavcodec .......... ${have_libavcodec}
libavutil ........... ${have_libavutil}
diff --git a/src/common/surface.c b/src/common/surface.c
index c31dfbf..c86ca80 100644
--- a/src/common/surface.c
+++ b/src/common/surface.c
@@ -260,18 +260,31 @@
/**
* Returns whether the given rectangle should be combined into the existing
- * dirty rectangle, to be eventually flushed as a "png" instruction.
+ * dirty rectangle, to be eventually flushed as image data, or would be best
+ * kept independent of the current rectangle.
*
- * @param surface The surface to be queried.
- * @param rect The update rectangle.
- * @param rect_only Non-zero if this update, by its nature, contains only
- * metainformation about the update's rectangle, zero if
- * the update also contains image data.
- * @return Non-zero if the update should be combined with any existing update,
- * zero otherwise.
+ * @param surface
+ * The surface being updated.
+ *
+ * @param rect
+ * The bounding rectangle of the update being made to the surface.
+ *
+ * @param rect_only
+ * Non-zero if this update, by its nature, contains only metainformation
+ * about the update's bounding rectangle, zero if the update also contains
+ * image data.
+ *
+ * @return
+ * Non-zero if the update should be combined with any existing update, zero
+ * otherwise.
*/
static int __guac_common_should_combine(guac_common_surface* surface, const guac_common_rect* rect, int rect_only) {
+ /* Always favor combining updates if surface is currently a purely
+ * server-side scratch area */
+ if (!surface->realized)
+ return 1;
+
if (surface->dirty) {
int combined_cost, dirty_cost, update_cost;
diff --git a/src/guacd-docker/bin/link-freerdp-plugins.sh b/src/guacd-docker/bin/link-freerdp-plugins.sh
index 332d4c0..625e85b 100755
--- a/src/guacd-docker/bin/link-freerdp-plugins.sh
+++ b/src/guacd-docker/bin/link-freerdp-plugins.sh
@@ -70,7 +70,7 @@
# Determine correct install location for FreeRDP plugins
FREERDP_DIR="$(where_is_freerdp "$1")"
- FREERDP_PLUGIN_DIR="${FREERDP_DIR}/freerdp"
+ FREERDP_PLUGIN_DIR="${FREERDP_DIR}/freerdp2"
# Add symbolic link if necessary
if [ ! -e "$FREERDP_PLUGIN_DIR/$(basename "$1")" ]; then
diff --git a/src/protocols/rdp/.gitignore b/src/protocols/rdp/.gitignore
index 9f87ecb..5cb764a 100644
--- a/src/protocols/rdp/.gitignore
+++ b/src/protocols/rdp/.gitignore
@@ -4,5 +4,6 @@
test_rdp
# Autogenerated sources
+_generated_channel_entry_wrappers.c
_generated_keymaps.c
diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am
index 97b551d..70f7f58 100644
--- a/src/protocols/rdp/Makefile.am
+++ b/src/protocols/rdp/Makefile.am
@@ -29,218 +29,172 @@
lib_LTLIBRARIES = libguac-client-rdp.la
SUBDIRS = . tests
-nodist_libguac_client_rdp_la_SOURCES = \
- _generated_keymaps.c
-
-libguac_client_rdp_la_SOURCES = \
- audio_input.c \
- client.c \
- decompose.c \
- dvc.c \
- error.c \
- input.c \
- keyboard.c \
- ptr_string.c \
- rdp.c \
- rdp_bitmap.c \
- rdp_cliprdr.c \
- rdp_color.c \
- rdp_disp.c \
- rdp_fs.c \
- rdp_gdi.c \
- rdp_glyph.c \
- rdp_keymap.c \
- rdp_print_job.c \
- rdp_pointer.c \
- rdp_rail.c \
- rdp_settings.c \
- rdp_stream.c \
- rdp_svc.c \
- resolution.c \
- unicode.c \
- user.c
-
-guacai_sources = \
- audio_input.c \
- guac_ai/ai_messages.c \
- guac_ai/ai_service.c \
- ptr_string.c
-
-guacsvc_sources = \
- guac_svc/svc_service.c \
- rdp_svc.c
-
-guacsnd_sources = \
- guac_rdpsnd/rdpsnd_messages.c \
- guac_rdpsnd/rdpsnd_service.c
-
-guacdr_sources = \
- guac_rdpdr/rdpdr_fs_messages.c \
- guac_rdpdr/rdpdr_fs_messages_dir_info.c \
- guac_rdpdr/rdpdr_fs_messages_file_info.c \
- guac_rdpdr/rdpdr_fs_messages_vol_info.c \
- guac_rdpdr/rdpdr_fs_service.c \
- guac_rdpdr/rdpdr_messages.c \
- guac_rdpdr/rdpdr_printer.c \
- guac_rdpdr/rdpdr_service.c \
- rdp_fs.c \
- rdp_print_job.c \
- rdp_stream.c \
- unicode.c
-
-noinst_HEADERS = \
- compat/client-cliprdr.h \
- compat/rail.h \
- guac_ai/ai_messages.h \
- guac_ai/ai_service.h \
- guac_rdpdr/rdpdr_fs_messages.h \
- guac_rdpdr/rdpdr_fs_messages_dir_info.h \
- guac_rdpdr/rdpdr_fs_messages_file_info.h \
- guac_rdpdr/rdpdr_fs_messages_vol_info.h \
- guac_rdpdr/rdpdr_fs_service.h \
- guac_rdpdr/rdpdr_messages.h \
- guac_rdpdr/rdpdr_printer.h \
- guac_rdpdr/rdpdr_service.h \
- guac_rdpsnd/rdpsnd_messages.h \
- guac_rdpsnd/rdpsnd_service.h \
- guac_svc/svc_service.h \
- audio_input.h \
- client.h \
- decompose.h \
- dvc.h \
- error.h \
- input.h \
- keyboard.h \
- ptr_string.h \
- rdp.h \
- rdp_bitmap.h \
- rdp_cliprdr.h \
- rdp_color.h \
- rdp_disp.h \
- rdp_fs.h \
- rdp_gdi.h \
- rdp_glyph.h \
- rdp_keymap.h \
- rdp_pointer.h \
- rdp_print_job.h \
- rdp_rail.h \
- rdp_settings.h \
- rdp_status.h \
- rdp_stream.h \
- rdp_svc.h \
- resolution.h \
- unicode.h \
- user.h
-
-# Add compatibility layer for WinPR if not available
-if ! ENABLE_WINPR
-noinst_HEADERS += compat/winpr-stream.h compat/winpr-wtypes.h
-libguac_client_rdp_la_SOURCES += compat/winpr-stream.c
-guacai_sources += compat/winpr-stream.c
-guacsvc_sources += compat/winpr-stream.c
-guacsnd_sources += compat/winpr-stream.c
-guacdr_sources += compat/winpr-stream.c
-endif
-
#
# Main RDP client library
#
+nodist_libguac_client_rdp_la_SOURCES = \
+ _generated_channel_entry_wrappers.c \
+ _generated_keymaps.c
+
+libguac_client_rdp_la_SOURCES = \
+ bitmap.c \
+ channels/audio-input/audio-buffer.c \
+ channels/audio-input/audio-input.c \
+ channels/cliprdr.c \
+ channels/common-svc.c \
+ channels/disp.c \
+ channels/pipe-svc.c \
+ channels/rail.c \
+ channels/rdpdr/rdpdr-fs-messages-dir-info.c \
+ channels/rdpdr/rdpdr-fs-messages-file-info.c \
+ channels/rdpdr/rdpdr-fs-messages-vol-info.c \
+ channels/rdpdr/rdpdr-fs-messages.c \
+ channels/rdpdr/rdpdr-fs.c \
+ channels/rdpdr/rdpdr-messages.c \
+ channels/rdpdr/rdpdr-printer.c \
+ channels/rdpdr/rdpdr.c \
+ channels/rdpsnd/rdpsnd-messages.c \
+ channels/rdpsnd/rdpsnd.c \
+ client.c \
+ color.c \
+ decompose.c \
+ download.c \
+ error.c \
+ fs.c \
+ gdi.c \
+ glyph.c \
+ input.c \
+ keyboard.c \
+ keymap.c \
+ log.c \
+ ls.c \
+ plugins/channels.c \
+ plugins/ptr-string.c \
+ pointer.c \
+ print-job.c \
+ rdp.c \
+ resolution.c \
+ settings.c \
+ unicode.c \
+ upload.c \
+ user.c
+
+noinst_HEADERS = \
+ bitmap.h \
+ channels/audio-input/audio-buffer.h \
+ channels/audio-input/audio-input.h \
+ channels/cliprdr.h \
+ channels/common-svc.h \
+ channels/disp.h \
+ channels/pipe-svc.h \
+ channels/rail.h \
+ channels/rdpdr/rdpdr-fs-messages-dir-info.h \
+ channels/rdpdr/rdpdr-fs-messages-file-info.h \
+ channels/rdpdr/rdpdr-fs-messages-vol-info.h \
+ channels/rdpdr/rdpdr-fs-messages.h \
+ channels/rdpdr/rdpdr-fs.h \
+ channels/rdpdr/rdpdr-messages.h \
+ channels/rdpdr/rdpdr-printer.h \
+ channels/rdpdr/rdpdr.h \
+ channels/rdpsnd/rdpsnd-messages.h \
+ channels/rdpsnd/rdpsnd.h \
+ client.h \
+ color.h \
+ decompose.h \
+ download.h \
+ error.h \
+ fs.h \
+ gdi.h \
+ glyph.h \
+ input.h \
+ keyboard.h \
+ keymap.h \
+ log.h \
+ ls.h \
+ plugins/channels.h \
+ plugins/guacai/guacai-messages.h \
+ plugins/guacai/guacai.h \
+ plugins/ptr-string.h \
+ pointer.h \
+ print-job.h \
+ rdp.h \
+ resolution.h \
+ settings.h \
+ unicode.h \
+ upload.h \
+ user.h
+
libguac_client_rdp_la_CFLAGS = \
-Werror -Wall -Iinclude \
@COMMON_INCLUDE@ \
@COMMON_SSH_INCLUDE@ \
- @LIBGUAC_INCLUDE@
+ @LIBGUAC_INCLUDE@ \
+ @RDP_CFLAGS@
libguac_client_rdp_la_LDFLAGS = \
-version-info 0:0:0 \
@CAIRO_LIBS@ \
@PTHREAD_LIBS@ \
- @RDP_LIBS@ \
- @WINPR_LIBS@
+ @RDP_LIBS@
-libguac_client_rdp_la_LIBADD = \
- @COMMON_LTLIB@ \
+libguac_client_rdp_la_LIBADD = \
+ @COMMON_LTLIB@ \
@LIBGUAC_LTLIB@
#
-# RDPDR
+# Plugins for FreeRDP
#
-guacdr_cflags = \
- -Werror -Wall -Iinclude \
- @COMMON_INCLUDE@ \
- @COMMON_SSH_INCLUDE@ \
- @LIBGUAC_INCLUDE@
+freerdp_LTLIBRARIES = \
+ libguac-common-svc-client.la \
+ libguacai-client.la
-guacdr_ldflags = \
- -module -avoid-version -shared \
- @PTHREAD_LIBS@ \
- @RDP_LIBS@ \
- @WINPR_LIBS@
+freerdpdir = ${libdir}/freerdp2
-guacdr_libadd = \
- @COMMON_LTLIB@ \
+#
+# Common SVC plugin (shared by RDPDR, RDPSND, etc.)
+#
+
+libguac_common_svc_client_la_SOURCES = \
+ plugins/guac-common-svc/guac-common-svc.c
+
+libguac_common_svc_client_la_CFLAGS = \
+ -Werror -Wall -Iinclude \
+ @LIBGUAC_INCLUDE@ \
+ @RDP_CFLAGS@
+
+libguac_common_svc_client_la_LDFLAGS = \
+ -module -avoid-version -shared \
+ @RDP_LIBS@
+
+libguac_common_svc_client_la_LIBADD = \
@LIBGUAC_LTLIB@
#
# Audio Input
#
-guacai_cflags = \
- -Werror -Wall -Iinclude \
- @COMMON_INCLUDE@ \
- @COMMON_SSH_INCLUDE@ \
- @LIBGUAC_INCLUDE@
+libguacai_client_la_SOURCES = \
+ channels/audio-input/audio-buffer.c \
+ plugins/guacai/guacai-messages.c \
+ plugins/guacai/guacai.c \
+ plugins/ptr-string.c
-guacai_ldflags = \
+libguacai_client_la_CFLAGS = \
+ -Werror -Wall -Iinclude \
+ @COMMON_INCLUDE@ \
+ @COMMON_SSH_INCLUDE@ \
+ @LIBGUAC_INCLUDE@ \
+ @RDP_CFLAGS@
+
+libguacai_client_la_LDFLAGS = \
-module -avoid-version -shared \
@PTHREAD_LIBS@ \
- @RDP_LIBS@ \
- @WINPR_LIBS@
+ @RDP_LIBS@
-guacai_libadd = \
- @COMMON_LTLIB@ \
- @LIBGUAC_LTLIB@
-
-#
-# RDPSND
-#
-
-guacsnd_cflags = \
- -Werror -Wall -Iinclude \
- @COMMON_INCLUDE@ \
- @COMMON_SSH_INCLUDE@ \
- @LIBGUAC_INCLUDE@
-
-guacsnd_ldflags = \
- -module -avoid-version -shared \
- @PTHREAD_LIBS@ \
- @RDP_LIBS@ \
- @WINPR_LIBS@
-
-guacsnd_libadd = \
- @COMMON_LTLIB@ \
- @LIBGUAC_LTLIB@
-
-#
-# Static Virtual Channels
-#
-
-guacsvc_cflags = \
- -Werror -Wall -Iinclude \
- @COMMON_INCLUDE@ \
- @COMMON_SSH_INCLUDE@ \
- @LIBGUAC_INCLUDE@
-
-guacsvc_ldflags = \
- -module -avoid-version -shared \
- @PTHREAD_LIBS@ \
- @RDP_LIBS@ \
- @WINPR_LIBS@
-
-guacsvc_libadd = \
- @COMMON_LTLIB@ \
+libguacai_client_la_LIBADD = \
+ @COMMON_LTLIB@ \
@LIBGUAC_LTLIB@
#
@@ -254,13 +208,18 @@
endif
#
-# Autogenerate keymaps
+# Autogenerated keymaps and channel wrapper functions
#
-CLEANFILES = _generated_keymaps.c
-BUILT_SOURCES = _generated_keymaps.c
+CLEANFILES = \
+ _generated_channel_entry_wrappers.c \
+ _generated_keymaps.c
-rdp_keymaps = \
+BUILT_SOURCES = \
+ _generated_channel_entry_wrappers.c \
+ _generated_keymaps.c
+
+rdp_keymaps = \
$(srcdir)/keymaps/base.keymap \
$(srcdir)/keymaps/failsafe.keymap \
$(srcdir)/keymaps/de_de_qwertz.keymap \
@@ -280,71 +239,13 @@
$(srcdir)/keymaps/tr_tr_qwerty.keymap
_generated_keymaps.c: $(rdp_keymaps)
- $(srcdir)/keymaps/generate.pl $(rdp_keymaps)
+ $(AM_V_GEN) $(srcdir)/keymaps/generate.pl $(rdp_keymaps)
-EXTRA_DIST = \
- $(rdp_keymaps) \
- keymaps/generate.pl
+_generated_channel_entry_wrappers.c: $(srcdir)/plugins/channels.h $(srcdir)/plugins/generate-entry-wrappers.pl
+ $(AM_V_GEN) $(srcdir)/plugins/generate-entry-wrappers.pl $(srcdir)/plugins/channels.h
-if LEGACY_FREERDP_EXTENSIONS
-
-# FreeRDP 1.0-style extensions
-freerdp_LTLIBRARIES = \
- guacai.la \
- guacdr.la \
- guacsnd.la \
- guacsvc.la
-
-guacai_la_SOURCES = ${guacai_sources}
-guacai_la_CFLAGS = ${guacai_cflags}
-guacai_la_LDFLAGS = ${guacai_ldflags}
-guacai_la_LIBADD = ${guacai_libadd}
-
-guacdr_la_SOURCES = ${guacdr_sources}
-guacdr_la_CFLAGS = ${guacdr_cflags}
-guacdr_la_LDFLAGS = ${guacdr_ldflags}
-guacdr_la_LIBADD = ${guacdr_libadd}
-
-guacsnd_la_SOURCES = ${guacsnd_sources}
-guacsnd_la_CFLAGS = ${guacsnd_cflags}
-guacsnd_la_LDFLAGS = ${guacsnd_ldflags}
-guacsnd_la_LIBADD = ${guacsnd_libadd}
-
-guacsvc_la_SOURCES = ${guacsvc_sources}
-guacsvc_la_CFLAGS = ${guacsvc_cflags}
-guacsvc_la_LDFLAGS = ${guacsvc_ldflags}
-guacsvc_la_LIBADD = ${guacsvc_libadd}
-
-else
-
-# FreeRDP 1.1 (and hopefully onward) extensions
-freerdp_LTLIBRARIES = \
- guacai-client.la \
- guacdr-client.la \
- guacsnd-client.la \
- guacsvc-client.la
-
-guacai_client_la_SOURCES = ${guacai_sources}
-guacai_client_la_CFLAGS = ${guacai_cflags}
-guacai_client_la_LDFLAGS = ${guacai_ldflags}
-guacai_client_la_LIBADD = ${guacai_libadd}
-
-guacdr_client_la_SOURCES = ${guacdr_sources}
-guacdr_client_la_CFLAGS = ${guacdr_cflags}
-guacdr_client_la_LDFLAGS = ${guacdr_ldflags}
-guacdr_client_la_LIBADD = ${guacdr_libadd}
-
-guacsnd_client_la_SOURCES = ${guacsnd_sources}
-guacsnd_client_la_CFLAGS = ${guacsnd_cflags}
-guacsnd_client_la_LDFLAGS = ${guacsnd_ldflags}
-guacsnd_client_la_LIBADD = ${guacsnd_libadd}
-
-guacsvc_client_la_SOURCES = ${guacsvc_sources}
-guacsvc_client_la_CFLAGS = ${guacsvc_cflags}
-guacsvc_client_la_LDFLAGS = ${guacsvc_ldflags}
-guacsvc_client_la_LIBADD = ${guacsvc_libadd}
-
-endif
-
-freerdpdir = ${libdir}/freerdp
+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
new file mode 100644
index 0000000..db29316
--- /dev/null
+++ b/src/protocols/rdp/bitmap.c
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "bitmap.h"
+#include "common/display.h"
+#include "common/surface.h"
+#include "rdp.h"
+
+#include <cairo/cairo.h>
+#include <freerdp/freerdp.h>
+#include <guacamole/client.h>
+#include <winpr/crt.h>
+#include <winpr/wtypes.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ /* Allocate buffer */
+ guac_common_display_layer* buffer = guac_common_display_alloc_buffer(
+ rdp_client->display, bitmap->width, bitmap->height);
+
+ /* Cache image data if present */
+ if (bitmap->data != NULL) {
+
+ /* Create surface from image data */
+ cairo_surface_t* image = cairo_image_surface_create_for_data(
+ bitmap->data, CAIRO_FORMAT_RGB24,
+ bitmap->width, bitmap->height, 4*bitmap->width);
+
+ /* Send surface to buffer */
+ guac_common_surface_draw(buffer->surface, 0, 0, image);
+
+ /* Free surface */
+ cairo_surface_destroy(image);
+
+ }
+
+ /* Store buffer reference in bitmap */
+ ((guac_rdp_bitmap*) bitmap)->layer = buffer;
+
+}
+
+BOOL guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
+
+ /* No corresponding surface yet - caching is deferred. */
+ ((guac_rdp_bitmap*) bitmap)->layer = NULL;
+
+ /* Start at zero usage */
+ ((guac_rdp_bitmap*) bitmap)->used = 0;
+
+ return TRUE;
+
+}
+
+BOOL guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ guac_common_display_layer* buffer = ((guac_rdp_bitmap*) bitmap)->layer;
+
+ int width = bitmap->right - bitmap->left + 1;
+ int height = bitmap->bottom - bitmap->top + 1;
+
+ /* If not cached, cache if necessary */
+ if (buffer == NULL && ((guac_rdp_bitmap*) bitmap)->used >= 1)
+ guac_rdp_cache_bitmap(context, bitmap);
+
+ /* If cached, retrieve from cache */
+ if (buffer != NULL)
+ guac_common_surface_copy(buffer->surface, 0, 0, width, height,
+ rdp_client->display->default_surface,
+ bitmap->left, bitmap->top);
+
+ /* Otherwise, draw with stored image data */
+ else if (bitmap->data != NULL) {
+
+ /* Create surface from image data */
+ cairo_surface_t* image = cairo_image_surface_create_for_data(
+ bitmap->data, CAIRO_FORMAT_RGB24,
+ width, height, 4*bitmap->width);
+
+ /* Draw image on default surface */
+ guac_common_surface_draw(rdp_client->display->default_surface,
+ bitmap->left, bitmap->top, image);
+
+ /* Free surface */
+ cairo_surface_destroy(image);
+
+ }
+
+ /* Increment usage counter */
+ ((guac_rdp_bitmap*) bitmap)->used++;
+
+ return TRUE;
+
+}
+
+void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_common_display_layer* buffer = ((guac_rdp_bitmap*) bitmap)->layer;
+
+ /* If cached, free buffer */
+ if (buffer != NULL)
+ guac_common_display_free_buffer(rdp_client->display, buffer);
+
+ /* NOTE: 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);
+ free(bitmap);
+
+}
+
+BOOL guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ if (primary)
+ rdp_client->current_surface = rdp_client->display->default_surface;
+
+ else {
+
+ /* Make sure that the recieved bitmap is not NULL before processing */
+ if (bitmap == NULL) {
+ guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in bitmap_setsurface instruction.");
+ return TRUE;
+ }
+
+ /* If not available as a surface, make available. */
+ if (((guac_rdp_bitmap*) bitmap)->layer == NULL)
+ guac_rdp_cache_bitmap(context, bitmap);
+
+ rdp_client->current_surface =
+ ((guac_rdp_bitmap*) bitmap)->layer->surface;
+
+ }
+
+ return TRUE;
+
+}
+
diff --git a/src/protocols/rdp/bitmap.h b/src/protocols/rdp/bitmap.h
new file mode 100644
index 0000000..297230c
--- /dev/null
+++ b/src/protocols/rdp/bitmap.h
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_BITMAP_H
+#define GUAC_RDP_BITMAP_H
+
+#include "config.h"
+#include "common/display.h"
+
+#include <freerdp/freerdp.h>
+#include <freerdp/graphics.h>
+#include <guacamole/layer.h>
+#include <winpr/wtypes.h>
+
+/**
+ * Guacamole-specific rdpBitmap data.
+ */
+typedef struct guac_rdp_bitmap {
+
+ /**
+ * FreeRDP bitmap data - MUST GO FIRST.
+ */
+ rdpBitmap bitmap;
+
+ /**
+ * Layer containing cached image data.
+ */
+ guac_common_display_layer* layer;
+
+ /**
+ * The number of times a bitmap has been used.
+ */
+ int used;
+
+} guac_rdp_bitmap;
+
+/**
+ * Caches the given bitmap immediately, storing its data in a remote Guacamole
+ * buffer. As RDP bitmaps are frequently created, used once, and immediately
+ * destroyed, we defer actual remote-side caching of RDP bitmaps until they are
+ * used at least once.
+ *
+ * @param context
+ * The rdpContext associated with the current RDP session.
+ *
+ * @param bitmap
+ * The bitmap to cache.
+ */
+void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap);
+
+/**
+ * Initializes the given newly-created rdpBitmap.
+ *
+ * @param context
+ * The rdpContext associated with the current RDP session.
+ *
+ * @param bitmap
+ * The bitmap to initialize.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
+ */
+BOOL guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap);
+
+/**
+ * Paints the given rdpBitmap on the primary display surface. Note that this
+ * operation does NOT draw to the "current" surface set by calls to
+ * guac_rdp_bitmap_setsurface().
+ *
+ * @param context
+ * The rdpContext associated with the current RDP session.
+ *
+ * @param bitmap
+ * The bitmap to paint. This structure will also contain the specifics of
+ * the paint operation to perform, including the destination X/Y
+ * coordinates.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
+ */
+BOOL guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap);
+
+/**
+ * Frees any Guacamole-specific data associated with the given rdpBitmap.
+ *
+ * @param context
+ * The rdpContext associated with the current RDP session.
+ *
+ * @param bitmap
+ * The bitmap whose Guacamole-specific data is to be freed.
+ */
+void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap);
+
+/**
+ * Sets the given rdpBitmap as the drawing surface for future operations or,
+ * if the primary flag is set, resets the current drawing surface to the
+ * primary drawing surface of the remote display.
+ *
+ * @param context
+ * The rdpContext associated with the current RDP session.
+ *
+ * @param bitmap
+ * The rdpBitmap to set as the current drawing surface. This parameter is
+ * only valid if the primary flag is FALSE.
+ *
+ * @param primary
+ * TRUE if the bitmap parameter should be ignored, and the current drawing
+ * surface should be reset to the primary drawing surface of the remote
+ * display, FALSE otherwise.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
+ */
+BOOL guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap,
+ BOOL primary);
+
+#endif
diff --git a/src/protocols/rdp/audio_input.c b/src/protocols/rdp/channels/audio-input/audio-buffer.c
similarity index 69%
rename from src/protocols/rdp/audio_input.c
rename to src/protocols/rdp/channels/audio-input/audio-buffer.c
index 014ea2d..3051341 100644
--- a/src/protocols/rdp/audio_input.c
+++ b/src/protocols/rdp/channels/audio-input/audio-buffer.c
@@ -17,181 +17,19 @@
* under the License.
*/
-#include "config.h"
-#include "audio_input.h"
-#include "dvc.h"
-#include "ptr_string.h"
+#include "channels/audio-input/audio-buffer.h"
#include "rdp.h"
-#include <freerdp/freerdp.h>
-#include <freerdp/channels/channels.h>
+#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/stream.h>
#include <guacamole/user.h>
#include <assert.h>
-#include <errno.h>
-#include <stdlib.h>
#include <pthread.h>
-
-/**
- * Parses the given raw audio mimetype, producing the corresponding rate,
- * number of channels, and bytes per sample.
- *
- * @param mimetype
- * The raw auduio mimetype to parse.
- *
- * @param rate
- * A pointer to an int where the sample rate for the PCM format described
- * by the given mimetype should be stored.
- *
- * @param channels
- * A pointer to an int where the number of channels used by the PCM format
- * described by the given mimetype should be stored.
- *
- * @param bps
- * A pointer to an int where the number of bytes used the PCM format for
- * each sample (independent of number of channels) described by the given
- * mimetype should be stored.
- *
- * @return
- * Zero if the given mimetype is a raw audio mimetype and has been parsed
- * successfully, non-zero otherwise.
- */
-static int guac_rdp_audio_parse_mimetype(const char* mimetype,
- int* rate, int* channels, int* bps) {
-
- int parsed_rate = -1;
- int parsed_channels = 1;
- int parsed_bps;
-
- /* PCM audio with one byte per sample */
- if (strncmp(mimetype, "audio/L8;", 9) == 0) {
- mimetype += 8; /* Advance to semicolon ONLY */
- parsed_bps = 1;
- }
-
- /* PCM audio with two bytes per sample */
- else if (strncmp(mimetype, "audio/L16;", 10) == 0) {
- mimetype += 9; /* Advance to semicolon ONLY */
- parsed_bps = 2;
- }
-
- /* Unsupported mimetype */
- else
- return 1;
-
- /* Parse each parameter name/value pair within the mimetype */
- do {
-
- /* Advance to first character of parameter (current is either a
- * semicolon or a comma) */
- mimetype++;
-
- /* Parse number of channels */
- if (strncmp(mimetype, "channels=", 9) == 0) {
-
- mimetype += 9;
- parsed_channels = strtol(mimetype, (char**) &mimetype, 10);
-
- /* Fail if value invalid / out of range */
- if (errno == EINVAL || errno == ERANGE)
- return 1;
-
- }
-
- /* Parse number of rate */
- else if (strncmp(mimetype, "rate=", 5) == 0) {
-
- mimetype += 5;
- parsed_rate = strtol(mimetype, (char**) &mimetype, 10);
-
- /* Fail if value invalid / out of range */
- if (errno == EINVAL || errno == ERANGE)
- return 1;
-
- }
-
- /* Advance to next parameter */
- mimetype = strchr(mimetype, ',');
-
- } while (mimetype != NULL);
-
- /* Mimetype is invalid if rate was not specified */
- if (parsed_rate == -1)
- return 1;
-
- /* Parse success */
- *rate = parsed_rate;
- *channels = parsed_channels;
- *bps = parsed_bps;
-
- return 0;
-
-}
-
-int guac_rdp_audio_handler(guac_user* user, guac_stream* stream,
- char* mimetype) {
-
- guac_client* client = user->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
-
- int rate;
- int channels;
- int bps;
-
- /* Parse mimetype, abort on parse error */
- if (guac_rdp_audio_parse_mimetype(mimetype, &rate, &channels, &bps)) {
- guac_user_log(user, GUAC_LOG_WARNING, "Denying user audio stream with "
- "unsupported mimetype: \"%s\"", mimetype);
- guac_protocol_send_ack(user->socket, stream, "Unsupported audio "
- "mimetype", GUAC_PROTOCOL_STATUS_CLIENT_BAD_TYPE);
- return 0;
- }
-
- /* Init stream data */
- stream->blob_handler = guac_rdp_audio_blob_handler;
- stream->end_handler = guac_rdp_audio_end_handler;
-
- /* Associate stream with audio buffer */
- guac_rdp_audio_buffer_set_stream(rdp_client->audio_input, user, stream,
- rate, channels, bps);
-
- return 0;
-
-}
-
-int guac_rdp_audio_blob_handler(guac_user* user, guac_stream* stream,
- void* data, int length) {
-
- guac_client* client = user->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
-
- /* Write blob to audio stream, buffering if necessary */
- guac_rdp_audio_buffer_write(rdp_client->audio_input, data, length);
-
- return 0;
-
-}
-
-int guac_rdp_audio_end_handler(guac_user* user, guac_stream* stream) {
-
- /* Ignore - the AUDIO_INPUT channel will simply not receive anything */
- return 0;
-
-}
-
-void guac_rdp_audio_load_plugin(rdpContext* context, guac_rdp_dvc_list* list) {
-
- guac_client* client = ((rdp_freerdp_context*) context)->client;
- char client_ref[GUAC_RDP_PTR_STRING_LENGTH];
-
- /* Add "AUDIO_INPUT" channel */
- guac_rdp_ptr_to_string(client, client_ref);
- guac_rdp_dvc_list_add(list, "guacai", client_ref, NULL);
-
-}
+#include <stdint.h>
+#include <stdlib.h>
guac_rdp_audio_buffer* guac_rdp_audio_buffer_alloc() {
guac_rdp_audio_buffer* buffer = calloc(1, sizeof(guac_rdp_audio_buffer));
diff --git a/src/protocols/rdp/audio_input.h b/src/protocols/rdp/channels/audio-input/audio-buffer.h
similarity index 87%
rename from src/protocols/rdp/audio_input.h
rename to src/protocols/rdp/channels/audio-input/audio-buffer.h
index 6280662..32f7def 100644
--- a/src/protocols/rdp/audio_input.h
+++ b/src/protocols/rdp/channels/audio-input/audio-buffer.h
@@ -17,16 +17,11 @@
* under the License.
*/
-#ifndef GUAC_RDP_AUDIO_INPUT_H
-#define GUAC_RDP_AUDIO_INPUT_H
+#ifndef GUAC_RDP_CHANNELS_AUDIO_INPUT_AUDIO_BUFFER_H
+#define GUAC_RDP_CHANNELS_AUDIO_INPUT_AUDIO_BUFFER_H
-#include "config.h"
-#include "dvc.h"
-
-#include <freerdp/freerdp.h>
#include <guacamole/stream.h>
#include <guacamole/user.h>
-
#include <pthread.h>
/**
@@ -278,36 +273,5 @@
*/
void guac_rdp_audio_buffer_free(guac_rdp_audio_buffer* audio_buffer);
-/**
- * Handler for inbound audio data (audio input).
- */
-guac_user_audio_handler guac_rdp_audio_handler;
-
-/**
- * Handler for stream data related to audio input.
- */
-guac_user_blob_handler guac_rdp_audio_blob_handler;
-
-/**
- * Handler for end-of-stream related to audio input.
- */
-guac_user_end_handler guac_rdp_audio_end_handler;
-
-/**
- * Adds Guacamole's "guacai" plugin to the list of dynamic virtual channel
- * plugins to be loaded by FreeRDP's "drdynvc" plugin. The plugin will only
- * be loaded once guac_rdp_load_drdynvc() is invoked with the guac_rdp_dvc_list
- * passed to this function. The "guacai" plugin ultimately adds support for the
- * "AUDIO_INPUT" dynamic virtual channel.
- *
- * @param context
- * The rdpContext associated with the active RDP session.
- *
- * @param list
- * The guac_rdp_dvc_list to which the "guacai" plugin should be added, such
- * that it may later be loaded by guac_rdp_load_drdynvc().
- */
-void guac_rdp_audio_load_plugin(rdpContext* context, guac_rdp_dvc_list* list);
-
#endif
diff --git a/src/protocols/rdp/channels/audio-input/audio-input.c b/src/protocols/rdp/channels/audio-input/audio-input.c
new file mode 100644
index 0000000..51fa118
--- /dev/null
+++ b/src/protocols/rdp/channels/audio-input/audio-input.c
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "channels/audio-input/audio-buffer.h"
+#include "channels/audio-input/audio-input.h"
+#include "plugins/channels.h"
+#include "plugins/ptr-string.h"
+#include "rdp.h"
+
+#include <freerdp/freerdp.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/stream.h>
+#include <guacamole/user.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Parses the given raw audio mimetype, producing the corresponding rate,
+ * number of channels, and bytes per sample.
+ *
+ * @param mimetype
+ * The raw auduio mimetype to parse.
+ *
+ * @param rate
+ * A pointer to an int where the sample rate for the PCM format described
+ * by the given mimetype should be stored.
+ *
+ * @param channels
+ * A pointer to an int where the number of channels used by the PCM format
+ * described by the given mimetype should be stored.
+ *
+ * @param bps
+ * A pointer to an int where the number of bytes used the PCM format for
+ * each sample (independent of number of channels) described by the given
+ * mimetype should be stored.
+ *
+ * @return
+ * Zero if the given mimetype is a raw audio mimetype and has been parsed
+ * successfully, non-zero otherwise.
+ */
+static int guac_rdp_audio_parse_mimetype(const char* mimetype,
+ int* rate, int* channels, int* bps) {
+
+ int parsed_rate = -1;
+ int parsed_channels = 1;
+ int parsed_bps;
+
+ /* PCM audio with one byte per sample */
+ if (strncmp(mimetype, "audio/L8;", 9) == 0) {
+ mimetype += 8; /* Advance to semicolon ONLY */
+ parsed_bps = 1;
+ }
+
+ /* PCM audio with two bytes per sample */
+ else if (strncmp(mimetype, "audio/L16;", 10) == 0) {
+ mimetype += 9; /* Advance to semicolon ONLY */
+ parsed_bps = 2;
+ }
+
+ /* Unsupported mimetype */
+ else
+ return 1;
+
+ /* Parse each parameter name/value pair within the mimetype */
+ do {
+
+ /* Advance to first character of parameter (current is either a
+ * semicolon or a comma) */
+ mimetype++;
+
+ /* Parse number of channels */
+ if (strncmp(mimetype, "channels=", 9) == 0) {
+
+ mimetype += 9;
+ parsed_channels = strtol(mimetype, (char**) &mimetype, 10);
+
+ /* Fail if value invalid / out of range */
+ if (errno == EINVAL || errno == ERANGE)
+ return 1;
+
+ }
+
+ /* Parse number of rate */
+ else if (strncmp(mimetype, "rate=", 5) == 0) {
+
+ mimetype += 5;
+ parsed_rate = strtol(mimetype, (char**) &mimetype, 10);
+
+ /* Fail if value invalid / out of range */
+ if (errno == EINVAL || errno == ERANGE)
+ return 1;
+
+ }
+
+ /* Advance to next parameter */
+ mimetype = strchr(mimetype, ',');
+
+ } while (mimetype != NULL);
+
+ /* Mimetype is invalid if rate was not specified */
+ if (parsed_rate == -1)
+ return 1;
+
+ /* Parse success */
+ *rate = parsed_rate;
+ *channels = parsed_channels;
+ *bps = parsed_bps;
+
+ return 0;
+
+}
+
+int guac_rdp_audio_handler(guac_user* user, guac_stream* stream,
+ char* mimetype) {
+
+ guac_client* client = user->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ int rate;
+ int channels;
+ int bps;
+
+ /* Parse mimetype, abort on parse error */
+ if (guac_rdp_audio_parse_mimetype(mimetype, &rate, &channels, &bps)) {
+ guac_user_log(user, GUAC_LOG_WARNING, "Denying user audio stream with "
+ "unsupported mimetype: \"%s\"", mimetype);
+ guac_protocol_send_ack(user->socket, stream, "Unsupported audio "
+ "mimetype", GUAC_PROTOCOL_STATUS_CLIENT_BAD_TYPE);
+ return 0;
+ }
+
+ /* Init stream data */
+ stream->blob_handler = guac_rdp_audio_blob_handler;
+ stream->end_handler = guac_rdp_audio_end_handler;
+
+ /* Associate stream with audio buffer */
+ guac_rdp_audio_buffer_set_stream(rdp_client->audio_input, user, stream,
+ rate, channels, bps);
+
+ return 0;
+
+}
+
+int guac_rdp_audio_blob_handler(guac_user* user, guac_stream* stream,
+ void* data, int length) {
+
+ guac_client* client = user->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ /* Write blob to audio stream, buffering if necessary */
+ guac_rdp_audio_buffer_write(rdp_client->audio_input, data, length);
+
+ return 0;
+
+}
+
+int guac_rdp_audio_end_handler(guac_user* user, guac_stream* stream) {
+
+ /* Ignore - the AUDIO_INPUT channel will simply not receive anything */
+ return 0;
+
+}
+
+void guac_rdp_audio_load_plugin(rdpContext* context) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+ char client_ref[GUAC_RDP_PTR_STRING_LENGTH];
+
+ /* Add "AUDIO_INPUT" channel */
+ guac_rdp_ptr_to_string(client, client_ref);
+ guac_freerdp_dynamic_channel_collection_add(context->settings, "guacai", client_ref, NULL);
+
+}
+
diff --git a/src/protocols/rdp/channels/audio-input/audio-input.h b/src/protocols/rdp/channels/audio-input/audio-input.h
new file mode 100644
index 0000000..a3b705f
--- /dev/null
+++ b/src/protocols/rdp/channels/audio-input/audio-input.h
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_CHANNELS_AUDIO_INPUT_H
+#define GUAC_RDP_CHANNELS_AUDIO_INPUT_H
+
+#include <freerdp/freerdp.h>
+#include <guacamole/user.h>
+
+/**
+ * Handler for inbound audio data (audio input).
+ */
+guac_user_audio_handler guac_rdp_audio_handler;
+
+/**
+ * Handler for stream data related to audio input.
+ */
+guac_user_blob_handler guac_rdp_audio_blob_handler;
+
+/**
+ * Handler for end-of-stream related to audio input.
+ */
+guac_user_end_handler guac_rdp_audio_end_handler;
+
+/**
+ * Adds Guacamole's "guacai" plugin to the list of dynamic virtual channel
+ * plugins to be loaded by FreeRDP's "drdynvc" plugin. The plugin will only
+ * be loaded once the "drdynvc" plugin is loaded. The "guacai" plugin
+ * ultimately adds support for the "AUDIO_INPUT" dynamic virtual channel.
+ *
+ * @param context
+ * The rdpContext associated with the active RDP session.
+ */
+void guac_rdp_audio_load_plugin(rdpContext* context);
+
+#endif
+
diff --git a/src/protocols/rdp/channels/cliprdr.c b/src/protocols/rdp/channels/cliprdr.c
new file mode 100644
index 0000000..6911c19
--- /dev/null
+++ b/src/protocols/rdp/channels/cliprdr.c
@@ -0,0 +1,620 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "channels/cliprdr.h"
+#include "client.h"
+#include "common/clipboard.h"
+#include "common/iconv.h"
+#include "config.h"
+#include "plugins/channels.h"
+#include "rdp.h"
+
+#include <freerdp/client/cliprdr.h>
+#include <freerdp/event.h>
+#include <freerdp/freerdp.h>
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+#include <guacamole/user.h>
+#include <winpr/wtsapi.h>
+#include <winpr/wtypes.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef FREERDP_CLIPRDR_CALLBACKS_REQUIRE_CONST
+/**
+ * FreeRDP 2.0.0-rc4 and newer requires the final argument for all CLIPRDR
+ * callbacks to be const.
+ */
+#define CLIPRDR_CONST const
+#else
+/**
+ * FreeRDP 2.0.0-rc3 and older requires the final argument for all CLIPRDR
+ * callbacks to NOT be const.
+ */
+#define CLIPRDR_CONST
+#endif
+
+/**
+ * Sends a Format List PDU to the RDP server containing the formats of
+ * clipboard data supported. This PDU is used both to indicate the general
+ * clipboard formats supported at the begining of an RDP session and to inform
+ * the RDP server that new clipboard data is available within the listed
+ * formats.
+ *
+ * @param cliprdr
+ * The CliprdrClientContext structure used by FreeRDP to handle the
+ * CLIPRDR channel for the current RDP session.
+ *
+ * @return
+ * CHANNEL_RC_OK (zero) if the Format List PDU was sent successfully, an
+ * error code (non-zero) otherwise.
+ */
+static UINT guac_rdp_cliprdr_send_format_list(CliprdrClientContext* cliprdr) {
+
+ /* This function is only invoked within FreeRDP-specific handlers for
+ * CLIPRDR, which are not assigned, and thus not callable, until after the
+ * relevant guac_rdp_clipboard structure is allocated and associated with
+ * the CliprdrClientContext */
+ guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
+ assert(clipboard != NULL);
+
+ /* We support CP-1252 and UTF-16 text */
+ CLIPRDR_FORMAT_LIST format_list = {
+ .formats = (CLIPRDR_FORMAT[]) {
+ { .formatId = CF_TEXT },
+ { .formatId = CF_UNICODETEXT }
+ },
+ .numFormats = 2
+ };
+
+ guac_client_log(clipboard->client, GUAC_LOG_TRACE, "CLIPRDR: Sending "
+ "format list");
+
+ return cliprdr->ClientFormatList(cliprdr, &format_list);
+
+}
+
+/**
+ * Sends a Clipboard Capabilities PDU to the RDP server describing the features
+ * of the CLIPRDR channel that are supported by the client.
+ *
+ * @param cliprdr
+ * The CliprdrClientContext structure used by FreeRDP to handle the
+ * CLIPRDR channel for the current RDP session.
+ *
+ * @return
+ * CHANNEL_RC_OK (zero) if the Clipboard Capabilities PDU was sent
+ * successfully, an error code (non-zero) otherwise.
+ */
+static UINT guac_rdp_cliprdr_send_capabilities(CliprdrClientContext* cliprdr) {
+
+ CLIPRDR_GENERAL_CAPABILITY_SET cap_set = {
+ .capabilitySetType = CB_CAPSTYPE_GENERAL, /* CLIPRDR specification requires that this is CB_CAPSTYPE_GENERAL, the only defined set type */
+ .capabilitySetLength = 12, /* The size of the capability set within the PDU - for CB_CAPSTYPE_GENERAL, this is ALWAYS 12 bytes */
+ .version = CB_CAPS_VERSION_2, /* The version of the CLIPRDR specification supported */
+ .generalFlags = CB_USE_LONG_FORMAT_NAMES /* Bitwise OR of all supported feature flags */
+ };
+
+ CLIPRDR_CAPABILITIES caps = {
+ .cCapabilitiesSets = 1,
+ .capabilitySets = (CLIPRDR_CAPABILITY_SET*) &cap_set
+ };
+
+ return cliprdr->ClientCapabilities(cliprdr, &caps);
+
+}
+
+/**
+ * Callback invoked by the FreeRDP CLIPRDR plugin for received Monitor Ready
+ * PDUs. The Monitor Ready PDU is sent by the RDP server only during
+ * initialization of the CLIPRDR channel. It is part of the CLIPRDR channel
+ * handshake and indicates that the RDP server's handling of clipboard
+ * redirection is ready to proceed.
+ *
+ * @param cliprdr
+ * The CliprdrClientContext structure used by FreeRDP to handle the CLIPRDR
+ * channel for the current RDP session.
+ *
+ * @param monitor_ready
+ * The CLIPRDR_MONITOR_READY structure representing the Monitor Ready PDU
+ * that was received.
+ *
+ * @return
+ * CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
+ * (non-zero) otherwise.
+ */
+static UINT guac_rdp_cliprdr_monitor_ready(CliprdrClientContext* cliprdr,
+ CLIPRDR_CONST CLIPRDR_MONITOR_READY* monitor_ready) {
+
+ /* FreeRDP-specific handlers for CLIPRDR are not assigned, and thus not
+ * callable, until after the relevant guac_rdp_clipboard structure is
+ * allocated and associated with the CliprdrClientContext */
+ guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
+ assert(clipboard != NULL);
+
+ guac_client_log(clipboard->client, GUAC_LOG_TRACE, "CLIPRDR: Received "
+ "monitor ready.");
+
+ /* Respond with capabilities ... */
+ int status = guac_rdp_cliprdr_send_capabilities(cliprdr);
+ if (status != CHANNEL_RC_OK)
+ return status;
+
+ /* ... and supported format list */
+ return guac_rdp_cliprdr_send_format_list(cliprdr);
+
+}
+
+/**
+ * Sends a Format Data Request PDU to the RDP server, requesting that available
+ * clipboard data be sent to the client in the specified format. This PDU is
+ * sent when the server indicates that clipboard data is available via a Format
+ * List PDU.
+ *
+ * @param client
+ * The guac_client associated with the current RDP session.
+ *
+ * @param format
+ * The clipboard format to request. This format must be one of the
+ * documented values used by the CLIPRDR channel for clipboard format IDs.
+ *
+ * @return
+ * CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
+ * (non-zero) otherwise.
+ */
+static UINT guac_rdp_cliprdr_send_format_data_request(
+ CliprdrClientContext* cliprdr, UINT32 format) {
+
+ /* FreeRDP-specific handlers for CLIPRDR are not assigned, and thus not
+ * callable, until after the relevant guac_rdp_clipboard structure is
+ * allocated and associated with the CliprdrClientContext */
+ guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
+ assert(clipboard != NULL);
+
+ /* Create new data request */
+ CLIPRDR_FORMAT_DATA_REQUEST data_request = {
+ .requestedFormatId = format
+ };
+
+ /* Note the format we've requested for reference later when the requested
+ * data is received via a Format Data Response PDU */
+ clipboard->requested_format = format;
+
+ guac_client_log(clipboard->client, GUAC_LOG_TRACE, "CLIPRDR: Sending "
+ "format data request.");
+
+ /* Send request */
+ return cliprdr->ClientFormatDataRequest(cliprdr, &data_request);
+
+}
+
+/**
+ * Returns whether the given Format List PDU indicates support for the given
+ * clipboard format.
+ *
+ * @param format_list
+ * The CLIPRDR_FORMAT_LIST structure representing the Format List PDU
+ * being tested.
+ *
+ * @param format_id
+ * The ID of the clipboard format to test, such as CF_TEXT or
+ * CF_UNICODETEXT.
+ *
+ * @return
+ * Non-zero if the given Format List PDU indicates support for the given
+ * clipboard format, zero otherwise.
+ */
+static int guac_rdp_cliprdr_format_supported(const CLIPRDR_FORMAT_LIST* format_list,
+ UINT format_id) {
+
+ /* Search format list for matching ID */
+ for (int i = 0; i < format_list->numFormats; i++) {
+ if (format_list->formats[i].formatId == format_id)
+ return 1;
+ }
+
+ /* If no matching ID, format is not supported */
+ return 0;
+
+}
+
+/**
+ * Callback invoked by the FreeRDP CLIPRDR plugin for received Format List
+ * PDUs. The Format List PDU is sent by the RDP server to indicate that new
+ * clipboard data has been copied and is available for retrieval in the formats
+ * listed. A client wishing to retrieve that data responds with a Format Data
+ * Request PDU.
+ *
+ * @param cliprdr
+ * The CliprdrClientContext structure used by FreeRDP to handle the CLIPRDR
+ * channel for the current RDP session.
+ *
+ * @param format_list
+ * The CLIPRDR_FORMAT_LIST structure representing the Format List PDU that
+ * was received.
+ *
+ * @return
+ * CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
+ * (non-zero) otherwise.
+ */
+static UINT guac_rdp_cliprdr_format_list(CliprdrClientContext* cliprdr,
+ CLIPRDR_CONST CLIPRDR_FORMAT_LIST* format_list) {
+
+ /* FreeRDP-specific handlers for CLIPRDR are not assigned, and thus not
+ * callable, until after the relevant guac_rdp_clipboard structure is
+ * allocated and associated with the CliprdrClientContext */
+ guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
+ assert(clipboard != NULL);
+
+ guac_client_log(clipboard->client, GUAC_LOG_TRACE, "CLIPRDR: Received "
+ "format list.");
+
+ CLIPRDR_FORMAT_LIST_RESPONSE format_list_response = {
+ .msgFlags = CB_RESPONSE_OK
+ };
+
+ /* Report successful processing of format list */
+ cliprdr->ClientFormatListResponse(cliprdr, &format_list_response);
+
+ /* Prefer Unicode (in this case, UTF-16) */
+ if (guac_rdp_cliprdr_format_supported(format_list, CF_UNICODETEXT))
+ return guac_rdp_cliprdr_send_format_data_request(cliprdr, CF_UNICODETEXT);
+
+ /* Use Windows' CP-1252 if Unicode unavailable */
+ if (guac_rdp_cliprdr_format_supported(format_list, CF_TEXT))
+ return guac_rdp_cliprdr_send_format_data_request(cliprdr, CF_TEXT);
+
+ /* Ignore any unsupported data */
+ guac_client_log(clipboard->client, GUAC_LOG_DEBUG, "Ignoring unsupported "
+ "clipboard data. Only Unicode and text clipboard formats are "
+ "currently supported.");
+
+ return CHANNEL_RC_OK;
+
+}
+
+/**
+ * Callback invoked by the FreeRDP CLIPRDR plugin for received Format Data
+ * Request PDUs. The Format Data Request PDU is sent by the RDP server when
+ * requesting that clipboard data be sent, in response to a received Format
+ * List PDU. The client is required to respond with a Format Data Response PDU
+ * containing the requested data.
+ *
+ * @param cliprdr
+ * The CliprdrClientContext structure used by FreeRDP to handle the CLIPRDR
+ * channel for the current RDP session.
+ *
+ * @param format_data_request
+ * The CLIPRDR_FORMAT_DATA_REQUEST structure representing the Format Data
+ * Request PDU that was received.
+ *
+ * @return
+ * CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
+ * (non-zero) otherwise.
+ */
+static UINT guac_rdp_cliprdr_format_data_request(CliprdrClientContext* cliprdr,
+ CLIPRDR_CONST CLIPRDR_FORMAT_DATA_REQUEST* format_data_request) {
+
+ /* FreeRDP-specific handlers for CLIPRDR are not assigned, and thus not
+ * callable, until after the relevant guac_rdp_clipboard structure is
+ * allocated and associated with the CliprdrClientContext */
+ guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
+ assert(clipboard != NULL);
+
+ guac_client_log(clipboard->client, GUAC_LOG_TRACE, "CLIPRDR: Received "
+ "format data request.");
+
+ guac_iconv_write* writer;
+ const char* input = clipboard->clipboard->buffer;
+ char* output = malloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
+
+ /* Map requested clipboard format to a guac_iconv writer */
+ switch (format_data_request->requestedFormatId) {
+
+ case CF_TEXT:
+ writer = GUAC_WRITE_CP1252;
+ break;
+
+ case CF_UNICODETEXT:
+ writer = GUAC_WRITE_UTF16;
+ break;
+
+ /* Warn if clipboard data cannot be sent as intended due to a violation
+ * of the CLIPRDR spec */
+ default:
+ guac_client_log(clipboard->client, GUAC_LOG_WARNING, "Received "
+ "clipboard data cannot be sent to the RDP server because "
+ "the RDP server has requested a clipboard format which "
+ "was not declared as available. This violates the "
+ "specification for the CLIPRDR channel.");
+ free(output);
+ return CHANNEL_RC_OK;
+
+ }
+
+ /* Send received clipboard data to the RDP server in the format
+ * requested */
+ BYTE* start = (BYTE*) output;
+ guac_iconv(GUAC_READ_UTF8, &input, clipboard->clipboard->length,
+ writer, &output, GUAC_RDP_CLIPBOARD_MAX_LENGTH);
+
+ CLIPRDR_FORMAT_DATA_RESPONSE data_response = {
+ .requestedFormatData = (BYTE*) start,
+ .dataLen = ((BYTE*) output) - start,
+ .msgFlags = CB_RESPONSE_OK
+ };
+
+ guac_client_log(clipboard->client, GUAC_LOG_TRACE, "CLIPRDR: Sending "
+ "format data response.");
+
+ return cliprdr->ClientFormatDataResponse(cliprdr, &data_response);
+
+}
+
+/**
+ * Callback invoked by the FreeRDP CLIPRDR plugin for received Format Data
+ * Response PDUs. The Format Data Response PDU is sent by the RDP server when
+ * fullfilling a request for clipboard data received via a Format Data Request
+ * PDU.
+ *
+ * @param cliprdr
+ * The CliprdrClientContext structure used by FreeRDP to handle the CLIPRDR
+ * channel for the current RDP session.
+ *
+ * @param format_data_response
+ * The CLIPRDR_FORMAT_DATA_RESPONSE structure representing the Format Data
+ * Response PDU that was received.
+ *
+ * @return
+ * CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
+ * (non-zero) otherwise.
+ */
+static UINT guac_rdp_cliprdr_format_data_response(CliprdrClientContext* cliprdr,
+ CLIPRDR_CONST CLIPRDR_FORMAT_DATA_RESPONSE* format_data_response) {
+
+ /* FreeRDP-specific handlers for CLIPRDR are not assigned, and thus not
+ * callable, until after the relevant guac_rdp_clipboard structure is
+ * allocated and associated with the CliprdrClientContext */
+ guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
+ assert(clipboard != NULL);
+
+ guac_client_log(clipboard->client, GUAC_LOG_TRACE, "CLIPRDR: Received "
+ "format data response.");
+
+ char received_data[GUAC_RDP_CLIPBOARD_MAX_LENGTH];
+
+ guac_iconv_read* reader;
+ const char* input = (char*) format_data_response->requestedFormatData;
+ char* output = received_data;
+
+ /* Find correct source encoding */
+ switch (clipboard->requested_format) {
+
+ /* Non-Unicode (Windows CP-1252) */
+ case CF_TEXT:
+ reader = GUAC_READ_CP1252;
+ break;
+
+ /* Unicode (UTF-16) */
+ case CF_UNICODETEXT:
+ reader = GUAC_READ_UTF16;
+ break;
+
+ /* If the format ID stored within the guac_rdp_clipboard structure is actually
+ * not supported here, then something has been implemented incorrectly.
+ * Either incorrect values are (somehow) being stored, or support for
+ * the format indicated by that value is incomplete and must be added
+ * here. The values which may be stored within requested_format are
+ * completely within our control. */
+ default:
+ guac_client_log(clipboard->client, GUAC_LOG_DEBUG, "Requested "
+ "clipboard data in unsupported format (0x%X).",
+ clipboard->requested_format);
+ return CHANNEL_RC_OK;
+
+ }
+
+ /* Convert, store, and forward the clipboard data received from RDP
+ * server */
+ if (guac_iconv(reader, &input, format_data_response->dataLen,
+ GUAC_WRITE_UTF8, &output, sizeof(received_data))) {
+ int length = strnlen(received_data, sizeof(received_data));
+ guac_common_clipboard_reset(clipboard->clipboard, "text/plain");
+ guac_common_clipboard_append(clipboard->clipboard, received_data, length);
+ guac_common_clipboard_send(clipboard->clipboard, clipboard->client);
+ }
+
+ return CHANNEL_RC_OK;
+
+}
+
+/**
+ * Callback which associates handlers specific to Guacamole with the
+ * CliprdrClientContext instance allocated by FreeRDP to deal with received
+ * CLIPRDR (clipboard redirection) messages.
+ *
+ * This function is called whenever a channel connects via the PubSub event
+ * system within FreeRDP, but only has any effect if the connected channel is
+ * the CLIPRDR channel. This specific callback is registered with the PubSub
+ * system of the relevant rdpContext when guac_rdp_clipboard_load_plugin() is
+ * called.
+ *
+ * @param context
+ * The rdpContext associated with the active RDP session.
+ *
+ * @param e
+ * Event-specific arguments, mainly the name of the channel, and a
+ * reference to the associated plugin loaded for that channel by FreeRDP.
+ */
+static void guac_rdp_cliprdr_channel_connected(rdpContext* context,
+ ChannelConnectedEventArgs* e) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_rdp_clipboard* clipboard = rdp_client->clipboard;
+
+ /* FreeRDP-specific handlers for CLIPRDR are not assigned, and thus not
+ * callable, until after the relevant guac_rdp_clipboard structure is
+ * allocated and associated with the guac_rdp_client */
+ assert(clipboard != NULL);
+
+ /* Ignore connection event if it's not for the CLIPRDR channel */
+ if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) != 0)
+ return;
+
+ /* The structure pointed to by pInterface is guaranteed to be a
+ * CliprdrClientContext if the channel is CLIPRDR */
+ CliprdrClientContext* cliprdr = (CliprdrClientContext*) e->pInterface;
+
+ /* Associate FreeRDP CLIPRDR context and its Guacamole counterpart with
+ * eachother */
+ cliprdr->custom = clipboard;
+ clipboard->cliprdr = cliprdr;
+
+ cliprdr->MonitorReady = guac_rdp_cliprdr_monitor_ready;
+ cliprdr->ServerFormatList = guac_rdp_cliprdr_format_list;
+ cliprdr->ServerFormatDataRequest = guac_rdp_cliprdr_format_data_request;
+ cliprdr->ServerFormatDataResponse = guac_rdp_cliprdr_format_data_response;
+
+ guac_client_log(client, GUAC_LOG_DEBUG, "CLIPRDR (clipboard redirection) "
+ "channel connected.");
+
+}
+
+guac_rdp_clipboard* guac_rdp_clipboard_alloc(guac_client* client) {
+
+ /* Allocate clipboard and underlying storage */
+ guac_rdp_clipboard* clipboard = calloc(1, sizeof(guac_rdp_clipboard));
+ clipboard->client = client;
+ clipboard->clipboard = guac_common_clipboard_alloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
+ clipboard->requested_format = CF_TEXT;
+
+ return clipboard;
+
+}
+
+void guac_rdp_clipboard_load_plugin(guac_rdp_clipboard* clipboard,
+ rdpContext* context) {
+
+ /* Attempt to load FreeRDP support for the CLIPRDR channel */
+ if (guac_freerdp_channels_load_plugin(context, "cliprdr", NULL)) {
+ guac_client_log(clipboard->client, GUAC_LOG_WARNING,
+ "Support for the CLIPRDR channel (clipboard redirection) "
+ "could not be loaded. This support normally takes the form of "
+ "a plugin which is built into FreeRDP. Lacking this support, "
+ "clipboard will not work.");
+ return;
+ }
+
+ /* Complete RDP side of initialization when channel is connected */
+ PubSub_SubscribeChannelConnected(context->pubSub,
+ (pChannelConnectedEventHandler) guac_rdp_cliprdr_channel_connected);
+
+ guac_client_log(clipboard->client, GUAC_LOG_DEBUG, "Support for CLIPRDR "
+ "(clipboard redirection) registered. Awaiting channel "
+ "connection.");
+
+}
+
+void guac_rdp_clipboard_free(guac_rdp_clipboard* clipboard) {
+
+ /* Do nothing if the clipboard is not actually allocated */
+ if (clipboard == NULL)
+ return;
+
+ /* Free clipboard and underlying storage */
+ guac_common_clipboard_free(clipboard->clipboard);
+ free(clipboard);
+
+}
+
+int guac_rdp_clipboard_handler(guac_user* user, guac_stream* stream,
+ char* mimetype) {
+
+ guac_client* client = user->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ /* Ignore stream creation if no clipboard structure is available to handle
+ * received data */
+ guac_rdp_clipboard* clipboard = rdp_client->clipboard;
+ if (clipboard == NULL)
+ return 0;
+
+ /* Handle any future "blob" and "end" instructions for this stream with
+ * handlers that are aware of the RDP clipboard state */
+ stream->blob_handler = guac_rdp_clipboard_blob_handler;
+ stream->end_handler = guac_rdp_clipboard_end_handler;
+
+ /* Clear any current contents, assigning the mimetype the data which will
+ * be received */
+ guac_common_clipboard_reset(clipboard->clipboard, mimetype);
+ return 0;
+
+}
+
+int guac_rdp_clipboard_blob_handler(guac_user* user, guac_stream* stream,
+ void* data, int length) {
+
+ guac_client* client = user->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ /* Ignore received data if no clipboard structure is available to handle
+ * that data */
+ guac_rdp_clipboard* clipboard = rdp_client->clipboard;
+ if (clipboard == NULL)
+ return 0;
+
+ /* Append received data to current clipboard contents */
+ guac_common_clipboard_append(clipboard->clipboard, (char*) data, length);
+ return 0;
+
+}
+
+
+int guac_rdp_clipboard_end_handler(guac_user* user, guac_stream* stream) {
+
+ guac_client* client = user->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ /* Ignore end of stream if no clipboard structure is available to handle
+ * the data that was received */
+ guac_rdp_clipboard* clipboard = rdp_client->clipboard;
+ if (clipboard == NULL)
+ return 0;
+
+ /* Terminate clipboard data with NULL */
+ guac_common_clipboard_append(clipboard->clipboard, "", 1);
+
+ /* Notify RDP server of new data, if connected */
+ if (clipboard->cliprdr != NULL) {
+ guac_client_log(client, GUAC_LOG_DEBUG, "Clipboard data received. "
+ "Reporting availability of clipboard data to RDP server.");
+ guac_rdp_cliprdr_send_format_list(clipboard->cliprdr);
+ }
+ else
+ guac_client_log(client, GUAC_LOG_DEBUG, "Clipboard data has been "
+ "received, but cannot be sent to the RDP server because the "
+ "CLIPRDR channel is not yet connected.");
+
+ return 0;
+
+}
+
diff --git a/src/protocols/rdp/channels/cliprdr.h b/src/protocols/rdp/channels/cliprdr.h
new file mode 100644
index 0000000..4c920bf
--- /dev/null
+++ b/src/protocols/rdp/channels/cliprdr.h
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_CHANNELS_CLIPRDR_H
+#define GUAC_RDP_CHANNELS_CLIPRDR_H
+
+#include "common/clipboard.h"
+
+#include <freerdp/client/cliprdr.h>
+#include <freerdp/freerdp.h>
+#include <guacamole/client.h>
+#include <guacamole/user.h>
+#include <winpr/stream.h>
+#include <winpr/wtypes.h>
+
+/**
+ * RDP clipboard, leveraging the "CLIPRDR" channel.
+ */
+typedef struct guac_rdp_clipboard {
+
+ /**
+ * The guac_client associated with the RDP connection. The broadcast
+ * socket of this client will receive any clipboard data received from the
+ * RDP server.
+ */
+ guac_client* client;
+
+ /**
+ * CLIPRDR control interface.
+ */
+ CliprdrClientContext* cliprdr;
+
+ /**
+ * The current clipboard contents.
+ */
+ guac_common_clipboard* clipboard;
+
+ /**
+ * The format of the clipboard which was requested. Data received from
+ * the RDP server should conform to this format. This will be one of
+ * several legal clipboard format values defined within FreeRDP's WinPR
+ * library, such as CF_TEXT.
+ */
+ UINT requested_format;
+
+} guac_rdp_clipboard;
+
+/**
+ * Allocates a new guac_rdp_clipboard which has been initialized for processing
+ * of Guacamole clipboard data. Support for the RDP side of the clipboard (the
+ * CLIPRDR channel) must be loaded separately during FreeRDP's PreConnect event
+ * using guac_rdp_clipboard_load_plugin().
+ *
+ * @param client
+ * The guac_client associated with the Guacamole side of the RDP
+ * connection.
+ *
+ * @return
+ * A newly-allocated instance of guac_rdp_clipboard which has been
+ * initialized for processing Guacamole clipboard data.
+ */
+guac_rdp_clipboard* guac_rdp_clipboard_alloc(guac_client* client);
+
+/**
+ * Initializes clipboard support for RDP and handling of the CLIPRDR channel.
+ * If failures occur, messages noting the specifics of those failures will be
+ * logged, and the RDP side of clipboard support will not be functional.
+ *
+ * This MUST be called within the PreConnect callback of the freerdp instance
+ * for CLIPRDR support to be loaded.
+ *
+ * @param clipboard
+ * The guac_rdp_clipboard instance which has been allocated for the current
+ * RDP connection.
+ *
+ * @param context
+ * The rdpContext associated with the FreeRDP side of the RDP connection.
+ */
+void guac_rdp_clipboard_load_plugin(guac_rdp_clipboard* clipboard,
+ rdpContext* context);
+
+/**
+ * Frees the resources associated with clipboard support for RDP and handling
+ * of the CLIPRDR channel. Only resources specific to Guacamole are freed.
+ * Resources specific to FreeRDP's handling of the CLIPRDR channel will be
+ * freed by FreeRDP. If the provided guac_rdp_clipboard is NULL, this function
+ * has no effect.
+ *
+ * @param clipboard
+ * The guac_rdp_clipboard instance which was been allocated for the current
+ * RDP connection.
+ */
+void guac_rdp_clipboard_free(guac_rdp_clipboard* clipboard);
+
+/**
+ * Handler for inbound clipboard data, received via the stream created by an
+ * inbound "clipboard" instruction. This handler will assign the
+ * stream-specific handlers for processing "blob" and "end" instructions which
+ * will eventually be received as clipboard data is sent. This specific handler
+ * is expected to be assigned to the guac_user object of any user that may send
+ * clipboard data. The guac_rdp_clipboard instance which will receive this data
+ * MUST already be stored on the guac_rdp_client structure associated with the
+ * current RDP connection.
+ */
+guac_user_clipboard_handler guac_rdp_clipboard_handler;
+
+/**
+ * Handler for stream data related to clipboard, received via "blob"
+ * instructions for a stream which has already been created with an inbound
+ * "clipboard" instruction. This specific handler is assigned to the
+ * guac_stream structure associated with that clipboard stream by
+ * guac_rdp_clipboard_handler(). The guac_rdp_clipboard instance which will
+ * receive this data MUST already be stored on the guac_rdp_client structure
+ * associated with the current RDP connection.
+ */
+guac_user_blob_handler guac_rdp_clipboard_blob_handler;
+
+/**
+ * Handler for end-of-stream related to clipboard, indicated via an "end"
+ * instruction for a stream which has already been created with an inbound
+ * "clipboard" instruction. This specific handler is assigned to the
+ * guac_stream structure associated with that clipboard stream by
+ * guac_rdp_clipboard_handler(). The guac_rdp_clipboard instance which will
+ * receive this data MUST already be stored on the guac_rdp_client structure
+ * associated with the current RDP connection.
+ */
+guac_user_end_handler guac_rdp_clipboard_end_handler;
+
+#endif
+
diff --git a/src/protocols/rdp/channels/common-svc.c b/src/protocols/rdp/channels/common-svc.c
new file mode 100644
index 0000000..b73dd6e
--- /dev/null
+++ b/src/protocols/rdp/channels/common-svc.c
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "channels/common-svc.h"
+#include "plugins/channels.h"
+#include "rdp.h"
+
+#include <freerdp/settings.h>
+#include <guacamole/client.h>
+#include <guacamole/string.h>
+#include <winpr/stream.h>
+#include <winpr/wtsapi.h>
+#include <winpr/wtypes.h>
+
+#include <stdlib.h>
+
+int guac_rdp_common_svc_load_plugin(rdpContext* context,
+ char* name, ULONG channel_options,
+ guac_rdp_common_svc_connect_handler* connect_handler,
+ guac_rdp_common_svc_receive_handler* receive_handler,
+ guac_rdp_common_svc_terminate_handler* terminate_handler) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+
+ guac_rdp_common_svc* svc = calloc(1, sizeof(guac_rdp_common_svc));
+ svc->client = client;
+ svc->name = svc->_channel_def.name;
+ svc->_connect_handler = connect_handler;
+ svc->_receive_handler = receive_handler;
+ svc->_terminate_handler = terminate_handler;
+
+ /* Init FreeRDP channel definition */
+ int name_length = guac_strlcpy(svc->_channel_def.name, name, GUAC_RDP_SVC_MAX_LENGTH);
+ svc->_channel_def.options =
+ CHANNEL_OPTION_INITIALIZED
+ | CHANNEL_OPTION_ENCRYPT_RDP
+ | channel_options;
+
+ /* Warn about name length */
+ if (name_length >= GUAC_RDP_SVC_MAX_LENGTH)
+ guac_client_log(client, GUAC_LOG_WARNING,
+ "Static channel name \"%s\" exceeds maximum length of %i "
+ "characters and will be truncated to \"%s\".",
+ name, GUAC_RDP_SVC_MAX_LENGTH - 1, svc->name);
+
+ /* Attempt to load the common SVC plugin for new static channel */
+ int result = guac_freerdp_channels_load_plugin(context, "guac-common-svc", svc);
+ if (result) {
+ guac_client_log(client, GUAC_LOG_WARNING, "Cannot create static "
+ "channel \"%s\": failed to load \"guac-common-svc\" plugin "
+ "for FreeRDP.", svc->name);
+ free(svc);
+ }
+
+ /* Store and log on success (SVC structure will be freed on channel termination) */
+ else
+ guac_client_log(client, GUAC_LOG_DEBUG, "Support for static channel "
+ "\"%s\" loaded.", svc->name);
+
+ return result;
+
+}
+
+void guac_rdp_common_svc_write(guac_rdp_common_svc* svc,
+ wStream* output_stream) {
+
+ /* Do not write if plugin not associated */
+ if (!svc->_open_handle) {
+ guac_client_log(svc->client, GUAC_LOG_WARNING, "%i bytes of data "
+ "written to SVC \"%s\" are being dropped because the remote "
+ "desktop side of that SVC is not yet connected.",
+ Stream_Length(output_stream), svc->name);
+ return;
+ }
+
+ /* NOTE: Data sent via pVirtualChannelWriteEx MUST always be dynamically
+ * allocated, as it will be automatically freed using free(). If provided,
+ * the last parameter (user data) MUST be a pointer to a wStream, as it
+ * will automatically be freed by FreeRDP using Stream_Free() */
+ svc->_entry_points.pVirtualChannelWriteEx(svc->_init_handle,
+ svc->_open_handle, Stream_Buffer(output_stream),
+ Stream_GetPosition(output_stream), output_stream);
+
+}
+
diff --git a/src/protocols/rdp/channels/common-svc.h b/src/protocols/rdp/channels/common-svc.h
new file mode 100644
index 0000000..0a420bc
--- /dev/null
+++ b/src/protocols/rdp/channels/common-svc.h
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_CHANNELS_COMMON_SVC_H
+#define GUAC_RDP_CHANNELS_COMMON_SVC_H
+
+#include <freerdp/freerdp.h>
+#include <freerdp/svc.h>
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+#include <winpr/stream.h>
+#include <winpr/wtsapi.h>
+#include <winpr/wtypes.h>
+
+/**
+ * The maximum number of bytes to allow within each channel name, including
+ * null terminator.
+ */
+#define GUAC_RDP_SVC_MAX_LENGTH 8
+
+/**
+ * The maximum number of bytes that the RDP server will be allowed to send
+ * within any single write operation, regardless of the number of chunks that
+ * write is split into. Bytes beyond this limit may be dropped.
+ */
+#define GUAC_SVC_MAX_ASSEMBLED_LENGTH 1048576
+
+/**
+ * Structure describing a static virtual channel, and the corresponding
+ * Guacamole pipes and FreeRDP resources.
+ */
+typedef struct guac_rdp_common_svc guac_rdp_common_svc;
+
+/**
+ * Handler which is invoked when a CHANNEL_EVENT_CONNECTED event has been
+ * processed and the connection/initialization process of the SVC is now
+ * complete.
+ *
+ * @param svc
+ * The guac_rdp_common_svc structure representing the SVC that is now
+ * connected.
+ */
+typedef void guac_rdp_common_svc_connect_handler(guac_rdp_common_svc* svc);
+
+/**
+ * Handler which is invoked when a logical block of data has been received
+ * along an SVC, having been reassembled from a series of
+ * CHANNEL_EVENT_DATA_RECEIVED events.
+ *
+ * @param svc
+ * The guac_rdp_common_svc structure representing the SVC that received the
+ * data.
+ *
+ * @param input_stream
+ * The reassembled block of data that was received.
+ */
+typedef void guac_rdp_common_svc_receive_handler(guac_rdp_common_svc* svc, wStream* input_stream);
+
+/**
+ * Handler which is invoked when a CHANNEL_EVENT_TERMINATED event has been
+ * processed and all resources associated with the SVC must now be freed.
+ *
+ * @param svc
+ * The guac_rdp_common_svc structure representing the SVC that has been
+ * terminated.
+ */
+typedef void guac_rdp_common_svc_terminate_handler(guac_rdp_common_svc* svc);
+
+struct guac_rdp_common_svc {
+
+ /**
+ * Reference to the client owning this static channel.
+ */
+ guac_client* client;
+
+ /**
+ * The name of the static virtual channel, as specified to
+ * guac_rdp_common_svc_load_plugin(). This value is stored and defined
+ * internally by the CHANNEL_DEF.
+ */
+ const char* name;
+
+ /**
+ * Arbitrary channel-specific data which may be assigned and referenced by
+ * channel implementations leveraging the "guac-common-svc" plugin.
+ */
+ void* data;
+
+ /**
+ * Handler which is invoked when handling a CHANNEL_EVENT_CONNECTED event.
+ */
+ guac_rdp_common_svc_connect_handler* _connect_handler;
+
+ /**
+ * Handler which is invoked when all chunks of data for a single logical
+ * block have been received via CHANNEL_EVENT_DATA_RECEIVED events and
+ * reassembled.
+ */
+ guac_rdp_common_svc_receive_handler* _receive_handler;
+
+ /**
+ * Handler which is invokved when the SVC has been disconnected and is
+ * about to be freed.
+ */
+ guac_rdp_common_svc_terminate_handler* _terminate_handler;
+
+ /**
+ * The definition of this static virtual channel, including its name.
+ */
+ CHANNEL_DEF _channel_def;
+
+ /**
+ * Functions and data specific to the FreeRDP side of the virtual channel
+ * and plugin.
+ */
+ CHANNEL_ENTRY_POINTS_FREERDP_EX _entry_points;
+
+ /**
+ * Handle which identifies the client connection, typically referred to
+ * within the FreeRDP source as pInitHandle. This handle is provided to the
+ * channel entry point and the channel init event handler. The handle must
+ * eventually be used within the channel open event handler to obtain a
+ * handle to the channel itself.
+ */
+ PVOID _init_handle;
+
+ /**
+ * Handle which identifies the channel itself, typically referred to within
+ * the FreeRDP source as OpenHandle. This handle is obtained through a call
+ * to entry_points.pVirtualChannelOpenEx() in response to receiving a
+ * CHANNEL_EVENT_CONNECTED event via the init event handler.
+ *
+ * Data is received in CHANNEL_EVENT_DATA_RECEIVED events via the open
+ * event handler, and data is written through calls to
+ * entry_points.pVirtualChannelWriteEx().
+ */
+ DWORD _open_handle;
+
+ /**
+ * All data that has been received thus far from the current RDP server
+ * write operation. Data received along virtual channels is sent in chunks
+ * (typically 1600 bytes), and thus must be gradually reassembled as it is
+ * received.
+ */
+ wStream* _input_stream;
+
+};
+
+/**
+ * Initializes arbitrary static virtual channel (SVC) support for RDP, loading
+ * a new instance of Guacamole's arbitrary SVC plugin for FreeRDP ("guacsvc")
+ * supporting the channel having the given name. Data sent from within the RDP
+ * session using this channel will be sent along an identically-named pipe
+ * stream to the Guacamole client, and data sent along a pipe stream having the
+ * same name will be written to the SVC and received within the RDP session. If
+ * failures occur while loading the plugin, messages noting the specifics of
+ * those failures will be logged, and support for the given channel will not be
+ * functional.
+ *
+ * This MUST be called within the PreConnect callback of the freerdp instance
+ * for static virtual channel support to be loaded.
+ *
+ * @param context
+ * The rdpContext associated with the FreeRDP side of the RDP connection.
+ *
+ * @param name
+ * The name of the SVC which should be handled by the new instance of the
+ * plugin.
+ *
+ * @param channel_options
+ * Bitwise OR of any of the several CHANNEL_OPTION_* flags. Regardless of
+ * whether specified here, the CHANNEL_OPTION_INTIALIZED and
+ * CHANNEL_OPTION_ENCRYPT_RDP flags will automatically be set.
+ *
+ * @param connect_handler
+ * The function to invoke when the SVC has been connected.
+ *
+ * @param receive_handler
+ * The function to invoke when the SVC has received a logical block of
+ * data, reassembled from perhaps several smaller chunks of data.
+ *
+ * @param terminate_handler
+ * The function to invoke when the SVC has been disconnected and is about
+ * to be freed.
+ *
+ * @return
+ * Zero if the plugin was loaded successfully, non-zero if the plugin could
+ * not be loaded.
+ */
+int guac_rdp_common_svc_load_plugin(rdpContext* context,
+ char* name, ULONG channel_options,
+ guac_rdp_common_svc_connect_handler* connect_handler,
+ guac_rdp_common_svc_receive_handler* receive_handler,
+ guac_rdp_common_svc_terminate_handler* terminate_handler);
+
+/**
+ * Writes the given data to the virtual channel such that it can be received
+ * within the RDP session. The given data MUST be dynamically allocated, as the
+ * write operation may be queued and the actual write may not occur until
+ * later. The provided wStream and the buffer it points to will be
+ * automatically freed after the write occurs.
+ *
+ * @param svc
+ * The static virtual channel to write data to.
+ *
+ * @param output_stream
+ * The data to write, which MUST be dynamically allocated.
+ */
+void guac_rdp_common_svc_write(guac_rdp_common_svc* svc,
+ wStream* output_stream);
+
+#endif
+
diff --git a/src/protocols/rdp/rdp_disp.c b/src/protocols/rdp/channels/disp.c
similarity index 69%
rename from src/protocols/rdp/rdp_disp.c
rename to src/protocols/rdp/channels/disp.c
index 7c7e059..a25f6fa 100644
--- a/src/protocols/rdp/rdp_disp.c
+++ b/src/protocols/rdp/channels/disp.c
@@ -17,29 +17,26 @@
* under the License.
*/
-#include "config.h"
-#include "client.h"
-#include "dvc.h"
+#include "channels/disp.h"
+#include "plugins/channels.h"
#include "rdp.h"
-#include "rdp_disp.h"
-#include "rdp_settings.h"
+#include "settings.h"
+#include <freerdp/client/disp.h>
#include <freerdp/freerdp.h>
+#include <freerdp/event.h>
#include <guacamole/client.h>
#include <guacamole/timestamp.h>
-#ifdef HAVE_FREERDP_CLIENT_DISP_H
-#include <freerdp/client/disp.h>
-#endif
+#include <stdlib.h>
+#include <string.h>
guac_rdp_disp* guac_rdp_disp_alloc() {
guac_rdp_disp* disp = malloc(sizeof(guac_rdp_disp));
-#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
/* Not yet connected */
disp->disp = NULL;
-#endif
/* No requests have been made */
disp->last_request = guac_timestamp_current();
@@ -55,23 +52,60 @@
free(disp);
}
-void guac_rdp_disp_load_plugin(rdpContext* context, guac_rdp_dvc_list* list) {
+/**
+ * Callback which associates handlers specific to Guacamole with the
+ * DispClientContext instance allocated by FreeRDP to deal with received
+ * Display Update (client-initiated dynamic display resizing) messages.
+ *
+ * This function is called whenever a channel connects via the PubSub event
+ * system within FreeRDP, but only has any effect if the connected channel is
+ * the Display Update channel. This specific callback is registered with the
+ * PubSub system of the relevant rdpContext when guac_rdp_disp_load_plugin() is
+ * called.
+ *
+ * @param context
+ * The rdpContext associated with the active RDP session.
+ *
+ * @param e
+ * Event-specific arguments, mainly the name of the channel, and a
+ * reference to the associated plugin loaded for that channel by FreeRDP.
+ */
+static void guac_rdp_disp_channel_connected(rdpContext* context,
+ ChannelConnectedEventArgs* e) {
-#ifdef HAVE_RDPSETTINGS_SUPPORTDISPLAYCONTROL
- context->settings->SupportDisplayControl = TRUE;
-#endif
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_rdp_disp* guac_disp = rdp_client->disp;
+
+ /* Ignore connection event if it's not for the Display Update channel */
+ if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) != 0)
+ return;
+
+ /* Init module with current display size */
+ guac_rdp_disp_set_size(guac_disp, rdp_client->settings,
+ context->instance, guac_rdp_get_width(context->instance),
+ guac_rdp_get_height(context->instance));
+
+ /* Store reference to the display update plugin once it's connected */
+ DispClientContext* disp = (DispClientContext*) e->pInterface;
+ guac_disp->disp = disp;
+
+ guac_client_log(client, GUAC_LOG_DEBUG, "Display update channel "
+ "will be used for display size changes.");
+
+}
+
+void guac_rdp_disp_load_plugin(rdpContext* context) {
+
+ /* Subscribe to and handle channel connected events */
+ PubSub_SubscribeChannelConnected(context->pubSub,
+ (pChannelConnectedEventHandler) guac_rdp_disp_channel_connected);
/* Add "disp" channel */
- guac_rdp_dvc_list_add(list, "disp", NULL);
+ guac_freerdp_dynamic_channel_collection_add(context->settings, "disp", NULL);
}
-#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
-void guac_rdp_disp_connect(guac_rdp_disp* guac_disp, DispClientContext* disp) {
- guac_disp->disp = disp;
-}
-#endif
-
/**
* Fits a given dimension within the allowed bounds for Display Update
* messages, adjusting the other dimension such that aspect ratio is
@@ -172,7 +206,6 @@
}
else if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) {
-#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
DISPLAY_CONTROL_MONITOR_LAYOUT monitors[1] = {{
.Flags = 0x1, /* DISPLAYCONTROL_MONITOR_PRIMARY */
.Left = 0,
@@ -189,7 +222,6 @@
/* Send display update notification if display channel is connected */
if (disp->disp != NULL)
disp->disp->SendMonitorLayout(disp->disp, 1, monitors);
-#endif
}
}
diff --git a/src/protocols/rdp/rdp_disp.h b/src/protocols/rdp/channels/disp.h
similarity index 75%
rename from src/protocols/rdp/rdp_disp.h
rename to src/protocols/rdp/channels/disp.h
index 0f34fe1..2aff7c2 100644
--- a/src/protocols/rdp/rdp_disp.h
+++ b/src/protocols/rdp/channels/disp.h
@@ -17,17 +17,15 @@
* under the License.
*/
-#ifndef GUAC_RDP_DISP_H
-#define GUAC_RDP_DISP_H
+#ifndef GUAC_RDP_CHANNELS_DISP_H
+#define GUAC_RDP_CHANNELS_DISP_H
-#include "dvc.h"
-#include "rdp_settings.h"
+#include "settings.h"
-#include <freerdp/freerdp.h>
-
-#ifdef HAVE_FREERDP_CLIENT_DISP_H
#include <freerdp/client/disp.h>
-#endif
+#include <freerdp/freerdp.h>
+#include <guacamole/client.h>
+#include <guacamole/timestamp.h>
/**
* The minimum value for width or height, in pixels.
@@ -50,12 +48,10 @@
*/
typedef struct guac_rdp_disp {
-#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
/**
* Display control interface.
*/
DispClientContext* disp;
-#endif
/**
* The timestamp of the last display update request, or 0 if no request
@@ -83,53 +79,43 @@
/**
* Allocates a new display update module, which will ultimately control the
- * display update channel once conected.
+ * display update channel once connected.
*
- * @return A new display update module.
+ * @return
+ * A newly-allocated display update module.
*/
guac_rdp_disp* guac_rdp_disp_alloc();
/**
- * Frees the given display update module.
+ * Frees the resources associated with support for the RDP Display Update
+ * channel. Only resources specific to Guacamole are freed. Resources specific
+ * to FreeRDP's handling of the Display Update channel will be freed by
+ * FreeRDP. If no resources are currently allocated for Display Update support,
+ * this function has no effect.
*
- * @param disp The display update module to free.
+ * @param disp
+ * The display update module to free.
*/
void guac_rdp_disp_free(guac_rdp_disp* disp);
/**
- * @param context The rdpContext associated with the active RDP session.
- */
-/**
* Adds FreeRDP's "disp" plugin to the list of dynamic virtual channel plugins
- * to be loaded by FreeRDP's "drdynvc" plugin. The plugin will only be loaded
- * once guac_rdp_load_drdynvc() is invoked with the guac_rdp_dvc_list passed to
- * this function. The "disp" plugin ultimately adds support for the Display
- * Update channel. NOTE: It is still up to external code to detect when the
- * "disp" channel is connected, and update the guac_rdp_disp with a call to
- * guac_rdp_disp_connect().
+ * to be loaded by FreeRDP's "drdynvc" plugin. The context of the plugin will
+ * automatically be assicated with the guac_rdp_disp instance pointed to by the
+ * current guac_rdp_client. The plugin will only be loaded once the "drdynvc"
+ * plugin is loaded. The "disp" plugin ultimately adds support for the Display
+ * Update channel.
+ *
+ * If failures occur, messages noting the specifics of those failures will be
+ * logged, and the RDP side of Display Update support will not be functional.
+ *
+ * This MUST be called within the PreConnect callback of the freerdp instance
+ * for Display Update support to be loaded.
*
* @param context
* The rdpContext associated with the active RDP session.
- *
- * @param list
- * The guac_rdp_dvc_list to which the "disp" plugin should be added, such
- * that it may later be loaded by guac_rdp_load_drdynvc().
*/
-void guac_rdp_disp_load_plugin(rdpContext* context, guac_rdp_dvc_list* list);
-
-#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
-/**
- * Stores the given DispClientContext within the given guac_rdp_disp, such that
- * display updates can be properly sent. Until this is called, changes to the
- * display size will be deferred.
- *
- * @param guac_disp The display update module to associate with the connected
- * display update channel.
- * @param disp The DispClientContext associated by FreeRDP with the connected
- * display update channel.
- */
-void guac_rdp_disp_connect(guac_rdp_disp* guac_disp, DispClientContext* disp);
-#endif
+void guac_rdp_disp_load_plugin(rdpContext* context);
/**
* Requests a display size update, which may then be sent immediately to the
@@ -200,6 +186,10 @@
* Returns whether a full RDP reconnect is required for display update changes
* to take effect.
*
+ * @param disp
+ * The display update module that should be checked to determine whether a
+ * reconnect is required.
+ *
* @return
* Non-zero if a reconnect is needed, zero otherwise.
*/
diff --git a/src/protocols/rdp/channels/pipe-svc.c b/src/protocols/rdp/channels/pipe-svc.c
new file mode 100644
index 0000000..2db42d6
--- /dev/null
+++ b/src/protocols/rdp/channels/pipe-svc.c
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "channels/common-svc.h"
+#include "channels/pipe-svc.h"
+#include "common/list.h"
+#include "rdp.h"
+
+#include <freerdp/settings.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/stream.h>
+#include <guacamole/user.h>
+#include <winpr/stream.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+void guac_rdp_pipe_svc_send_pipe(guac_socket* socket, guac_rdp_pipe_svc* pipe_svc) {
+
+ /* Send pipe instruction for the SVC's output stream */
+ guac_protocol_send_pipe(socket, pipe_svc->output_pipe,
+ "application/octet-stream", pipe_svc->svc->name);
+
+}
+
+void guac_rdp_pipe_svc_send_pipes(guac_user* user) {
+
+ guac_client* client = user->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ guac_common_list_lock(rdp_client->available_svc);
+
+ /* Send pipe for each allocated SVC's output stream */
+ guac_common_list_element* current = rdp_client->available_svc->head;
+ while (current != NULL) {
+ guac_rdp_pipe_svc_send_pipe(user->socket, (guac_rdp_pipe_svc*) current->data);
+ current = current->next;
+ }
+
+ guac_common_list_unlock(rdp_client->available_svc);
+
+}
+
+void guac_rdp_pipe_svc_add(guac_client* client, guac_rdp_pipe_svc* pipe_svc) {
+
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ /* Add to list of available SVC */
+ guac_common_list_lock(rdp_client->available_svc);
+ guac_common_list_add(rdp_client->available_svc, pipe_svc);
+ guac_common_list_unlock(rdp_client->available_svc);
+
+}
+
+guac_rdp_pipe_svc* guac_rdp_pipe_svc_get(guac_client* client, const char* name) {
+
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_common_list_element* current;
+ guac_rdp_pipe_svc* found = NULL;
+
+ /* For each available SVC */
+ guac_common_list_lock(rdp_client->available_svc);
+ current = rdp_client->available_svc->head;
+ while (current != NULL) {
+
+ /* If name matches, found */
+ guac_rdp_pipe_svc* current_svc = (guac_rdp_pipe_svc*) current->data;
+ if (strcmp(current_svc->svc->name, name) == 0) {
+ found = current_svc;
+ break;
+ }
+
+ current = current->next;
+
+ }
+ guac_common_list_unlock(rdp_client->available_svc);
+
+ return found;
+
+}
+
+guac_rdp_pipe_svc* guac_rdp_pipe_svc_remove(guac_client* client, const char* name) {
+
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_common_list_element* current;
+ guac_rdp_pipe_svc* found = NULL;
+
+ /* For each available SVC */
+ guac_common_list_lock(rdp_client->available_svc);
+ current = rdp_client->available_svc->head;
+ while (current != NULL) {
+
+ /* If name matches, remove entry */
+ guac_rdp_pipe_svc* current_svc = (guac_rdp_pipe_svc*) current->data;
+ if (strcmp(current_svc->svc->name, name) == 0) {
+ guac_common_list_remove(rdp_client->available_svc, current);
+ found = current_svc;
+ break;
+ }
+
+ current = current->next;
+
+ }
+ guac_common_list_unlock(rdp_client->available_svc);
+
+ /* Return removed entry, if any */
+ return found;
+
+}
+
+int guac_rdp_pipe_svc_pipe_handler(guac_user* user, guac_stream* stream,
+ char* mimetype, char* name) {
+
+ guac_rdp_pipe_svc* pipe_svc = guac_rdp_pipe_svc_get(user->client, name);
+
+ /* Fail if no such SVC */
+ if (pipe_svc == NULL) {
+ guac_user_log(user, GUAC_LOG_WARNING, "User requested non-existent "
+ "pipe (no such SVC configured): \"%s\"", name);
+ guac_protocol_send_ack(user->socket, stream, "FAIL (NO SUCH PIPE)",
+ GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
+ guac_socket_flush(user->socket);
+ return 0;
+ }
+ else
+ guac_user_log(user, GUAC_LOG_DEBUG, "Inbound half of channel \"%s\" "
+ "connected.", name);
+
+ /* Init stream data */
+ stream->data = pipe_svc;
+ stream->blob_handler = guac_rdp_pipe_svc_blob_handler;
+
+ return 0;
+
+}
+
+int guac_rdp_pipe_svc_blob_handler(guac_user* user, guac_stream* stream,
+ void* data, int length) {
+
+ guac_rdp_pipe_svc* pipe_svc = (guac_rdp_pipe_svc*) stream->data;
+
+ /* Write blob data to SVC directly */
+ wStream* output_stream = Stream_New(NULL, length);
+ Stream_Write(output_stream, data, length);
+ guac_rdp_common_svc_write(pipe_svc->svc, output_stream);
+
+ guac_protocol_send_ack(user->socket, stream, "OK (DATA RECEIVED)",
+ GUAC_PROTOCOL_STATUS_SUCCESS);
+ guac_socket_flush(user->socket);
+ return 0;
+
+}
+
+void guac_rdp_pipe_svc_process_connect(guac_rdp_common_svc* svc) {
+
+ /* Associate SVC with new Guacamole pipe */
+ guac_rdp_pipe_svc* pipe_svc = malloc(sizeof(guac_rdp_pipe_svc));
+ pipe_svc->svc = svc;
+ pipe_svc->output_pipe = guac_client_alloc_stream(svc->client);
+ svc->data = pipe_svc;
+
+ /* SVC may now receive data from client */
+ guac_rdp_pipe_svc_add(svc->client, pipe_svc);
+
+ /* Notify of pipe's existence */
+ guac_rdp_pipe_svc_send_pipe(svc->client->socket, pipe_svc);
+
+}
+
+void guac_rdp_pipe_svc_process_receive(guac_rdp_common_svc* svc,
+ wStream* input_stream) {
+
+ guac_rdp_pipe_svc* pipe_svc = (guac_rdp_pipe_svc*) svc->data;
+
+ /* Fail if output not created */
+ if (pipe_svc->output_pipe == NULL) {
+ guac_client_log(svc->client, GUAC_LOG_WARNING, "%i bytes of data "
+ "received from within the remote desktop session for SVC "
+ "\"%s\" are being dropped because the outbound pipe stream "
+ "for that SVC is not yet open. This should NOT happen.",
+ Stream_Length(input_stream), svc->name);
+ return;
+ }
+
+ /* Send received data as blob */
+ guac_protocol_send_blob(svc->client->socket, pipe_svc->output_pipe, Stream_Buffer(input_stream), Stream_Length(input_stream));
+ guac_socket_flush(svc->client->socket);
+
+}
+
+void guac_rdp_pipe_svc_process_terminate(guac_rdp_common_svc* svc) {
+
+ guac_rdp_pipe_svc* pipe_svc = (guac_rdp_pipe_svc*) svc->data;
+ if (pipe_svc == NULL)
+ return;
+
+ /* Remove and free SVC */
+ guac_rdp_pipe_svc_remove(svc->client, svc->name);
+ free(pipe_svc);
+
+}
+
+void guac_rdp_pipe_svc_load_plugin(rdpContext* context, char* name) {
+
+ /* Attempt to load support for static channel */
+ guac_rdp_common_svc_load_plugin(context, name, CHANNEL_OPTION_COMPRESS_RDP,
+ guac_rdp_pipe_svc_process_connect,
+ guac_rdp_pipe_svc_process_receive,
+ guac_rdp_pipe_svc_process_terminate);
+
+}
+
diff --git a/src/protocols/rdp/channels/pipe-svc.h b/src/protocols/rdp/channels/pipe-svc.h
new file mode 100644
index 0000000..242d4e5
--- /dev/null
+++ b/src/protocols/rdp/channels/pipe-svc.h
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_CHANNELS_PIPE_SVC_H
+#define GUAC_RDP_CHANNELS_PIPE_SVC_H
+
+#include "channels/common-svc.h"
+
+#include <freerdp/freerdp.h>
+#include <freerdp/svc.h>
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+#include <guacamole/socket.h>
+#include <guacamole/user.h>
+#include <winpr/wtsapi.h>
+
+/**
+ * The maximum number of bytes to allow within each channel name, including
+ * null terminator.
+ */
+#define GUAC_RDP_SVC_MAX_LENGTH 8
+
+/**
+ * Structure describing a static virtual channel and a corresponding Guacamole
+ * pipe stream;
+ */
+typedef struct guac_rdp_pipe_svc {
+
+ /**
+ * The output pipe, opened when the RDP server receives a connection to
+ * the static channel.
+ */
+ guac_stream* output_pipe;
+
+ /**
+ * The underlying static channel. Data written to this SVC by the RDP
+ * server will be forwarded along the pipe stream to the Guacamole client,
+ * and data written to the pipe stream by the Guacamole client will be
+ * forwarded along the SVC to the RDP server.
+ */
+ guac_rdp_common_svc* svc;
+
+} guac_rdp_pipe_svc;
+
+/**
+ * Initializes arbitrary static virtual channel (SVC) support for RDP, handling
+ * communication for the SVC having the given name. Data sent from within the
+ * RDP session using this channel will be sent along an identically-named pipe
+ * stream to the Guacamole client, and data sent along a pipe stream having the
+ * same name will be written to the SVC and received within the RDP session. If
+ * failures occur while loading the plugin, messages noting the specifics of
+ * those failures will be logged, and support for the given channel will not be
+ * functional.
+ *
+ * This MUST be called within the PreConnect callback of the freerdp instance
+ * for static virtual channel support to be loaded.
+ *
+ * @param context
+ * The rdpContext associated with the FreeRDP side of the RDP connection.
+ *
+ * @param name
+ * The name of the SVC which should be handled.
+ */
+void guac_rdp_pipe_svc_load_plugin(rdpContext* context, char* name);
+
+/**
+ * Sends the "pipe" instruction describing the given static virtual channel
+ * along the given socket. This pipe instruction will relate the SVC's
+ * underlying output stream with the SVC's name and the mimetype
+ * "application/octet-stream".
+ *
+ * @param socket
+ * The socket along which the "pipe" instruction should be sent.
+ *
+ * @param svc
+ * The static virtual channel that the "pipe" instruction should describe.
+ */
+void guac_rdp_pipe_svc_send_pipe(guac_socket* socket, guac_rdp_pipe_svc* svc);
+
+/**
+ * Sends the "pipe" instructions describing all static virtual channels
+ * available to the given user along that user's socket. Each pipe instruction
+ * will relate the associated SVC's underlying output stream with the SVC's
+ * name and the mimetype "application/octet-stream".
+ *
+ * @param user
+ * The user to send the "pipe" instructions to.
+ */
+void guac_rdp_pipe_svc_send_pipes(guac_user* user);
+
+/**
+ * Add the given SVC to the list of all available SVCs. This function must be
+ * invoked after the SVC is connected for inbound pipe streams having that
+ * SVC's name to result in received data being sent into the RDP session.
+ *
+ * @param client
+ * The guac_client associated with the current RDP session.
+ *
+ * @param svc
+ * The static virtual channel to add to the list of all such channels
+ * available.
+ */
+void guac_rdp_pipe_svc_add(guac_client* client, guac_rdp_pipe_svc* svc);
+
+/**
+ * Retrieve the SVC with the given name from the list stored in the client. The
+ * requested SVC must previously have been added using guac_rdp_pipe_svc_add().
+ *
+ * @param client
+ * The guac_client associated with the current RDP session.
+ *
+ * @param name
+ * The name of the static virtual channel to retrieve.
+ *
+ * @return
+ * The static virtual channel with the given name, or NULL if no such
+ * virtual channel exists.
+ */
+guac_rdp_pipe_svc* guac_rdp_pipe_svc_get(guac_client* client, const char* name);
+
+/**
+ * Removes the SVC with the given name from the list stored in the client.
+ * Inbound pipe streams having the given name will no longer be routed to the
+ * associated SVC.
+ *
+ * @param client
+ * The guac_client associated with the current RDP session.
+ *
+ * @param name
+ * The name of the static virtual channel to remove.
+ *
+ * @return
+ * The static virtual channel that was removed, or NULL if no such virtual
+ * channel exists.
+ */
+guac_rdp_pipe_svc* guac_rdp_pipe_svc_remove(guac_client* client, const char* name);
+
+/**
+ * Handler for "blob" instructions which writes received data to the associated
+ * SVC using guac_rdp_pipe_svc_write().
+ */
+guac_user_blob_handler guac_rdp_pipe_svc_blob_handler;
+
+/**
+ * Handler for "pipe" instructions which prepares received pipe streams to
+ * write received blobs to the SVC having the same name as the pipe stream.
+ * Received pipe streams are associated with the relevant guac_rdp_pipe_svc
+ * instance and the SVC-specific "blob" instruction handler
+ * (guac_rdp_pipe_svc_blob_handler).
+ */
+guac_user_pipe_handler guac_rdp_pipe_svc_pipe_handler;
+
+/**
+ * Handler which is invoked when an SVC associated with a Guacamole pipe stream
+ * is connected to the RDP server.
+ */
+guac_rdp_common_svc_connect_handler guac_rdp_pipe_svc_process_connect;
+
+/**
+ * Handler which is invoked when an SVC associated with a Guacamole pipe stream
+ * received data from the RDP server.
+ */
+guac_rdp_common_svc_receive_handler guac_rdp_pipe_svc_process_receive;
+
+/**
+ * Handler which is invoked when an SVC associated with a Guacamole pipe stream
+ * has disconnected and is about to be freed.
+ */
+guac_rdp_common_svc_terminate_handler guac_rdp_pipe_svc_process_terminate;
+
+#endif
+
diff --git a/src/protocols/rdp/channels/rail.c b/src/protocols/rdp/channels/rail.c
new file mode 100644
index 0000000..3e80007
--- /dev/null
+++ b/src/protocols/rdp/channels/rail.c
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "channels/rail.h"
+#include "plugins/channels.h"
+#include "rdp.h"
+#include "settings.h"
+
+#include <freerdp/client/rail.h>
+#include <freerdp/event.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/rail.h>
+#include <guacamole/client.h>
+#include <winpr/wtypes.h>
+#include <winpr/wtsapi.h>
+
+#include <stddef.h>
+#include <string.h>
+
+#ifdef FREERDP_RAIL_CALLBACKS_REQUIRE_CONST
+/**
+ * FreeRDP 2.0.0-rc4 and newer requires the final argument for all RAIL
+ * callbacks to be const.
+ */
+#define RAIL_CONST const
+#else
+/**
+ * FreeRDP 2.0.0-rc3 and older requires the final argument for all RAIL
+ * callbacks to NOT be const.
+ */
+#define RAIL_CONST
+#endif
+
+/**
+ * Completes initialization of the RemoteApp session, sending client system
+ * parameters and executing the desired RemoteApp command using the Client
+ * System Parameters Update PDU and Client Execute PDU respectively. These PDUs
+ * MUST be sent for the desired RemoteApp to run, and MUST NOT be sent until
+ * after a Handshake or HandshakeEx PDU has been received. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/60344497-883f-4711-8b9a-828d1c580195 (System Parameters Update PDU)
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/98a6e3c3-c2a9-42cc-ad91-0d9a6c211138 (Client Execute PDU)
+ *
+ * @param rail
+ * The RailClientContext structure used by FreeRDP to handle the RAIL
+ * channel for the current RDP session.
+ *
+ * @return
+ * CHANNEL_RC_OK (zero) if the PDUs were sent successfully, an error code
+ * (non-zero) otherwise.
+ */
+static UINT guac_rdp_rail_complete_handshake(RailClientContext* rail) {
+
+ guac_client* client = (guac_client*) rail->custom;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ RAIL_SYSPARAM_ORDER sysparam = {
+ .workArea = {
+ .left = 0,
+ .top = 0,
+ .right = rdp_client->settings->width,
+ .bottom = rdp_client->settings->height
+ },
+ .dragFullWindows = FALSE
+ };
+
+ /* Send client system parameters */
+ UINT status = rail->ClientSystemParam(rail, &sysparam);
+ if (status != CHANNEL_RC_OK)
+ return status;
+
+ RAIL_EXEC_ORDER exec = {
+ .RemoteApplicationProgram = rdp_client->settings->remote_app,
+ .RemoteApplicationWorkingDir = rdp_client->settings->remote_app_dir,
+ .RemoteApplicationArguments = rdp_client->settings->remote_app_args,
+ };
+
+ /* Execute desired RemoteApp command */
+ return rail->ClientExecute(rail, &exec);
+
+}
+
+/**
+ * Callback which is invoked when a Handshake PDU is received from the RDP
+ * server. No communication for RemoteApp may occur until the Handshake PDU
+ * (or, alternatively, the HandshakeEx PDU) is received. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/cec4eb83-b304-43c9-8378-b5b8f5e7082a
+ *
+ * @param rail
+ * The RailClientContext structure used by FreeRDP to handle the RAIL
+ * channel for the current RDP session.
+ *
+ * @param handshake
+ * The RAIL_HANDSHAKE_ORDER structure representing the Handshake PDU that
+ * was received.
+ *
+ * @return
+ * CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
+ * (non-zero) otherwise.
+ */
+static UINT guac_rdp_rail_handshake(RailClientContext* rail,
+ RAIL_CONST RAIL_HANDSHAKE_ORDER* handshake) {
+ return guac_rdp_rail_complete_handshake(rail);
+}
+
+/**
+ * Callback which is invoked when a HandshakeEx PDU is received from the RDP
+ * server. No communication for RemoteApp may occur until the HandshakeEx PDU
+ * (or, alternatively, the Handshake PDU) is received. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/5cec5414-27de-442e-8d4a-c8f8b41f3899
+ *
+ * @param rail
+ * The RailClientContext structure used by FreeRDP to handle the RAIL
+ * channel for the current RDP session.
+ *
+ * @param handshake_ex
+ * The RAIL_HANDSHAKE_EX_ORDER structure representing the HandshakeEx PDU
+ * that was received.
+ *
+ * @return
+ * CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
+ * (non-zero) otherwise.
+ */
+static UINT guac_rdp_rail_handshake_ex(RailClientContext* rail,
+ RAIL_CONST RAIL_HANDSHAKE_EX_ORDER* handshake_ex) {
+ return guac_rdp_rail_complete_handshake(rail);
+}
+
+/**
+ * Callback which associates handlers specific to Guacamole with the
+ * RailClientContext instance allocated by FreeRDP to deal with received
+ * RAIL (RemoteApp) messages.
+ *
+ * This function is called whenever a channel connects via the PubSub event
+ * system within FreeRDP, but only has any effect if the connected channel is
+ * the RAIL channel. This specific callback is registered with the PubSub
+ * system of the relevant rdpContext when guac_rdp_rail_load_plugin() is
+ * called.
+ *
+ * @param context
+ * The rdpContext associated with the active RDP session.
+ *
+ * @param e
+ * Event-specific arguments, mainly the name of the channel, and a
+ * reference to the associated plugin loaded for that channel by FreeRDP.
+ */
+static void guac_rdp_rail_channel_connected(rdpContext* context,
+ ChannelConnectedEventArgs* e) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+
+ /* Ignore connection event if it's not for the RAIL channel */
+ if (strcmp(e->name, RAIL_SVC_CHANNEL_NAME) != 0)
+ return;
+
+ /* The structure pointed to by pInterface is guaranteed to be a
+ * RailClientContext if the channel is RAIL */
+ RailClientContext* rail = (RailClientContext*) e->pInterface;
+
+ /* Init FreeRDP RAIL context, ensuring the guac_client can be accessed from
+ * within any RAIL-specific callbacks */
+ rail->custom = client;
+ rail->ServerHandshake = guac_rdp_rail_handshake;
+ rail->ServerHandshakeEx = guac_rdp_rail_handshake_ex;
+
+ guac_client_log(client, GUAC_LOG_DEBUG, "RAIL (RemoteApp) channel "
+ "connected.");
+
+}
+
+void guac_rdp_rail_load_plugin(rdpContext* context) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+
+ /* Attempt to load FreeRDP support for the RAIL channel */
+ if (guac_freerdp_channels_load_plugin(context, "rail", context->settings)) {
+ guac_client_log(client, GUAC_LOG_WARNING,
+ "Support for the RAIL channel (RemoteApp) could not be "
+ "loaded. This support normally takes the form of a plugin "
+ "which is built into FreeRDP. Lacking this support, "
+ "RemoteApp will not work.");
+ return;
+ }
+
+ /* Complete RDP side of initialization when channel is connected */
+ PubSub_SubscribeChannelConnected(context->pubSub,
+ (pChannelConnectedEventHandler) guac_rdp_rail_channel_connected);
+
+ guac_client_log(client, GUAC_LOG_DEBUG, "Support for RAIL (RemoteApp) "
+ "registered. Awaiting channel connection.");
+
+}
+
diff --git a/src/protocols/rdp/channels/rail.h b/src/protocols/rdp/channels/rail.h
new file mode 100644
index 0000000..0856034
--- /dev/null
+++ b/src/protocols/rdp/channels/rail.h
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_CHANNELS_RAIL_H
+#define GUAC_RDP_CHANNELS_RAIL_H
+
+#include <freerdp/freerdp.h>
+
+/**
+ * Initializes RemoteApp support for RDP and handling of the RAIL channel. If
+ * failures occur, messages noting the specifics of those failures will be
+ * logged, and RemoteApp support will not be functional.
+ *
+ * This MUST be called within the PreConnect callback of the freerdp instance
+ * for RAIL support to be loaded.
+ *
+ * @param context
+ * The rdpContext associated with the FreeRDP side of the RDP connection.
+ */
+void guac_rdp_rail_load_plugin(rdpContext* context);
+
+#endif
+
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.c b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-dir-info.c
similarity index 70%
rename from src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.c
rename to src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-dir-info.c
index c53eedc..70eb8cd 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.c
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-dir-info.c
@@ -17,26 +17,22 @@
* under the License.
*/
-#include "config.h"
-
-#include "rdpdr_service.h"
-#include "rdp_fs.h"
-#include "rdp_status.h"
+#include "channels/rdpdr/rdpdr-fs-messages-dir-info.h"
+#include "channels/rdpdr/rdpdr.h"
+#include "fs.h"
#include "unicode.h"
-#include <freerdp/utils/svc_plugin.h>
+#include <guacamole/client.h>
#include <guacamole/unicode.h>
-
-#ifdef ENABLE_WINPR
+#include <winpr/nt.h>
#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
#include <stddef.h>
+#include <stddef.h>
-void guac_rdpdr_fs_process_query_directory_info(guac_rdpdr_device* device,
- const char* entry_name, int file_id, int completion_id) {
+void guac_rdpdr_fs_process_query_directory_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ const char* entry_name, int entry_file_id) {
guac_rdp_fs_file* file;
@@ -49,16 +45,17 @@
(char*) utf16_entry_name, sizeof(utf16_entry_name));
/* Get file */
- file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+ file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, entry_file_id);
if (file == NULL)
return;
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
"%s: [file_id=%i (entry_name=\"%s\")]",
- __func__, file_id, entry_name);
+ __func__, entry_file_id, entry_name);
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- STATUS_SUCCESS, 4 + 64 + utf16_length + 2);
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS,
+ 4 + 64 + utf16_length + 2);
Stream_Write_UINT32(output_stream,
64 + utf16_length + 2); /* Length */
@@ -77,12 +74,13 @@
Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
Stream_Write(output_stream, "\0\0", 2);
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+ guac_rdp_common_svc_write(svc, output_stream);
}
-void guac_rdpdr_fs_process_query_full_directory_info(guac_rdpdr_device* device,
- const char* entry_name, int file_id, int completion_id) {
+void guac_rdpdr_fs_process_query_full_directory_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ const char* entry_name, int entry_file_id) {
guac_rdp_fs_file* file;
@@ -95,16 +93,17 @@
(char*) utf16_entry_name, sizeof(utf16_entry_name));
/* Get file */
- file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+ file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, entry_file_id);
if (file == NULL)
return;
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
"%s: [file_id=%i (entry_name=\"%s\")]",
- __func__, file_id, entry_name);
+ __func__, entry_file_id, entry_name);
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- STATUS_SUCCESS, 4 + 68 + utf16_length + 2);
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS,
+ 4 + 68 + utf16_length + 2);
Stream_Write_UINT32(output_stream,
68 + utf16_length + 2); /* Length */
@@ -124,12 +123,13 @@
Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
Stream_Write(output_stream, "\0\0", 2);
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+ guac_rdp_common_svc_write(svc, output_stream);
}
-void guac_rdpdr_fs_process_query_both_directory_info(guac_rdpdr_device* device,
- const char* entry_name, int file_id, int completion_id) {
+void guac_rdpdr_fs_process_query_both_directory_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ const char* entry_name, int entry_file_id) {
guac_rdp_fs_file* file;
@@ -142,16 +142,17 @@
(char*) utf16_entry_name, sizeof(utf16_entry_name));
/* Get file */
- file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+ file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, entry_file_id);
if (file == NULL)
return;
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
"%s: [file_id=%i (entry_name=\"%s\")]",
- __func__, file_id, entry_name);
+ __func__, entry_file_id, entry_name);
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- STATUS_SUCCESS, 4 + 69 + 24 + utf16_length + 2);
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS,
+ 4 + 69 + 24 + utf16_length + 2);
Stream_Write_UINT32(output_stream,
69 + 24 + utf16_length + 2); /* Length */
@@ -175,12 +176,13 @@
Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
Stream_Write(output_stream, "\0\0", 2);
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+ guac_rdp_common_svc_write(svc, output_stream);
}
-void guac_rdpdr_fs_process_query_names_info(guac_rdpdr_device* device,
- const char* entry_name, int file_id, int completion_id) {
+void guac_rdpdr_fs_process_query_names_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ const char* entry_name, int entry_file_id) {
guac_rdp_fs_file* file;
@@ -193,16 +195,17 @@
(char*) utf16_entry_name, sizeof(utf16_entry_name));
/* Get file */
- file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+ file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, entry_file_id);
if (file == NULL)
return;
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
"%s: [file_id=%i (entry_name=\"%s\")]",
- __func__, file_id, entry_name);
+ __func__, entry_file_id, entry_name);
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- STATUS_SUCCESS, 4 + 12 + utf16_length + 2);
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS,
+ 4 + 12 + utf16_length + 2);
Stream_Write_UINT32(output_stream,
12 + utf16_length + 2); /* Length */
@@ -213,7 +216,7 @@
Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
Stream_Write(output_stream, "\0\0", 2);
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+ guac_rdp_common_svc_write(svc, output_stream);
}
diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-dir-info.h b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-dir-info.h
new file mode 100644
index 0000000..2f259e9
--- /dev/null
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-dir-info.h
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_DIR_INFO_H
+#define GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_DIR_INFO_H
+
+/**
+ * Handlers for directory queries received over the RDPDR channel via the
+ * IRP_MJ_DIRECTORY_CONTROL major function and the IRP_MN_QUERY_DIRECTORY minor
+ * function.
+ *
+ * @file rdpdr-fs-messages-dir-info.h
+ */
+
+#include "channels/common-svc.h"
+#include "channels/rdpdr/rdpdr.h"
+
+#include <winpr/stream.h>
+
+/**
+ * Handler for Device I/O Requests which query information about the files
+ * within a directory.
+ *
+ * @param svc
+ * The guac_rdp_common_svc representing the static virtual channel being
+ * used for RDPDR.
+ *
+ * @param device
+ * The guac_rdpdr_device of the relevant device, as dictated by the
+ * deviceId field of the common RDPDR header within the received PDU.
+ * Within the guac_rdpdr_iorequest structure, the deviceId field is stored
+ * within device_id.
+ *
+ * @param iorequest
+ * The contents of the common RDPDR Device I/O Request header shared by all
+ * RDPDR devices.
+ *
+ * @param entry_name
+ * The filename of the file being queried.
+ *
+ * @param entry_file_id
+ * The ID of the file being queried.
+ */
+typedef void guac_rdpdr_directory_query_handler(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ const char* entry_name, int entry_file_id);
+
+/**
+ * Processes a query request for FileDirectoryInformation. From the
+ * documentation this is "defined as the file's name, time stamp, and size, or its
+ * attributes."
+ */
+guac_rdpdr_directory_query_handler guac_rdpdr_fs_process_query_directory_info;
+
+/**
+ * Processes a query request for FileFullDirectoryInformation. From the
+ * documentation, this is "defined as all the basic information, plus extended
+ * attribute size."
+ */
+guac_rdpdr_directory_query_handler guac_rdpdr_fs_process_query_full_directory_info;
+
+/**
+ * Processes a query request for FileBothDirectoryInformation. From the
+ * documentation, this absurdly-named request is "basic information plus
+ * extended attribute size and short name about a file or directory."
+ */
+guac_rdpdr_directory_query_handler guac_rdpdr_fs_process_query_both_directory_info;
+
+/**
+ * Processes a query request for FileNamesInformation. From the documentation,
+ * this is "detailed information on the names of files in a directory."
+ */
+guac_rdpdr_directory_query_handler guac_rdpdr_fs_process_query_names_info;
+
+#endif
+
diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.c b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.c
new file mode 100644
index 0000000..0f45303
--- /dev/null
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.c
@@ -0,0 +1,283 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "channels/rdpdr/rdpdr-fs-messages-file-info.h"
+#include "channels/rdpdr/rdpdr.h"
+#include "download.h"
+#include "fs.h"
+#include "unicode.h"
+
+#include <guacamole/client.h>
+#include <winpr/file.h>
+#include <winpr/nt.h>
+#include <winpr/stream.h>
+#include <winpr/wtypes.h>
+
+#include <stdint.h>
+#include <string.h>
+
+void guac_rdpdr_fs_process_query_basic_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ wStream* output_stream;
+ guac_rdp_fs_file* file;
+
+ /* Get file */
+ file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, iorequest->file_id);
+ if (file == NULL)
+ return;
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
+ iorequest->file_id);
+
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS, 40);
+
+ Stream_Write_UINT32(output_stream, 36);
+ Stream_Write_UINT64(output_stream, file->ctime); /* CreationTime */
+ Stream_Write_UINT64(output_stream, file->atime); /* LastAccessTime */
+ Stream_Write_UINT64(output_stream, file->mtime); /* LastWriteTime */
+ Stream_Write_UINT64(output_stream, file->mtime); /* ChangeTime */
+ Stream_Write_UINT32(output_stream, file->attributes); /* FileAttributes */
+
+ /* Reserved field must not be sent */
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_query_standard_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ wStream* output_stream;
+ guac_rdp_fs_file* file;
+ BOOL is_directory = FALSE;
+
+ /* Get file */
+ file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, iorequest->file_id);
+ if (file == NULL)
+ return;
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
+ iorequest->file_id);
+
+ if (file->attributes & FILE_ATTRIBUTE_DIRECTORY)
+ is_directory = TRUE;
+
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS, 26);
+
+ Stream_Write_UINT32(output_stream, 22);
+ Stream_Write_UINT64(output_stream, file->size); /* AllocationSize */
+ Stream_Write_UINT64(output_stream, file->size); /* EndOfFile */
+ Stream_Write_UINT32(output_stream, 1); /* NumberOfLinks */
+ Stream_Write_UINT8(output_stream, 0); /* DeletePending */
+ Stream_Write_UINT8(output_stream, is_directory); /* Directory */
+
+ /* Reserved field must not be sent */
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_query_attribute_tag_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ wStream* output_stream;
+ guac_rdp_fs_file* file;
+
+ /* Get file */
+ file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, iorequest->file_id);
+ if (file == NULL)
+ return;
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
+ iorequest->file_id);
+
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS, 12);
+
+ Stream_Write_UINT32(output_stream, 8);
+ Stream_Write_UINT32(output_stream, file->attributes); /* FileAttributes */
+ Stream_Write_UINT32(output_stream, 0); /* ReparseTag */
+
+ /* Reserved field must not be sent */
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_set_rename_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ int length, wStream* input_stream) {
+
+ int result;
+ int filename_length;
+ wStream* output_stream;
+ char destination_path[GUAC_RDP_FS_MAX_PATH];
+
+ /* Read structure */
+ Stream_Seek_UINT8(input_stream); /* ReplaceIfExists */
+ Stream_Seek_UINT8(input_stream); /* RootDirectory */
+ Stream_Read_UINT32(input_stream, filename_length); /* FileNameLength */
+
+ /* Convert name to UTF-8 */
+ guac_rdp_utf16_to_utf8(Stream_Pointer(input_stream), filename_length/2,
+ destination_path, sizeof(destination_path));
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]"
+ "destination_path=\"%s\"", __func__, iorequest->file_id,
+ destination_path);
+
+ /* If file moving to \Download folder, start stream, do not move */
+ if (strncmp(destination_path, "\\Download\\", 10) == 0) {
+
+ guac_rdp_fs_file* file;
+
+ /* Get file */
+ file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, iorequest->file_id);
+ if (file == NULL)
+ return;
+
+ /* Initiate download, pretend move succeeded */
+ guac_client_for_owner(svc->client, guac_rdp_download_to_user, file->absolute_path);
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS, 4);
+
+ }
+
+ /* Otherwise, rename as requested */
+ else {
+
+ result = guac_rdp_fs_rename((guac_rdp_fs*) device->data,
+ iorequest->file_id, destination_path);
+ if (result < 0)
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, guac_rdp_fs_get_status(result), 4);
+ else
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS, 4);
+
+ }
+
+ Stream_Write_UINT32(output_stream, length);
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_set_allocation_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ int length, wStream* input_stream) {
+
+ int result;
+ UINT64 size;
+ wStream* output_stream;
+
+ /* Read new size */
+ Stream_Read_UINT64(input_stream, size); /* AllocationSize */
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] "
+ "size=%" PRIu64, __func__, iorequest->file_id, (uint64_t) size);
+
+ /* Truncate file */
+ result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, iorequest->file_id, size);
+ if (result < 0)
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, guac_rdp_fs_get_status(result), 4);
+ else
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS, 4);
+
+ Stream_Write_UINT32(output_stream, length);
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_set_disposition_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ int length, wStream* input_stream) {
+
+ wStream* output_stream;
+
+ /* Delete file */
+ int result = guac_rdp_fs_delete((guac_rdp_fs*) device->data, iorequest->file_id);
+ if (result < 0)
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, guac_rdp_fs_get_status(result), 4);
+ else
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS, 4);
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
+ iorequest->file_id);
+
+ Stream_Write_UINT32(output_stream, length);
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_set_end_of_file_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ int length, wStream* input_stream) {
+
+ int result;
+ UINT64 size;
+ wStream* output_stream;
+
+ /* Read new size */
+ Stream_Read_UINT64(input_stream, size); /* AllocationSize */
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] "
+ "size=%" PRIu64, __func__, iorequest->file_id, (uint64_t) size);
+
+ /* Truncate file */
+ result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, iorequest->file_id, size);
+ if (result < 0)
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, guac_rdp_fs_get_status(result), 4);
+ else
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS, 4);
+
+ Stream_Write_UINT32(output_stream, length);
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_set_basic_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ int length, wStream* input_stream) {
+
+ wStream* output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS, 4);
+
+ /* Currently do nothing, just respond */
+ Stream_Write_UINT32(output_stream, length);
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] IGNORED",
+ __func__, iorequest->file_id);
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.h b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.h
new file mode 100644
index 0000000..f7a8784
--- /dev/null
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.h
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_FILE_INFO_H
+#define GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_FILE_INFO_H
+
+/**
+ * Handlers for file queries received over the RDPDR channel via the
+ * IRP_MJ_QUERY_INFORMATION major function.
+ *
+ * @file rdpdr-fs-messages-file-info.h
+ */
+
+#include "channels/common-svc.h"
+#include "channels/rdpdr/rdpdr.h"
+
+#include <winpr/stream.h>
+
+/**
+ * Handler for Device I/O Requests which set/update file information.
+ *
+ * @param svc
+ * The guac_rdp_common_svc representing the static virtual channel being
+ * used for RDPDR.
+ *
+ * @param device
+ * The guac_rdpdr_device of the relevant device, as dictated by the
+ * deviceId field of the common RDPDR header within the received PDU.
+ * Within the guac_rdpdr_iorequest structure, the deviceId field is stored
+ * within device_id.
+ *
+ * @param iorequest
+ * The contents of the common RDPDR Device I/O Request header shared by all
+ * RDPDR devices.
+ *
+ * @param length
+ * The length of the SetBuffer field of the I/O request, in bytes. Whether
+ * the SetBuffer field is applicable to a particular request, as well as
+ * the specific contents of that field, depend on the type of request.
+ *
+ * @param input_stream
+ * The remaining data within the received PDU, following the common RDPDR
+ * Device I/O Request header and length field. If the SetBuffer field is
+ * used for this request, the first byte of SetBuffer will be the first
+ * byte read from this stream.
+ */
+typedef void guac_rdpdr_set_information_request_handler(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ int length, wStream* input_stream);
+
+/**
+ * Processes a query for FileBasicInformation. From the documentation, this is
+ * "used to query a file for the times of creation, last access, last write,
+ * and change, in addition to file attribute information."
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_basic_info;
+
+/**
+ * Processes a query for FileStandardInformation. From the documentation, this
+ * is "used to query for file information such as allocation size, end-of-file
+ * position, and number of links."
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_standard_info;
+
+/**
+ * Processes a query for FileAttributeTagInformation. From the documentation
+ * this is "used to query for file attribute and reparse tag information."
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_attribute_tag_info;
+
+/**
+ * Process a set operation for FileRenameInformation. From the documentation,
+ * this operation is used to rename a file.
+ */
+guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_rename_info;
+
+/**
+ * Process a set operation for FileAllocationInformation. From the
+ * documentation, this operation is used to set a file's allocation size.
+ */
+guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_allocation_info;
+
+/**
+ * Process a set operation for FileDispositionInformation. From the
+ * documentation, this operation is used to mark a file for deletion.
+ */
+guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_disposition_info;
+
+/**
+ * Process a set operation for FileEndOfFileInformation. From the
+ * documentation, this operation is used "to set end-of-file information for
+ * a file."
+ */
+guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_end_of_file_info;
+
+/**
+ * Process a set operation for FileBasicInformation. From the documentation,
+ * this is "used to set file information such as the times of creation, last
+ * access, last write, and change, in addition to file attributes."
+ */
+guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_basic_info;
+
+#endif
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.c b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-vol-info.c
similarity index 60%
rename from src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.c
rename to src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-vol-info.c
index c107295..0ce4684 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.c
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-vol-info.c
@@ -17,33 +17,29 @@
* under the License.
*/
-#include "config.h"
+#include "channels/common-svc.h"
+#include "channels/rdpdr/rdpdr-fs-messages-vol-info.h"
+#include "channels/rdpdr/rdpdr-fs.h"
+#include "channels/rdpdr/rdpdr.h"
+#include "fs.h"
-#include "rdpdr_messages.h"
-#include "rdpdr_service.h"
-#include "rdp_fs.h"
-#include "rdp_status.h"
-
-#include <freerdp/utils/svc_plugin.h>
+#include <guacamole/client.h>
#include <guacamole/unicode.h>
-
-#ifdef ENABLE_WINPR
+#include <winpr/file.h>
+#include <winpr/io.h>
+#include <winpr/nt.h>
#include <winpr/stream.h>
#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-stream.h"
-#include "compat/winpr-wtypes.h"
-#endif
-void guac_rdpdr_fs_process_query_volume_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id) {
+void guac_rdpdr_fs_process_query_volume_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
wStream* output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_SUCCESS, 21 + GUAC_FILESYSTEM_LABEL_LENGTH);
+ iorequest->completion_id, STATUS_SUCCESS, 21 + GUAC_FILESYSTEM_LABEL_LENGTH);
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i]",
- __func__, file_id);
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
+ iorequest->file_id);
Stream_Write_UINT32(output_stream, 17 + GUAC_FILESYSTEM_LABEL_LENGTH);
Stream_Write_UINT64(output_stream, 0); /* VolumeCreationTime */
@@ -53,22 +49,22 @@
/* Reserved field must not be sent */
Stream_Write(output_stream, GUAC_FILESYSTEM_LABEL, GUAC_FILESYSTEM_LABEL_LENGTH);
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+ guac_rdp_common_svc_write(svc, output_stream);
}
-void guac_rdpdr_fs_process_query_size_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id) {
+void guac_rdpdr_fs_process_query_size_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
guac_rdp_fs_info info = {0};
guac_rdp_fs_get_info((guac_rdp_fs*) device->data, &info);
wStream* output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_SUCCESS, 28);
+ iorequest->completion_id, STATUS_SUCCESS, 28);
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i]",
- __func__, file_id);
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
+ iorequest->file_id);
Stream_Write_UINT32(output_stream, 24);
Stream_Write_UINT64(output_stream, info.blocks_total); /* TotalAllocationUnits */
@@ -76,39 +72,39 @@
Stream_Write_UINT32(output_stream, 1); /* SectorsPerAllocationUnit */
Stream_Write_UINT32(output_stream, info.block_size); /* BytesPerSector */
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+ guac_rdp_common_svc_write(svc, output_stream);
}
-void guac_rdpdr_fs_process_query_device_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id) {
+void guac_rdpdr_fs_process_query_device_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
wStream* output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_SUCCESS, 12);
+ iorequest->completion_id, STATUS_SUCCESS, 12);
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i]",
- __func__, file_id);
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
+ iorequest->file_id);
Stream_Write_UINT32(output_stream, 8);
Stream_Write_UINT32(output_stream, FILE_DEVICE_DISK); /* DeviceType */
Stream_Write_UINT32(output_stream, 0); /* Characteristics */
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+ guac_rdp_common_svc_write(svc, output_stream);
}
-void guac_rdpdr_fs_process_query_attribute_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id) {
+void guac_rdpdr_fs_process_query_attribute_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
int name_len = guac_utf8_strlen(device->device_name);
wStream* output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_SUCCESS, 16 + name_len);
+ iorequest->completion_id, STATUS_SUCCESS, 16 + name_len);
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i]",
- __func__, file_id);
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
+ iorequest->file_id);
Stream_Write_UINT32(output_stream, 12 + name_len);
Stream_Write_UINT32(output_stream,
@@ -119,22 +115,22 @@
Stream_Write_UINT32(output_stream, name_len);
Stream_Write(output_stream, device->device_name, name_len);
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+ guac_rdp_common_svc_write(svc, output_stream);
}
-void guac_rdpdr_fs_process_query_full_size_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id) {
+void guac_rdpdr_fs_process_query_full_size_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
guac_rdp_fs_info info = {0};
guac_rdp_fs_get_info((guac_rdp_fs*) device->data, &info);
wStream* output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_SUCCESS, 36);
+ iorequest->completion_id, STATUS_SUCCESS, 36);
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i]",
- __func__, file_id);
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
+ iorequest->file_id);
Stream_Write_UINT32(output_stream, 32);
Stream_Write_UINT64(output_stream, info.blocks_total); /* TotalAllocationUnits */
@@ -143,7 +139,7 @@
Stream_Write_UINT32(output_stream, 1); /* SectorsPerAllocationUnit */
Stream_Write_UINT32(output_stream, info.block_size); /* BytesPerSector */
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+ guac_rdp_common_svc_write(svc, output_stream);
}
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.h b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-vol-info.h
similarity index 61%
rename from src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.h
rename to src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-vol-info.h
index 0c82a2f..b8c9522 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.h
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-vol-info.h
@@ -17,59 +17,47 @@
* under the License.
*/
-
-#ifndef __GUAC_RDPDR_FS_MESSAGES_VOL_INFO_H
-#define __GUAC_RDPDR_FS_MESSAGES_VOL_INFO_H
+#ifndef GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_VOL_INFO_H
+#define GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_VOL_INFO_H
/**
* Handlers for directory queries received over the RDPDR channel via the
* IRP_MJ_DIRECTORY_CONTROL major function and the IRP_MN_QUERY_DIRECTORY minor
* function.
*
- * @file rdpdr_fs_messages_vol_info.h
+ * @file rdpdr-fs-messages-vol-info.h
*/
-#include "config.h"
+#include "channels/rdpdr/rdpdr.h"
-#include "rdpdr_service.h"
-
-#ifdef ENABLE_WINPR
#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
/**
* Processes a query request for FileFsVolumeInformation. According to the
* documentation, this is "used to query information for a volume on which a
* file system is mounted."
*/
-void guac_rdpdr_fs_process_query_volume_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_volume_info;
/**
* Processes a query request for FileFsSizeInformation.
*/
-void guac_rdpdr_fs_process_query_size_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_size_info;
/**
* Processes a query request for FileFsAttributeInformation.
*/
-void guac_rdpdr_fs_process_query_attribute_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_attribute_info;
/**
* Processes a query request for FileFsFullSizeInformation.
*/
-void guac_rdpdr_fs_process_query_full_size_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_full_size_info;
/**
* Processes a query request for FileFsDeviceInformation.
*/
-void guac_rdpdr_fs_process_query_device_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_device_info;
#endif
diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c
new file mode 100644
index 0000000..529eea5
--- /dev/null
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c
@@ -0,0 +1,516 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "channels/common-svc.h"
+#include "channels/rdpdr/rdpdr-fs-messages-dir-info.h"
+#include "channels/rdpdr/rdpdr-fs-messages-file-info.h"
+#include "channels/rdpdr/rdpdr-fs-messages-vol-info.h"
+#include "channels/rdpdr/rdpdr-fs-messages.h"
+#include "channels/rdpdr/rdpdr.h"
+#include "download.h"
+#include "fs.h"
+#include "unicode.h"
+
+#include <freerdp/channels/rdpdr.h>
+#include <guacamole/client.h>
+#include <winpr/nt.h>
+#include <winpr/stream.h>
+#include <winpr/wtypes.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+void guac_rdpdr_fs_process_create(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ wStream* output_stream;
+ int file_id;
+
+ int desired_access, file_attributes;
+ int create_disposition, create_options, path_length;
+ char path[GUAC_RDP_FS_MAX_PATH];
+
+ /* Read "create" information */
+ Stream_Read_UINT32(input_stream, desired_access);
+ Stream_Seek_UINT64(input_stream); /* allocation size */
+ Stream_Read_UINT32(input_stream, file_attributes);
+ Stream_Seek_UINT32(input_stream); /* shared access */
+ Stream_Read_UINT32(input_stream, create_disposition);
+ Stream_Read_UINT32(input_stream, create_options);
+ Stream_Read_UINT32(input_stream, path_length);
+
+ /* Convert path to UTF-8 */
+ guac_rdp_utf16_to_utf8(Stream_Pointer(input_stream), path_length/2 - 1,
+ path, sizeof(path));
+
+ /* Open file */
+ file_id = guac_rdp_fs_open((guac_rdp_fs*) device->data, path,
+ desired_access, file_attributes,
+ create_disposition, create_options);
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
+ "%s: [file_id=%i] "
+ "desired_access=0x%x, file_attributes=0x%x, "
+ "create_disposition=0x%x, create_options=0x%x, path=\"%s\"",
+ __func__, file_id,
+ desired_access, file_attributes,
+ create_disposition, create_options, path);
+
+ /* If an error occurred, notify server */
+ if (file_id < 0) {
+ guac_client_log(svc->client, GUAC_LOG_ERROR,
+ "File open refused (%i): \"%s\"", file_id, path);
+
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, guac_rdp_fs_get_status(file_id), 5);
+ Stream_Write_UINT32(output_stream, 0); /* fileId */
+ Stream_Write_UINT8(output_stream, 0); /* information */
+ }
+
+ /* Otherwise, open succeeded */
+ else {
+
+ guac_rdp_fs_file* file;
+
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS, 5);
+ Stream_Write_UINT32(output_stream, file_id); /* fileId */
+ Stream_Write_UINT8(output_stream, 0); /* information */
+
+ /* Create \Download if it doesn't exist */
+ file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+ if (file != NULL && strcmp(file->absolute_path, "\\") == 0) {
+ int download_id =
+ guac_rdp_fs_open((guac_rdp_fs*) device->data, "\\Download",
+ GENERIC_READ, 0, FILE_OPEN_IF, FILE_DIRECTORY_FILE);
+
+ if (download_id >= 0)
+ guac_rdp_fs_close((guac_rdp_fs*) device->data, download_id);
+ }
+
+ }
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_read(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ UINT32 length;
+ UINT64 offset;
+ char* buffer;
+ int bytes_read;
+
+ wStream* output_stream;
+
+ /* Read packet */
+ Stream_Read_UINT32(input_stream, length);
+ Stream_Read_UINT64(input_stream, offset);
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
+ "%s: [file_id=%i] length=%i, offset=%" PRIu64,
+ __func__, iorequest->file_id, length, (uint64_t) offset);
+
+ /* Ensure buffer size does not exceed a safe maximum */
+ if (length > GUAC_RDP_MAX_READ_BUFFER)
+ length = GUAC_RDP_MAX_READ_BUFFER;
+
+ /* Allocate buffer */
+ buffer = malloc(length);
+
+ /* Attempt read */
+ bytes_read = guac_rdp_fs_read((guac_rdp_fs*) device->data,
+ iorequest->file_id, offset, buffer, length);
+
+ /* If error, return invalid parameter */
+ if (bytes_read < 0) {
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, guac_rdp_fs_get_status(bytes_read), 4);
+ Stream_Write_UINT32(output_stream, 0); /* Length */
+ }
+
+ /* Otherwise, send bytes read */
+ else {
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS, 4+bytes_read);
+ Stream_Write_UINT32(output_stream, bytes_read); /* Length */
+ Stream_Write(output_stream, buffer, bytes_read); /* ReadData */
+ }
+
+ guac_rdp_common_svc_write(svc, output_stream);
+ free(buffer);
+
+}
+
+void guac_rdpdr_fs_process_write(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ UINT32 length;
+ UINT64 offset;
+ int bytes_written;
+
+ wStream* output_stream;
+
+ /* Read packet */
+ Stream_Read_UINT32(input_stream, length);
+ Stream_Read_UINT64(input_stream, offset);
+ Stream_Seek(input_stream, 20); /* Padding */
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
+ "%s: [file_id=%i] length=%i, offset=%" PRIu64,
+ __func__, iorequest->file_id, length, (uint64_t) offset);
+
+ /* Attempt write */
+ bytes_written = guac_rdp_fs_write((guac_rdp_fs*) device->data,
+ iorequest->file_id, offset, Stream_Pointer(input_stream), length);
+
+ /* If error, return invalid parameter */
+ if (bytes_written < 0) {
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, guac_rdp_fs_get_status(bytes_written), 5);
+ Stream_Write_UINT32(output_stream, 0); /* Length */
+ Stream_Write_UINT8(output_stream, 0); /* Padding */
+ }
+
+ /* Otherwise, send success */
+ else {
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS, 5);
+ Stream_Write_UINT32(output_stream, bytes_written); /* Length */
+ Stream_Write_UINT8(output_stream, 0); /* Padding */
+ }
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_close(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ wStream* output_stream;
+ guac_rdp_fs_file* file;
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]",
+ __func__, iorequest->file_id);
+
+ /* Get file */
+ file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, iorequest->file_id);
+ if (file == NULL)
+ return;
+
+ /* If file was written to, and it's in the \Download folder, start stream */
+ if (file->bytes_written > 0 &&
+ strncmp(file->absolute_path, "\\Download\\", 10) == 0) {
+ guac_client_for_owner(svc->client, guac_rdp_download_to_user, file->absolute_path);
+ guac_rdp_fs_delete((guac_rdp_fs*) device->data, iorequest->file_id);
+ }
+
+ /* Close file */
+ guac_rdp_fs_close((guac_rdp_fs*) device->data, iorequest->file_id);
+
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_SUCCESS, 4);
+ Stream_Write(output_stream, "\0\0\0\0", 4); /* Padding */
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_volume_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ int fs_information_class;
+
+ Stream_Read_UINT32(input_stream, fs_information_class);
+
+ /* Dispatch to appropriate class-specific handler */
+ switch (fs_information_class) {
+
+ case FileFsVolumeInformation:
+ guac_rdpdr_fs_process_query_volume_info(svc, device, iorequest, input_stream);
+ break;
+
+ case FileFsSizeInformation:
+ guac_rdpdr_fs_process_query_size_info(svc, device, iorequest, input_stream);
+ break;
+
+ case FileFsDeviceInformation:
+ guac_rdpdr_fs_process_query_device_info(svc, device, iorequest, input_stream);
+ break;
+
+ case FileFsAttributeInformation:
+ guac_rdpdr_fs_process_query_attribute_info(svc, device, iorequest, input_stream);
+ break;
+
+ case FileFsFullSizeInformation:
+ guac_rdpdr_fs_process_query_full_size_info(svc, device, iorequest, input_stream);
+ break;
+
+ default:
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
+ "Unknown volume information class: 0x%x", fs_information_class);
+ }
+
+}
+
+void guac_rdpdr_fs_process_file_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ int fs_information_class;
+
+ Stream_Read_UINT32(input_stream, fs_information_class);
+
+ /* Dispatch to appropriate class-specific handler */
+ switch (fs_information_class) {
+
+ case FileBasicInformation:
+ guac_rdpdr_fs_process_query_basic_info(svc, device, iorequest, input_stream);
+ break;
+
+ case FileStandardInformation:
+ guac_rdpdr_fs_process_query_standard_info(svc, device, iorequest, input_stream);
+ break;
+
+ case FileAttributeTagInformation:
+ guac_rdpdr_fs_process_query_attribute_tag_info(svc, device, iorequest, input_stream);
+ break;
+
+ default:
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
+ "Unknown file information class: 0x%x", fs_information_class);
+ }
+
+}
+
+void guac_rdpdr_fs_process_set_volume_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ wStream* output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_NOT_SUPPORTED, 0);
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
+ "%s: [file_id=%i] Set volume info not supported",
+ __func__, iorequest->file_id);
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_set_file_info(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ int fs_information_class;
+ int length;
+
+ Stream_Read_UINT32(input_stream, fs_information_class);
+ Stream_Read_UINT32(input_stream, length); /* Length */
+ Stream_Seek(input_stream, 24); /* Padding */
+
+ /* Dispatch to appropriate class-specific handler */
+ switch (fs_information_class) {
+
+ case FileBasicInformation:
+ guac_rdpdr_fs_process_set_basic_info(svc, device, iorequest, length, input_stream);
+ break;
+
+ case FileEndOfFileInformation:
+ guac_rdpdr_fs_process_set_end_of_file_info(svc, device, iorequest, length, input_stream);
+ break;
+
+ case FileDispositionInformation:
+ guac_rdpdr_fs_process_set_disposition_info(svc, device, iorequest, length, input_stream);
+ break;
+
+ case FileRenameInformation:
+ guac_rdpdr_fs_process_set_rename_info(svc, device, iorequest, length, input_stream);
+ break;
+
+ case FileAllocationInformation:
+ guac_rdpdr_fs_process_set_allocation_info(svc, device, iorequest, length, input_stream);
+ break;
+
+ default:
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
+ "Unknown file information class: 0x%x",
+ fs_information_class);
+ }
+
+}
+
+void guac_rdpdr_fs_process_device_control(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ wStream* output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_INVALID_PARAMETER, 4);
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] IGNORED",
+ __func__, iorequest->file_id);
+
+ /* No content for now */
+ Stream_Write_UINT32(output_stream, 0);
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_notify_change_directory(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] Not "
+ "implemented", __func__, iorequest->file_id);
+
+}
+
+void guac_rdpdr_fs_process_query_directory(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ wStream* output_stream;
+
+ guac_rdp_fs_file* file;
+ int fs_information_class, initial_query;
+ int path_length;
+
+ const char* entry_name;
+
+ /* Get file */
+ file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, iorequest->file_id);
+ if (file == NULL)
+ return;
+
+ /* Read main header */
+ Stream_Read_UINT32(input_stream, fs_information_class);
+ Stream_Read_UINT8(input_stream, initial_query);
+ Stream_Read_UINT32(input_stream, path_length);
+
+ /* If this is the first query, the path is included after padding */
+ if (initial_query) {
+
+ Stream_Seek(input_stream, 23); /* Padding */
+
+ /* Convert path to UTF-8 */
+ guac_rdp_utf16_to_utf8(Stream_Pointer(input_stream), path_length/2 - 1,
+ file->dir_pattern, sizeof(file->dir_pattern));
+
+ }
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] "
+ "initial_query=%i, dir_pattern=\"%s\"", __func__,
+ iorequest->file_id, initial_query, file->dir_pattern);
+
+ /* Find first matching entry in directory */
+ while ((entry_name = guac_rdp_fs_read_dir((guac_rdp_fs*) device->data,
+ iorequest->file_id)) != NULL) {
+
+ /* Convert to absolute path */
+ char entry_path[GUAC_RDP_FS_MAX_PATH];
+ if (guac_rdp_fs_convert_path(file->absolute_path,
+ entry_name, entry_path) == 0) {
+
+ int entry_file_id;
+
+ /* Pattern defined and match fails, continue with next file */
+ if (guac_rdp_fs_matches(entry_path, file->dir_pattern))
+ continue;
+
+ /* Open directory entry */
+ entry_file_id = guac_rdp_fs_open((guac_rdp_fs*) device->data,
+ entry_path, FILE_READ_DATA, 0, FILE_OPEN, 0);
+
+ if (entry_file_id >= 0) {
+
+ /* Dispatch to appropriate class-specific handler */
+ switch (fs_information_class) {
+
+ case FileDirectoryInformation:
+ guac_rdpdr_fs_process_query_directory_info(svc, device,
+ iorequest, entry_name, entry_file_id);
+ break;
+
+ case FileFullDirectoryInformation:
+ guac_rdpdr_fs_process_query_full_directory_info(svc,
+ device, iorequest, entry_name, entry_file_id);
+ break;
+
+ case FileBothDirectoryInformation:
+ guac_rdpdr_fs_process_query_both_directory_info(svc,
+ device, iorequest, entry_name, entry_file_id);
+ break;
+
+ case FileNamesInformation:
+ guac_rdpdr_fs_process_query_names_info(svc, device,
+ iorequest, entry_name, entry_file_id);
+ break;
+
+ default:
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
+ "Unknown dir information class: 0x%x",
+ fs_information_class);
+ }
+
+ guac_rdp_fs_close((guac_rdp_fs*) device->data, entry_file_id);
+ return;
+
+ } /* end if file exists */
+ } /* end if path valid */
+ } /* end if entry exists */
+
+ /*
+ * Handle errors as a lack of files.
+ */
+
+ output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_NO_MORE_FILES, 5);
+
+ Stream_Write_UINT32(output_stream, 0); /* Length */
+ Stream_Write_UINT8(output_stream, 0); /* Padding */
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_lock_control(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ wStream* output_stream = guac_rdpdr_new_io_completion(device,
+ iorequest->completion_id, STATUS_NOT_SUPPORTED, 5);
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
+ "%s: [file_id=%i] Lock not supported",
+ __func__, iorequest->file_id);
+
+ Stream_Zero(output_stream, 5); /* Padding */
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.h b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.h
new file mode 100644
index 0000000..63fcbb9
--- /dev/null
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.h
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_H
+#define GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_H
+
+/**
+ * Handlers for core drive I/O requests. Requests handled here may be simple
+ * messages handled directly, or more complex multi-type messages handled
+ * elsewhere.
+ *
+ * @file rdpdr-fs-messages.h
+ */
+
+#include "channels/rdpdr/rdpdr.h"
+
+#include <winpr/stream.h>
+
+/**
+ * Handles a Server Create Drive Request. Despite its name, this request opens
+ * a file.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_create;
+
+/**
+ * Handles a Server Close Drive Request. This request closes an open file.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_close;
+
+/**
+ * Handles a Server Drive Read Request. This request reads from a file.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_read;
+
+/**
+ * Handles a Server Drive Write Request. This request writes to a file.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_write;
+
+/**
+ * Handles a Server Drive Control Request. This request handles one of any
+ * number of Windows FSCTL_* control functions.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_device_control;
+
+/**
+ * Handles a Server Drive Query Volume Information Request. This request
+ * queries information about the redirected volume (drive). This request
+ * has several query types which have their own handlers defined in a
+ * separate file.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_volume_info;
+
+/**
+ * Handles a Server Drive Set Volume Information Request. Currently, this
+ * RDPDR implementation does not support setting of volume information.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_set_volume_info;
+
+/**
+ * Handles a Server Drive Query Information Request. This request queries
+ * information about a specific file. This request has several query types
+ * which have their own handlers defined in a separate file.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_file_info;
+
+/**
+ * Handles a Server Drive Set Information Request. This request sets
+ * information about a specific file. Currently, this RDPDR implementation does
+ * not support setting of file information.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_set_file_info;
+
+/**
+ * Handles a Server Drive Query Directory Request. This request queries
+ * information about a specific directory. This request has several query types
+ * which have their own handlers defined in a separate file.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_directory;
+
+/**
+ * Handles a Server Drive NotifyChange Directory Request. This request requests
+ * directory change notification.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_notify_change_directory;
+
+/**
+ * Handles a Server Drive Lock Control Request. This request locks or unlocks
+ * portions of a file.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_lock_control;
+
+#endif
+
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.c b/src/protocols/rdp/channels/rdpdr/rdpdr-fs.c
similarity index 61%
rename from src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.c
rename to src/protocols/rdp/channels/rdpdr/rdpdr-fs.c
index e064d4d..ed67ac6 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.c
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs.c
@@ -17,119 +17,116 @@
* under the License.
*/
-
-#include "config.h"
-
+#include "channels/rdpdr/rdpdr-fs.h"
+#include "channels/rdpdr/rdpdr-fs-messages.h"
+#include "channels/rdpdr/rdpdr.h"
#include "rdp.h"
-#include "rdpdr_fs_messages.h"
-#include "rdpdr_messages.h"
-#include "rdpdr_service.h"
-#include <freerdp/utils/svc_plugin.h>
+#include <freerdp/channels/rdpdr.h>
+#include <freerdp/settings.h>
#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-#include <guacamole/socket.h>
#include <guacamole/unicode.h>
-
-#ifdef ENABLE_WINPR
#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-static void guac_rdpdr_device_fs_iorequest_handler(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id, int major_func, int minor_func) {
+#include <stddef.h>
- switch (major_func) {
+void guac_rdpdr_device_fs_iorequest_handler(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
+
+ switch (iorequest->major_func) {
/* File open */
case IRP_MJ_CREATE:
- guac_rdpdr_fs_process_create(device, input_stream, completion_id);
+ guac_rdpdr_fs_process_create(svc, device, iorequest, input_stream);
break;
/* File close */
case IRP_MJ_CLOSE:
- guac_rdpdr_fs_process_close(device, input_stream, file_id, completion_id);
+ guac_rdpdr_fs_process_close(svc, device, iorequest, input_stream);
break;
/* File read */
case IRP_MJ_READ:
- guac_rdpdr_fs_process_read(device, input_stream, file_id, completion_id);
+ guac_rdpdr_fs_process_read(svc, device, iorequest, input_stream);
break;
/* File write */
case IRP_MJ_WRITE:
- guac_rdpdr_fs_process_write(device, input_stream, file_id, completion_id);
+ guac_rdpdr_fs_process_write(svc, device, iorequest, input_stream);
break;
/* Device control request (Windows FSCTL_ control codes) */
case IRP_MJ_DEVICE_CONTROL:
- guac_rdpdr_fs_process_device_control(device, input_stream, file_id, completion_id);
+ guac_rdpdr_fs_process_device_control(svc, device, iorequest, input_stream);
break;
/* Query volume (drive) information */
case IRP_MJ_QUERY_VOLUME_INFORMATION:
- guac_rdpdr_fs_process_volume_info(device, input_stream, file_id, completion_id);
+ guac_rdpdr_fs_process_volume_info(svc, device, iorequest, input_stream);
break;
/* Set volume (drive) information */
case IRP_MJ_SET_VOLUME_INFORMATION:
- guac_rdpdr_fs_process_set_volume_info(device, input_stream, file_id, completion_id);
+ guac_rdpdr_fs_process_set_volume_info(svc, device, iorequest, input_stream);
break;
/* Query file information */
case IRP_MJ_QUERY_INFORMATION:
- guac_rdpdr_fs_process_file_info(device, input_stream, file_id, completion_id);
+ guac_rdpdr_fs_process_file_info(svc, device, iorequest, input_stream);
break;
/* Set file information */
case IRP_MJ_SET_INFORMATION:
- guac_rdpdr_fs_process_set_file_info(device, input_stream, file_id, completion_id);
+ guac_rdpdr_fs_process_set_file_info(svc, device, iorequest, input_stream);
break;
case IRP_MJ_DIRECTORY_CONTROL:
/* Enumerate directory contents */
- if (minor_func == IRP_MN_QUERY_DIRECTORY)
- guac_rdpdr_fs_process_query_directory(device, input_stream, file_id, completion_id);
+ if (iorequest->minor_func == IRP_MN_QUERY_DIRECTORY)
+ guac_rdpdr_fs_process_query_directory(svc, device, iorequest,
+ input_stream);
/* Request notification of changes to directory */
- else if (minor_func == IRP_MN_NOTIFY_CHANGE_DIRECTORY)
- guac_rdpdr_fs_process_notify_change_directory(device, input_stream,
- file_id, completion_id);
+ else if (iorequest->minor_func == IRP_MN_NOTIFY_CHANGE_DIRECTORY)
+ guac_rdpdr_fs_process_notify_change_directory(svc, device,
+ iorequest, input_stream);
break;
/* Lock/unlock portions of a file */
case IRP_MJ_LOCK_CONTROL:
- guac_rdpdr_fs_process_lock_control(device, input_stream, file_id, completion_id);
+ guac_rdpdr_fs_process_lock_control(svc, device, iorequest, input_stream);
break;
default:
- guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
+ guac_client_log(svc->client, GUAC_LOG_DEBUG,
"Unknown filesystem I/O request function: 0x%x/0x%x",
- major_func, minor_func);
+ iorequest->major_func, iorequest->minor_func);
}
}
-static void guac_rdpdr_device_fs_free_handler(guac_rdpdr_device* device) {
+void guac_rdpdr_device_fs_free_handler(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device) {
Stream_Free(device->device_announce, 1);
}
-void guac_rdpdr_register_fs(guac_rdpdrPlugin* rdpdr, char* drive_name) {
+void guac_rdpdr_register_fs(guac_rdp_common_svc* svc, char* drive_name) {
- guac_client* client = rdpdr->client;
+ guac_client* client = svc->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data;
int id = rdpdr->devices_registered++;
/* Get new device */
guac_rdpdr_device* device = &(rdpdr->devices[id]);
/* Init device */
- device->rdpdr = rdpdr;
device->device_id = id;
device->device_name = drive_name;
int device_name_len = guac_utf8_strlen(device->device_name);
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.h b/src/protocols/rdp/channels/rdpdr/rdpdr-fs.h
similarity index 69%
rename from src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.h
rename to src/protocols/rdp/channels/rdpdr/rdpdr-fs.h
index f990806..ae90045 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.h
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs.h
@@ -17,38 +17,46 @@
* under the License.
*/
-
-#ifndef __GUAC_RDPDR_FS_H
-#define __GUAC_RDPDR_FS_H
+#ifndef GUAC_RDP_RDPDR_FS_H
+#define GUAC_RDP_RDPDR_FS_H
/**
* Functions and macros specific to filesystem handling and initialization
- * independent of RDP. The functions here may deal with the RDPDR device
+ * independent of RDP. The functions here may deal with the RDPDR device
* directly, but their semantics must not deal with RDP protocol messaging.
* Functions here represent a virtual Windows-style filesystem on top of UNIX
* system calls and structures, using the guac_rdpdr_device structure as a home
* for common data.
*
- * @file rdpdr_fs.h
+ * @file rdpdr-fs.h
*/
-#include "config.h"
-
-#include "rdpdr_service.h"
+#include "channels/common-svc.h"
+#include "channels/rdpdr/rdpdr.h"
#include <guacamole/pool.h>
/**
+ * The UTF-16 string that should be sent as the label of the filesystem.
+ */
+#define GUAC_FILESYSTEM_LABEL "G\0U\0A\0C\0F\0I\0L\0E\0"
+
+/**
+ * The size of GUAC_FILESYSTEM_LABEL in bytes.
+ */
+#define GUAC_FILESYSTEM_LABEL_LENGTH 16
+
+/**
* Registers a new filesystem device within the RDPDR plugin. This must be done
* before RDPDR connection finishes.
*
- * @param rdpdr
- * The RDP device redirection plugin with which to register the device.
+ * @param svc
+ * The static virtual channel instance being used for RDPDR.
*
* @param drive_name
* The name of the redirected drive to display in the RDP connection.
*/
-void guac_rdpdr_register_fs(guac_rdpdrPlugin* rdpdr, char* drive_name);
+void guac_rdpdr_register_fs(guac_rdp_common_svc* svc, char* drive_name);
#endif
diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-messages.c b/src/protocols/rdp/channels/rdpdr/rdpdr-messages.c
new file mode 100644
index 0000000..28a27e6
--- /dev/null
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-messages.c
@@ -0,0 +1,349 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "channels/rdpdr/rdpdr-messages.h"
+#include "channels/rdpdr/rdpdr.h"
+#include "rdp.h"
+#include "settings.h"
+
+#include <freerdp/channels/rdpdr.h>
+#include <guacamole/client.h>
+#include <winpr/stream.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Sends a Client Announce Reply message. The Client Announce Reply message is
+ * required to be sent in response to the Server Announce Request message. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/d6fe6d1b-c145-4a6f-99aa-4fe3cdcea398
+ *
+ * @param svc
+ * The guac_rdp_common_svc representing the static virtual channel being
+ * used for RDPDR.
+ *
+ * @param major
+ * The major version of the RDPDR protocol in use. This value must always
+ * be 1.
+ *
+ * @param minor
+ * The minor version of the RDPDR protocol in use. This value must be
+ * either 2, 5, 10, 12, or 13.
+ *
+ * @param client_id
+ * The client ID received in the Server Announce Request, or a randomly
+ * generated ID.
+ */
+static void guac_rdpdr_send_client_announce_reply(guac_rdp_common_svc* svc,
+ unsigned int major, unsigned int minor, unsigned int client_id) {
+
+ wStream* output_stream = Stream_New(NULL, 12);
+
+ /* Write header */
+ Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
+ Stream_Write_UINT16(output_stream, PAKID_CORE_CLIENTID_CONFIRM);
+
+ /* Write content */
+ Stream_Write_UINT16(output_stream, major);
+ Stream_Write_UINT16(output_stream, minor);
+ Stream_Write_UINT32(output_stream, client_id);
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+/**
+ * Sends a Client Name Request message. The Client Name Request message is used
+ * by the client to announce its own name. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/902497f1-3b1c-4aee-95f8-1668f9b7b7d2
+ *
+ * @param svc
+ * The guac_rdp_common_svc representing the static virtual channel being
+ * used for RDPDR.
+ *
+ * @param name
+ * The name that should be used for the client.
+ */
+static void guac_rdpdr_send_client_name_request(guac_rdp_common_svc* svc,
+ const char* name) {
+
+ int name_bytes = strlen(name) + 1;
+ wStream* output_stream = Stream_New(NULL, 16 + name_bytes);
+
+ /* Write header */
+ Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
+ Stream_Write_UINT16(output_stream, PAKID_CORE_CLIENT_NAME);
+
+ /* Write content */
+ Stream_Write_UINT32(output_stream, 0); /* ASCII */
+ Stream_Write_UINT32(output_stream, 0); /* 0 required by RDPDR spec */
+ Stream_Write_UINT32(output_stream, name_bytes);
+ Stream_Write(output_stream, name, name_bytes);
+
+ guac_rdp_common_svc_write(svc, output_stream);
+
+}
+
+/**
+ * Sends a Client Core Capability Response message. The Client Core Capability
+ * Response message is used to announce the client's capabilities, in response
+ * to receiving the server's capabilities via a Server Core Capability Request.
+ * See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/f513bf87-cca0-488a-ac5c-18cf18f4a7e1
+ *
+ * @param svc
+ * The guac_rdp_common_svc representing the static virtual channel being
+ * used for RDPDR.
+ */
+static void guac_rdpdr_send_client_capability(guac_rdp_common_svc* svc) {
+
+ wStream* output_stream = Stream_New(NULL, 256);
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "Sending capabilities...");
+
+ /* Write header */
+ Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
+ Stream_Write_UINT16(output_stream, PAKID_CORE_CLIENT_CAPABILITY);
+
+ /* Capability count + padding */
+ Stream_Write_UINT16(output_stream, 3);
+ Stream_Write_UINT16(output_stream, 0); /* Padding */
+
+ /* General capability header */
+ Stream_Write_UINT16(output_stream, CAP_GENERAL_TYPE);
+ Stream_Write_UINT16(output_stream, 44);
+ Stream_Write_UINT32(output_stream, GENERAL_CAPABILITY_VERSION_02);
+
+ /* General capability data */
+ Stream_Write_UINT32(output_stream, GUAC_OS_TYPE); /* osType - required to be ignored */
+ Stream_Write_UINT32(output_stream, 0); /* osVersion */
+ Stream_Write_UINT16(output_stream, 1); /* protocolMajor - must be set to 1 */
+ Stream_Write_UINT16(output_stream, RDPDR_MINOR_RDP_VERSION_5_2); /* protocolMinor */
+ Stream_Write_UINT32(output_stream, 0xFFFF); /* ioCode1 */
+ Stream_Write_UINT32(output_stream, 0); /* ioCode2 */
+ Stream_Write_UINT32(output_stream,
+ RDPDR_DEVICE_REMOVE_PDUS
+ | RDPDR_CLIENT_DISPLAY_NAME_PDU
+ | RDPDR_USER_LOGGEDON_PDU); /* extendedPDU */
+ Stream_Write_UINT32(output_stream, 0); /* extraFlags1 */
+ Stream_Write_UINT32(output_stream, 0); /* extraFlags2 */
+ Stream_Write_UINT32(output_stream, 0); /* SpecialTypeDeviceCap */
+
+ /* Printer support header */
+ Stream_Write_UINT16(output_stream, CAP_PRINTER_TYPE);
+ Stream_Write_UINT16(output_stream, 8);
+ Stream_Write_UINT32(output_stream, PRINT_CAPABILITY_VERSION_01);
+
+ /* Drive support header */
+ Stream_Write_UINT16(output_stream, CAP_DRIVE_TYPE);
+ Stream_Write_UINT16(output_stream, 8);
+ Stream_Write_UINT32(output_stream, DRIVE_CAPABILITY_VERSION_02);
+
+ guac_rdp_common_svc_write(svc, output_stream);
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "Capabilities sent.");
+
+}
+
+/**
+ * Sends a Client Device List Announce Request message. The Client Device List
+ * Announce Request message is used by the client to enumerate all devices
+ * which should be made available within the RDP session via RDPDR. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/10ef9ada-cba2-4384-ab60-7b6290ed4a9a
+ *
+ * @param svc
+ * The guac_rdp_common_svc representing the static virtual channel being
+ * used for RDPDR.
+ */
+static void guac_rdpdr_send_client_device_list_announce_request(guac_rdp_common_svc* svc) {
+
+ guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data;
+
+ /* Calculate number of bytes needed for the stream */
+ int streamBytes = 16;
+ for (int i=0; i < rdpdr->devices_registered; i++)
+ streamBytes += rdpdr->devices[i].device_announce_len;
+
+ /* Allocate the stream */
+ wStream* output_stream = Stream_New(NULL, streamBytes);
+
+ /* Write header */
+ Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
+ Stream_Write_UINT16(output_stream, PAKID_CORE_DEVICELIST_ANNOUNCE);
+
+ /* Get the stream for each of the devices. */
+ Stream_Write_UINT32(output_stream, rdpdr->devices_registered);
+ for (int i=0; i<rdpdr->devices_registered; i++) {
+
+ Stream_Write(output_stream,
+ Stream_Buffer(rdpdr->devices[i].device_announce),
+ rdpdr->devices[i].device_announce_len);
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "Registered device %i (%s)",
+ rdpdr->devices[i].device_id, rdpdr->devices[i].device_name);
+
+ }
+
+ guac_rdp_common_svc_write(svc, output_stream);
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "All supported devices sent.");
+
+}
+
+void guac_rdpdr_process_server_announce(guac_rdp_common_svc* svc,
+ wStream* input_stream) {
+
+ unsigned int major, minor, client_id;
+
+ Stream_Read_UINT16(input_stream, major);
+ Stream_Read_UINT16(input_stream, minor);
+ Stream_Read_UINT32(input_stream, client_id);
+
+ /* Must choose own client ID if minor not >= 12 */
+ if (minor < 12)
+ client_id = random() & 0xFFFF;
+
+ guac_client_log(svc->client, GUAC_LOG_INFO, "Connected to RDPDR %u.%u as client 0x%04x", major, minor, client_id);
+
+ /* Respond to announce */
+ guac_rdpdr_send_client_announce_reply(svc, major, minor, client_id);
+
+ /* Name request */
+ guac_rdpdr_send_client_name_request(svc, ((guac_rdp_client*) svc->client->data)->settings->client_name);
+
+}
+
+void guac_rdpdr_process_clientid_confirm(guac_rdp_common_svc* svc,
+ wStream* input_stream) {
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "Client ID confirmed");
+}
+
+void guac_rdpdr_process_device_reply(guac_rdp_common_svc* svc,
+ wStream* input_stream) {
+
+ guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data;
+
+ unsigned int device_id, ntstatus;
+ int severity, c, n, facility, code;
+
+ Stream_Read_UINT32(input_stream, device_id);
+ Stream_Read_UINT32(input_stream, ntstatus);
+
+ severity = (ntstatus & 0xC0000000) >> 30;
+ c = (ntstatus & 0x20000000) >> 29;
+ n = (ntstatus & 0x10000000) >> 28;
+ facility = (ntstatus & 0x0FFF0000) >> 16;
+ code = ntstatus & 0x0000FFFF;
+
+ /* Log error / information */
+ if (device_id < rdpdr->devices_registered) {
+
+ if (severity == 0x0)
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "Device %i (%s) connected successfully",
+ device_id, rdpdr->devices[device_id].device_name);
+
+ else
+ guac_client_log(svc->client, GUAC_LOG_ERROR, "Problem connecting device %i (%s): "
+ "severity=0x%x, c=0x%x, n=0x%x, facility=0x%x, code=0x%x",
+ device_id, rdpdr->devices[device_id].device_name,
+ severity, c, n, facility, code);
+
+ }
+
+ else
+ guac_client_log(svc->client, GUAC_LOG_ERROR, "Unknown device ID: 0x%08x", device_id);
+
+}
+
+void guac_rdpdr_process_device_iorequest(guac_rdp_common_svc* svc,
+ wStream* input_stream) {
+
+ guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data;
+ guac_rdpdr_iorequest iorequest;
+
+ /* Read header */
+ Stream_Read_UINT32(input_stream, iorequest.device_id);
+ Stream_Read_UINT32(input_stream, iorequest.file_id);
+ Stream_Read_UINT32(input_stream, iorequest.completion_id);
+ Stream_Read_UINT32(input_stream, iorequest.major_func);
+ Stream_Read_UINT32(input_stream, iorequest.minor_func);
+
+ /* If printer, run printer handlers */
+ if (iorequest.device_id >= 0 && iorequest.device_id < rdpdr->devices_registered) {
+
+ /* Call handler on device */
+ guac_rdpdr_device* device = &(rdpdr->devices[iorequest.device_id]);
+ device->iorequest_handler(svc, device, &iorequest, input_stream);
+
+ }
+
+ else
+ guac_client_log(svc->client, GUAC_LOG_ERROR, "Unknown device ID: "
+ "0x%08x", iorequest.device_id);
+
+}
+
+void guac_rdpdr_process_server_capability(guac_rdp_common_svc* svc,
+ wStream* input_stream) {
+
+ int count;
+ int i;
+
+ /* Read header */
+ Stream_Read_UINT16(input_stream, count);
+ Stream_Seek(input_stream, 2);
+
+ /* Parse capabilities */
+ for (i=0; i<count; i++) {
+
+ int type;
+ int length;
+
+ Stream_Read_UINT16(input_stream, type);
+ Stream_Read_UINT16(input_stream, length);
+
+ /* Ignore all for now */
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "Ignoring server capability set type=0x%04x, length=%i", type, length);
+ Stream_Seek(input_stream, length - 4);
+
+ }
+
+ /* Send own capabilities */
+ guac_rdpdr_send_client_capability(svc);
+
+}
+
+void guac_rdpdr_process_user_loggedon(guac_rdp_common_svc* svc,
+ wStream* input_stream) {
+
+ guac_client_log(svc->client, GUAC_LOG_INFO, "RDPDR user logged on");
+ guac_rdpdr_send_client_device_list_announce_request(svc);
+
+}
+
+void guac_rdpdr_process_prn_cache_data(guac_rdp_common_svc* svc,
+ wStream* input_stream) {
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "Ignoring printer cached configuration data");
+}
+
+void guac_rdpdr_process_prn_using_xps(guac_rdp_common_svc* svc,
+ wStream* input_stream) {
+ guac_client_log(svc->client, GUAC_LOG_WARNING, "Printer unexpectedly switched to XPS mode");
+}
diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-messages.h b/src/protocols/rdp/channels/rdpdr/rdpdr-messages.h
new file mode 100644
index 0000000..a79c83b
--- /dev/null
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-messages.h
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_CHANNELS_RDPDR_MESSAGES_H
+#define GUAC_RDP_CHANNELS_RDPDR_MESSAGES_H
+
+#include "channels/common-svc.h"
+#include "channels/rdpdr/rdpdr.h"
+
+#include <winpr/stream.h>
+
+#include <stdint.h>
+
+/**
+ * A 32-bit arbitrary value for the osType field of certain requests. As this
+ * value is defined as completely arbitrary and required to be ignored by the
+ * server, we send "GUAC" as an integer.
+ */
+#define GUAC_OS_TYPE (*((uint32_t*) "GUAC"))
+
+/**
+ * Handler which processes a message specific to the RDPDR channel.
+ *
+ * @param svc
+ * The guac_rdp_common_svc representing the static virtual channel being
+ * used for RDPDR.
+ *
+ * @param input_stream
+ * A wStream containing the entire received message.
+ */
+typedef void guac_rdpdr_message_handler(guac_rdp_common_svc* svc,
+ wStream* input_stream);
+
+/**
+ * Handler which processes a received Server Announce Request message. The
+ * Server Announce Request message begins the RDPDR exchange and provides a
+ * client ID which the RDPDR client may use. The client may also supply its
+ * own, randomly-generated ID, and is required to do so for older versions of
+ * RDPDR. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/046047aa-62d8-49f9-bf16-7fe41880aaf4
+ */
+guac_rdpdr_message_handler guac_rdpdr_process_server_announce;
+
+/**
+ * Handler which processes a received Server Client ID Confirm message. The
+ * Server Client ID Confirm message is sent by the server to confirm the client
+ * ID requested by the client (in its response to the Server Announce Request)
+ * has been accepted. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/bbbb9666-6994-4cf6-8e65-0d46eb319c6e
+ */
+guac_rdpdr_message_handler guac_rdpdr_process_clientid_confirm;
+
+/**
+ * Handler which processes a received Server Device Announce Response message.
+ * The Server Device Announce Response message is sent in response to a Client
+ * Device List Announce message to communicate the success/failure status of
+ * device creation. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/a4c0b619-6e87-4721-bdc4-5d2db7f485f3
+ */
+guac_rdpdr_message_handler guac_rdpdr_process_device_reply;
+
+/**
+ * Handler which processes a received Device I/O Request message. The Device
+ * I/O Request message makes up the majority of traffic once RDPDR is
+ * established. Each I/O request consists of a device-specific major/minor
+ * function number pair, as well as several parameters. Device-specific
+ * handling of I/O requests within Guacamole is delegated to device- and
+ * function-specific implementations of yet another function type:
+ * guac_rdpdr_device_iorequest_handler.
+ *
+ * See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/a087ffa8-d0d5-4874-ac7b-0494f63e2d5d
+ */
+guac_rdpdr_message_handler guac_rdpdr_process_device_iorequest;
+
+/**
+ * Handler which processes a received Server Core Capability Request message.
+ * The Server Core Capability Request message is sent by the server to
+ * communicate its capabilities and to request that the client communicate the
+ * same. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/702789c3-b924-4bc2-9280-3221bc7d6797
+ */
+guac_rdpdr_message_handler guac_rdpdr_process_server_capability;
+
+/**
+ * Handler which processes a received Server User Logged On message. The Server
+ * User Logged On message is sent by the server to notify that the user has
+ * logged on to the session. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/dfc0e8ed-a242-4d00-bb88-e779e08f2f61
+ */
+guac_rdpdr_message_handler guac_rdpdr_process_user_loggedon;
+
+/**
+ * Handler which processes any one of several RDPDR messages specific to cached
+ * printer configuration data, each of these messages having the same
+ * PAKID_PRN_CACHE_DATA packet ID. The Guacamole RDPDR implementation ignores
+ * all PAKID_PRN_CACHE_DATA messages. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpepc/7fccae60-f077-433b-9dee-9bad4238bf40
+ */
+guac_rdpdr_message_handler guac_rdpdr_process_prn_cache_data;
+
+/**
+ * Handler which processes a received Server Printer Set XPS Mode message. The
+ * Server Printer Set XPS Mode message is specific to printers and requests
+ * that the client printer be set to XPS mode. The Guacamole RDPDR
+ * implementation ignores any request to set the printer to XPS mode. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpepc/f1789a66-bcd0-4df3-bfc2-6e7330d63145
+ */
+guac_rdpdr_message_handler guac_rdpdr_process_prn_using_xps;
+
+#endif
+
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c b/src/protocols/rdp/channels/rdpdr/rdpdr-printer.c
similarity index 71%
rename from src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
rename to src/protocols/rdp/channels/rdpdr/rdpdr-printer.c
index ace025c..d90116f 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-printer.c
@@ -17,41 +17,26 @@
* under the License.
*/
-#include "config.h"
-
-#include "rdpdr_messages.h"
-#include "rdpdr_printer.h"
-#include "rdpdr_service.h"
+#include "channels/rdpdr/rdpdr-printer.h"
+#include "channels/rdpdr/rdpdr.h"
+#include "print-job.h"
#include "rdp.h"
-#include "rdp_print_job.h"
-#include "rdp_status.h"
#include "unicode.h"
-#include <freerdp/utils/svc_plugin.h>
+#include <freerdp/channels/rdpdr.h>
+#include <freerdp/settings.h>
#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-#include <guacamole/socket.h>
-#include <guacamole/stream.h>
#include <guacamole/unicode.h>
-#include <guacamole/user.h>
-
-#ifdef ENABLE_WINPR
+#include <winpr/nt.h>
#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-#include <errno.h>
-#include <pthread.h>
-#include <stdint.h>
#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-void guac_rdpdr_process_print_job_create(guac_rdpdr_device* device,
- wStream* input_stream, int completion_id) {
+void guac_rdpdr_process_print_job_create(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
- guac_client* client = device->rdpdr->client;
+ guac_client* client = svc->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Log creation of print job */
@@ -63,17 +48,18 @@
/* Respond with success */
wStream* output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_SUCCESS, 4);
+ iorequest->completion_id, STATUS_SUCCESS, 4);
Stream_Write_UINT32(output_stream, 0); /* fileId */
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+ guac_rdp_common_svc_write(svc, output_stream);
}
-void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
- wStream* input_stream, int completion_id) {
+void guac_rdpdr_process_print_job_write(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
- guac_client* client = device->rdpdr->client;
+ guac_client* client = svc->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
@@ -100,19 +86,20 @@
}
wStream* output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, status, 5);
+ iorequest->completion_id, status, 5);
Stream_Write_UINT32(output_stream, length);
Stream_Write_UINT8(output_stream, 0); /* Padding */
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+ guac_rdp_common_svc_write(svc, output_stream);
}
-void guac_rdpdr_process_print_job_close(guac_rdpdr_device* device,
- wStream* input_stream, int completion_id) {
+void guac_rdpdr_process_print_job_close(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
- guac_client* client = device->rdpdr->client;
+ guac_client* client = svc->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
@@ -123,61 +110,63 @@
}
wStream* output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_SUCCESS, 4);
+ iorequest->completion_id, STATUS_SUCCESS, 4);
Stream_Write_UINT32(output_stream, 0); /* Padding */
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+ guac_rdp_common_svc_write(svc, output_stream);
/* Log end of print job */
guac_client_log(client, GUAC_LOG_INFO, "Print job closed");
}
-static void guac_rdpdr_device_printer_iorequest_handler(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id, int major_func, int minor_func) {
+void guac_rdpdr_device_printer_iorequest_handler(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream) {
- switch (major_func) {
+ switch (iorequest->major_func) {
/* Print job create */
case IRP_MJ_CREATE:
- guac_rdpdr_process_print_job_create(device, input_stream, completion_id);
+ guac_rdpdr_process_print_job_create(svc, device, iorequest, input_stream);
break;
/* Printer job write */
case IRP_MJ_WRITE:
- guac_rdpdr_process_print_job_write(device, input_stream, completion_id);
+ guac_rdpdr_process_print_job_write(svc, device, iorequest, input_stream);
break;
/* Printer job close */
case IRP_MJ_CLOSE:
- guac_rdpdr_process_print_job_close(device, input_stream, completion_id);
+ guac_rdpdr_process_print_job_close(svc, device, iorequest, input_stream);
break;
/* Log unknown */
default:
- guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
- "Unknown printer I/O request function: 0x%x/0x%x",
- major_func, minor_func);
+ guac_client_log(svc->client, GUAC_LOG_ERROR, "Unknown printer "
+ "I/O request function: 0x%x/0x%x", iorequest->major_func,
+ iorequest->minor_func);
}
}
-static void guac_rdpdr_device_printer_free_handler(guac_rdpdr_device* device) {
+void guac_rdpdr_device_printer_free_handler(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device) {
Stream_Free(device->device_announce, 1);
}
-void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr, char* printer_name) {
+void guac_rdpdr_register_printer(guac_rdp_common_svc* svc, char* printer_name) {
+ guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data;
int id = rdpdr->devices_registered++;
/* Get new device */
guac_rdpdr_device* device = &(rdpdr->devices[id]);
/* Init device */
- device->rdpdr = rdpdr;
device->device_id = id;
device->device_name = printer_name;
int device_name_len = guac_utf8_strlen(device->device_name);
diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-printer.h b/src/protocols/rdp/channels/rdpdr/rdpdr-printer.h
new file mode 100644
index 0000000..a6865d8
--- /dev/null
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr-printer.h
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_CHANNELS_RDPDR_PRINTER_H
+#define GUAC_RDP_CHANNELS_RDPDR_PRINTER_H
+
+#include "channels/common-svc.h"
+#include "channels/rdpdr/rdpdr.h"
+
+#include <winpr/stream.h>
+
+/**
+ * Name of the printer driver that should be used on the server.
+ */
+#define GUAC_PRINTER_DRIVER "M\0S\0 \0P\0u\0b\0l\0i\0s\0h\0e\0r\0 \0I\0m\0a\0g\0e\0s\0e\0t\0t\0e\0r\0\0\0"
+
+/**
+ * The size of GUAC_PRINTER_DRIVER in bytes.
+ */
+#define GUAC_PRINTER_DRIVER_LENGTH 50
+
+/**
+ * Registers a new printer device within the RDPDR plugin. This must be done
+ * before RDPDR connection finishes.
+ *
+ * @param svc
+ * The static virtual channel instance being used for RDPDR.
+ *
+ * @param printer_name
+ * The name of the printer that will be registered with the RDP
+ * connection and passed through to the server.
+ */
+void guac_rdpdr_register_printer(guac_rdp_common_svc* svc, char* printer_name);
+
+/**
+ * I/O request handler which processes a print job creation request.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_process_print_job_create;
+
+/**
+ * I/O request handler which processes a request to write data to an existing
+ * print job.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_process_print_job_write;
+
+/**
+ * I/O request handler which processes a request to close an existing print
+ * job.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_process_print_job_close;
+
+/**
+ * Handler for RDPDR Device I/O Requests which processes received messages on
+ * behalf of a printer device, in this case a simulated printer which produces
+ * PDF output.
+ */
+guac_rdpdr_device_iorequest_handler guac_rdpdr_device_printer_iorequest_handler;
+
+/**
+ * Free handler which frees all data specific to the simulated printer device.
+ */
+guac_rdpdr_device_free_handler guac_rdpdr_device_printer_free_handler;
+
+#endif
+
diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr.c b/src/protocols/rdp/channels/rdpdr/rdpdr.c
new file mode 100644
index 0000000..e04bc9d
--- /dev/null
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr.c
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "channels/rdpdr/rdpdr.h"
+#include "channels/rdpdr/rdpdr-fs.h"
+#include "channels/rdpdr/rdpdr-messages.h"
+#include "channels/rdpdr/rdpdr-printer.h"
+#include "rdp.h"
+#include "settings.h"
+
+#include <freerdp/channels/rdpdr.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/settings.h>
+#include <guacamole/client.h>
+#include <winpr/stream.h>
+
+#include <stdlib.h>
+
+void guac_rdpdr_process_receive(guac_rdp_common_svc* svc,
+ wStream* input_stream) {
+
+ int component;
+ int packet_id;
+
+ /* Read header */
+ Stream_Read_UINT16(input_stream, component);
+ Stream_Read_UINT16(input_stream, packet_id);
+
+ /* Core component */
+ if (component == RDPDR_CTYP_CORE) {
+
+ /* Dispatch handlers based on packet ID */
+ switch (packet_id) {
+
+ case PAKID_CORE_SERVER_ANNOUNCE:
+ guac_rdpdr_process_server_announce(svc, input_stream);
+ break;
+
+ case PAKID_CORE_CLIENTID_CONFIRM:
+ guac_rdpdr_process_clientid_confirm(svc, input_stream);
+ break;
+
+ case PAKID_CORE_DEVICE_REPLY:
+ guac_rdpdr_process_device_reply(svc, input_stream);
+ break;
+
+ case PAKID_CORE_DEVICE_IOREQUEST:
+ guac_rdpdr_process_device_iorequest(svc, input_stream);
+ break;
+
+ case PAKID_CORE_SERVER_CAPABILITY:
+ guac_rdpdr_process_server_capability(svc, input_stream);
+ break;
+
+ case PAKID_CORE_USER_LOGGEDON:
+ guac_rdpdr_process_user_loggedon(svc, input_stream);
+ break;
+
+ default:
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "Ignoring "
+ "RDPDR core packet with unexpected ID: 0x%04x",
+ packet_id);
+
+ }
+
+ } /* end if core */
+
+ /* Printer component */
+ else if (component == RDPDR_CTYP_PRN) {
+
+ /* Dispatch handlers based on packet ID */
+ switch (packet_id) {
+
+ case PAKID_PRN_CACHE_DATA:
+ guac_rdpdr_process_prn_cache_data(svc, input_stream);
+ break;
+
+ case PAKID_PRN_USING_XPS:
+ guac_rdpdr_process_prn_using_xps(svc, input_stream);
+ break;
+
+ default:
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "Ignoring RDPDR "
+ "printer packet with unexpected ID: 0x%04x",
+ packet_id);
+
+ }
+
+ } /* end if printer */
+
+ else
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "Ignoring packet for "
+ "unknown RDPDR component: 0x%04x", component);
+
+}
+
+wStream* guac_rdpdr_new_io_completion(guac_rdpdr_device* device,
+ int completion_id, int status, int size) {
+
+ wStream* output_stream = Stream_New(NULL, 16+size);
+
+ /* Write header */
+ Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
+ Stream_Write_UINT16(output_stream, PAKID_CORE_DEVICE_IOCOMPLETION);
+
+ /* Write content */
+ Stream_Write_UINT32(output_stream, device->device_id);
+ Stream_Write_UINT32(output_stream, completion_id);
+ Stream_Write_UINT32(output_stream, status);
+
+ return output_stream;
+
+}
+
+void guac_rdpdr_process_connect(guac_rdp_common_svc* svc) {
+
+ /* Get data from client */
+ guac_client* client = svc->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ guac_rdpdr* rdpdr = (guac_rdpdr*) calloc(1, sizeof(guac_rdpdr));
+ svc->data = rdpdr;
+
+ /* Register printer if enabled */
+ if (rdp_client->settings->printing_enabled)
+ guac_rdpdr_register_printer(svc, rdp_client->settings->printer_name);
+
+ /* Register drive if enabled */
+ if (rdp_client->settings->drive_enabled)
+ guac_rdpdr_register_fs(svc, rdp_client->settings->drive_name);
+
+}
+
+void guac_rdpdr_process_terminate(guac_rdp_common_svc* svc) {
+
+ guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data;
+ if (rdpdr == NULL)
+ return;
+
+ int i;
+
+ for (i=0; i<rdpdr->devices_registered; i++) {
+ guac_rdpdr_device* device = &(rdpdr->devices[i]);
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "Unloading device %i "
+ "(%s)", device->device_id, device->device_name);
+ device->free_handler(svc, device);
+ }
+
+ free(rdpdr);
+
+}
+
+
+void guac_rdpdr_load_plugin(rdpContext* context) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+
+ /* Load support for RDPDR */
+ if (guac_rdp_common_svc_load_plugin(context, "rdpdr",
+ CHANNEL_OPTION_COMPRESS_RDP, guac_rdpdr_process_connect,
+ guac_rdpdr_process_receive, guac_rdpdr_process_terminate)) {
+ guac_client_log(client, GUAC_LOG_WARNING, "Support for the RDPDR "
+ "channel (device redirection) could not be loaded. Drive "
+ "redirection and printing will not work. Sound MAY not work.");
+ }
+
+}
+
diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr.h b/src/protocols/rdp/channels/rdpdr/rdpdr.h
new file mode 100644
index 0000000..b6f9da3
--- /dev/null
+++ b/src/protocols/rdp/channels/rdpdr/rdpdr.h
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_CHANNELS_RDPDR_H
+#define GUAC_RDP_CHANNELS_RDPDR_H
+
+#include "channels/common-svc.h"
+
+#include <freerdp/freerdp.h>
+#include <guacamole/client.h>
+#include <winpr/stream.h>
+
+#include <stdint.h>
+
+/**
+ * The maximum number of bytes to allow for a device read.
+ */
+#define GUAC_RDP_MAX_READ_BUFFER 4194304
+
+/**
+ * Arbitrary device forwarded over the RDPDR channel.
+ */
+typedef struct guac_rdpdr_device guac_rdpdr_device;
+
+/**
+ * The contents of the header common to all RDPDR Device I/O Requests. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/a087ffa8-d0d5-4874-ac7b-0494f63e2d5d
+ */
+typedef struct guac_rdpdr_iorequest {
+
+ /**
+ * The unique ID assigned to the device receiving this I/O request.
+ */
+ int device_id;
+
+ /**
+ * The unique ID which identifies the relevant file, as returned when the
+ * file was opened. This field may not be relevant to all requests.
+ */
+ int file_id;
+
+ /**
+ * The unique ID that should be used to refer to this I/O request in future
+ * responses.
+ */
+ int completion_id;
+
+ /**
+ * Integer ID which identifies the function being requested, such as
+ * IRP_MJ_CREATE (open a file within a shared drive) or IRP_MJ_WRITE (write
+ * data to an open file).
+ */
+ int major_func;
+
+ /**
+ * Integer ID which identifies a variant of the function denoted by
+ * major_func. This value is only valid for IRP_MJ_DIRECTORY_CONTROL.
+ */
+ int minor_func;
+
+} guac_rdpdr_iorequest;
+
+/**
+ * Handler for Device I/O Requests. RDPDR devices must provide an
+ * implementation of this function to be able to handle inbound I/O requests.
+ *
+ * @param svc
+ * The guac_rdp_common_svc representing the static virtual channel being
+ * used for RDPDR.
+ *
+ * @param device
+ * The guac_rdpdr_device of the relevant device, as dictated by the
+ * deviceId field of the common RDPDR header within the received PDU.
+ * Within the guac_rdpdr_iorequest structure, the deviceId field is stored
+ * within device_id.
+ *
+ * @param iorequest
+ * The contents of the common RDPDR Device I/O Request header shared by all
+ * RDPDR devices.
+ *
+ * @param input_stream
+ * The remaining data within the received PDU, following the common RDPDR
+ * Device I/O Request header.
+ */
+typedef void guac_rdpdr_device_iorequest_handler(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
+ wStream* input_stream);
+
+/**
+ * Handler for cleaning up the dynamically-allocated portions of a device.
+ *
+ * @param svc
+ * The guac_rdp_common_svc representing the static virtual channel being
+ * used for RDPDR.
+ *
+ * @param device
+ * The guac_rdpdr_device of the device being freed.
+ */
+typedef void guac_rdpdr_device_free_handler(guac_rdp_common_svc* svc,
+ guac_rdpdr_device* device);
+
+struct guac_rdpdr_device {
+
+ /**
+ * The ID assigned to this device by the RDPDR plugin.
+ */
+ int device_id;
+
+ /**
+ * Device name, used for logging and for passthrough to the
+ * server.
+ */
+ const char* device_name;
+
+ /**
+ * The type of RDPDR device that this represents.
+ */
+ uint32_t device_type;
+
+ /**
+ * The DOS name of the device. Max 8 bytes, including terminator.
+ */
+ const char *dos_name;
+
+ /**
+ * The stream that stores the RDPDR device announcement for this device.
+ */
+ wStream* device_announce;
+
+ /**
+ * The length of the device_announce wStream.
+ */
+ int device_announce_len;
+
+ /**
+ * Handler which should be called for every I/O request received.
+ */
+ guac_rdpdr_device_iorequest_handler* iorequest_handler;
+
+ /**
+ * Handler which should be called when the device is being freed.
+ */
+ guac_rdpdr_device_free_handler* free_handler;
+
+ /**
+ * Arbitrary data, used internally by the handlers for this device.
+ */
+ void* data;
+
+};
+
+/**
+ * Structure representing the current state of the Guacamole RDPDR plugin for
+ * FreeRDP.
+ */
+typedef struct guac_rdpdr {
+
+ /**
+ * The number of devices registered within the devices array.
+ */
+ int devices_registered;
+
+ /**
+ * Array of registered devices.
+ */
+ guac_rdpdr_device devices[8];
+
+} guac_rdpdr;
+
+/**
+ * Creates a new stream which contains the common DR_DEVICE_IOCOMPLETION header
+ * used for virtually all responses. Depending on the specific I/O completion
+ * being sent, additional space may be reserved within the resulting stream for
+ * additional fields. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/10ef9ada-cba2-4384-ab60-7b6290ed4a9a
+ *
+ * @param device
+ * The device that completed the operation requested by a prior I/O
+ * request.
+ *
+ * @param completion_id
+ * The completion ID of the I/O request that requested the operation.
+ *
+ * @param status
+ * An NTSTATUS code describing the success/failure of the operation that
+ * was completed.
+ *
+ * @param size
+ * The number of additional bytes to reserve at the end of the resulting
+ * stream for additional fields to be appended.
+ *
+ * @return
+ * A new wStream containing an I/O completion header, followed by the
+ * requested additional free space.
+ */
+wStream* guac_rdpdr_new_io_completion(guac_rdpdr_device* device,
+ int completion_id, int status, int size);
+
+/**
+ * Initializes device redirection support (file transfer, printing, etc.) for
+ * RDP and handling of the RDPDR channel. If failures occur, messages noting
+ * the specifics of those failures will be logged, and the RDP side of
+ * device redirection support will not be functional.
+ *
+ * This MUST be called within the PreConnect callback of the freerdp instance
+ * for RDPDR support to be loaded.
+ *
+ * @param context
+ * The rdpContext associated with the FreeRDP side of the RDP connection.
+ */
+void guac_rdpdr_load_plugin(rdpContext* context);
+
+/**
+ * Handler which is invoked when the RDPDR channel is connected to the RDP
+ * server.
+ */
+guac_rdp_common_svc_connect_handler guac_rdpdr_process_connect;
+
+/**
+ * Handler which is invoked when the RDPDR channel has received data from the
+ * RDP server.
+ */
+guac_rdp_common_svc_receive_handler guac_rdpdr_process_receive;
+
+/**
+ * Handler which is invoked when the RDPDR channel has disconnected and is
+ * about to be freed.
+ */
+guac_rdp_common_svc_terminate_handler guac_rdpdr_process_terminate;
+
+#endif
+
diff --git a/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.c b/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c
similarity index 84%
rename from src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.c
rename to src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c
index ba0224c..1d69e06 100644
--- a/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.c
+++ b/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c
@@ -17,30 +17,20 @@
* under the License.
*/
-#include "config.h"
-
+#include "channels/rdpsnd/rdpsnd-messages.h"
+#include "channels/rdpsnd/rdpsnd.h"
#include "rdp.h"
-#include "rdpsnd_messages.h"
-#include "rdpsnd_service.h"
-#include <pthread.h>
-#include <stdlib.h>
-
-#include <freerdp/utils/svc_plugin.h>
+#include <freerdp/codec/audio.h>
#include <guacamole/audio.h>
#include <guacamole/client.h>
-
-#ifdef ENABLE_WINPR
#include <winpr/stream.h>
#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-stream.h"
-#include "compat/winpr-wtypes.h"
-#endif
-/* MESSAGE HANDLERS */
+#include <stdlib.h>
+#include <string.h>
-void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
+void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header) {
int server_format_count;
@@ -51,11 +41,10 @@
int output_body_size;
unsigned char* output_stream_end;
- /* Get associated client data */
- guac_client* client = rdpsnd->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_client* client = svc->client;
+ guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
- /* Get audio stream from client data */
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_audio_stream* audio = rdp_client->audio;
/* Reset own format count */
@@ -188,8 +177,7 @@
Stream_SetPointer(output_stream, output_stream_end);
/* Send accepted formats */
- pthread_mutex_lock(&(rdp_client->rdp_lock));
- svc_plugin_send((rdpSvcPlugin*)rdpsnd, output_stream);
+ guac_rdp_common_svc_write(svc, output_stream);
/* If version greater than 6, must send Quality Mode PDU */
if (server_version >= 6) {
@@ -202,23 +190,20 @@
Stream_Write_UINT16(output_stream, HIGH_QUALITY);
Stream_Write_UINT16(output_stream, 0);
- svc_plugin_send((rdpSvcPlugin*)rdpsnd, output_stream);
- }
+ guac_rdp_common_svc_write(svc, output_stream);
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
+ }
}
/* server is getting a feel of the round trip time */
-void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
+void guac_rdpsnd_training_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header) {
int data_size;
wStream* output_stream;
- /* Get associated client data */
- guac_client* client = rdpsnd->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
/* Read timestamp and data size */
Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);
@@ -232,22 +217,19 @@
Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp);
Stream_Write_UINT16(output_stream, data_size);
- pthread_mutex_lock(&(rdp_client->rdp_lock));
- svc_plugin_send((rdpSvcPlugin*) rdpsnd, output_stream);
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
+ guac_rdp_common_svc_write(svc, output_stream);
}
-void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
+void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header) {
int format;
- /* Get associated client data */
- guac_client* client = rdpsnd->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_client* client = svc->client;
+ guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
- /* Get audio stream from client data */
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_audio_stream* audio = rdp_client->audio;
/* Read wave information */
@@ -276,16 +258,13 @@
}
-void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
+void guac_rdpsnd_wave_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header) {
- rdpSvcPlugin* plugin = (rdpSvcPlugin*)rdpsnd;
+ guac_client* client = svc->client;
+ guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
- /* Get associated client data */
- guac_client* client = rdpsnd->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
-
- /* Get audio stream from client data */
guac_audio_stream* audio = rdp_client->audio;
/* Wave Confirmation PDU */
@@ -313,16 +292,14 @@
Stream_Write_UINT8(output_stream, 0);
/* Send Wave Confirmation PDU */
- pthread_mutex_lock(&(rdp_client->rdp_lock));
- svc_plugin_send(plugin, output_stream);
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
+ guac_rdp_common_svc_write(svc, output_stream);
/* We no longer expect to receive wave data */
rdpsnd->next_pdu_is_wave = FALSE;
}
-void guac_rdpsnd_close_handler(guac_rdpsndPlugin* rdpsnd,
+void guac_rdpsnd_close_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header) {
/* Do nothing */
diff --git a/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.h b/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.h
similarity index 64%
rename from src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.h
rename to src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.h
index 7083b84..9271151 100644
--- a/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.h
+++ b/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.h
@@ -17,87 +17,12 @@
* under the License.
*/
+#ifndef GUAC_RDP_CHANNELS_RDPSND_MESSAGES_H
+#define GUAC_RDP_CHANNELS_RDPSND_MESSAGES_H
-#ifndef __GUAC_RDPSND_MESSAGES_H
-#define __GUAC_RDPSND_MESSAGES_H
+#include "channels/common-svc.h"
-#include "config.h"
-
-#include "rdpsnd_service.h"
-
-#ifdef ENABLE_WINPR
#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-/*
- * PDU Message Types
- */
-
-/**
- * Close PDU
- */
-#define SNDC_CLOSE 1
-
-/**
- * WaveInfo PDU. This PDU is sent just before wave data is sent.
- */
-#define SNDC_WAVE 2
-
-/**
- * Wave Confirm PDU. This PDU is sent in response to the WaveInfo PDU,
- * confirming it has been received and played.
- */
-#define SNDC_WAVECONFIRM 5
-
-/**
- * Training PDU. This PDU is sent by the server occasionally and must be
- * responded to with another training PDU, similar to Guac's sync message.
- */
-#define SNDC_TRAINING 6
-
-/**
- * Server Audio Formats and Version PDU. This PDU is sent by the server to
- * advertise to the client which audio formats are supported.
- */
-#define SNDC_FORMATS 7
-
-/**
- * Quality Mode PDU. This PDU must be sent by the client to select an audio
- * quality mode if the server is at least version 6.
- */
-#define SNDC_QUALITYMODE 12
-
-/*
- * Quality Modes
- */
-
-/**
- * Dynamic Quality. The server will choose the audio quality based on its
- * perception of latency.
- */
-#define DYNAMIC_QUALITY 0x0000
-
-/**
- * Medium Quality. The server prioritizes bandwidth over quality.
- */
-#define MEDIUM_QUALITY 0x0001
-
-/**
- * High Quality. The server prioritizes quality over bandwidth.
- */
-#define HIGH_QUALITY 0x0002
-
-/*
- * Capabilities
- */
-#define TSSNDCAPS_ALIVE 1
-
-/*
- * Sound Formats
- */
-#define WAVE_FORMAT_PCM 1
/**
* The header common to all RDPSND PDUs.
@@ -121,8 +46,8 @@
* SNDC_FORMATS PDU describes all audio formats supported by the RDP server, as
* well as the version of RDPSND implemented.
*
- * @param rdpsnd
- * The Guacamole RDPSND plugin receiving the SNDC_FORMATS PDU.
+ * @param svc
+ * The RDPSND channel receiving the SNDC_FORMATS PDU.
*
* @param input_stream
* The FreeRDP input stream containing the remaining raw bytes (after the
@@ -132,7 +57,7 @@
* The header content of the SNDC_FORMATS PDU. All RDPSND messages contain
* the same header information.
*/
-void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
+void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header);
/**
@@ -142,8 +67,8 @@
*
* https://msdn.microsoft.com/en-us/library/cc240961.aspx
*
- * @param rdpsnd
- * The Guacamole RDPSND plugin receiving the SNDC_TRAINING PDU.
+ * @param svc
+ * The RDPSND channel receiving the SNDC_TRAINING PDU.
*
* @param input_stream
* The FreeRDP input stream containing the remaining raw bytes (after the
@@ -153,7 +78,7 @@
* The header content of the SNDC_TRAINING PDU. All RDPSND messages contain
* the same header information.
*/
-void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
+void guac_rdpsnd_training_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header);
/**
@@ -165,8 +90,8 @@
*
* https://msdn.microsoft.com/en-us/library/cc240963.aspx
*
- * @param rdpsnd
- * The Guacamole RDPSND plugin receiving the SNDC_WAVE PDU.
+ * @param svc
+ * The RDPSND channel receiving the SNDC_WAVE PDU.
*
* @param input_stream
* The FreeRDP input stream containing the remaining raw bytes (after the
@@ -176,7 +101,7 @@
* The header content of the SNDC_WAVE PDU. All RDPSND messages contain
* the same header information.
*/
-void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
+void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header);
/**
@@ -184,8 +109,8 @@
* PDU contains the actual audio data, less the four bytes of audio data
* included in the SNDC_WAVE PDU.
*
- * @param rdpsnd
- * The Guacamole RDPSND plugin receiving the SNDWAV PDU.
+ * @param svc
+ * The RDPSND channel receiving the SNDWAV PDU.
*
* @param input_stream
* The FreeRDP input stream containing the remaining raw bytes (after the
@@ -195,7 +120,7 @@
* The header content of the SNDWAV PDU. All RDPSND messages contain
* the same header information.
*/
-void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
+void guac_rdpsnd_wave_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header);
/**
@@ -204,8 +129,8 @@
*
* https://msdn.microsoft.com/en-us/library/cc240970.aspx
*
- * @param rdpsnd
- * The Guacamole RDPSND plugin receiving the SNDC_CLOSE PDU.
+ * @param svc
+ * The RDPSND channel receiving the SNDC_CLOSE PDU.
*
* @param input_stream
* The FreeRDP input stream containing the remaining raw bytes (after the
@@ -215,7 +140,7 @@
* The header content of the SNDC_CLOSE PDU. All RDPSND messages contain
* the same header information.
*/
-void guac_rdpsnd_close_handler(guac_rdpsndPlugin* rdpsnd,
+void guac_rdpsnd_close_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header);
#endif
diff --git a/src/protocols/rdp/channels/rdpsnd/rdpsnd.c b/src/protocols/rdp/channels/rdpsnd/rdpsnd.c
new file mode 100644
index 0000000..be6034d
--- /dev/null
+++ b/src/protocols/rdp/channels/rdpsnd/rdpsnd.c
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "channels/common-svc.h"
+#include "channels/rdpsnd/rdpsnd.h"
+#include "channels/rdpsnd/rdpsnd-messages.h"
+#include "rdp.h"
+
+#include <freerdp/codec/audio.h>
+#include <freerdp/freerdp.h>
+#include <guacamole/client.h>
+#include <winpr/stream.h>
+
+#include <stdlib.h>
+
+void guac_rdpsnd_process_receive(guac_rdp_common_svc* svc,
+ wStream* input_stream) {
+
+ guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
+ guac_rdpsnd_pdu_header header;
+
+ /* Read RDPSND PDU header */
+ Stream_Read_UINT8(input_stream, header.message_type);
+ Stream_Seek_UINT8(input_stream);
+ Stream_Read_UINT16(input_stream, header.body_size);
+
+ /*
+ * If next PDU is SNDWAVE (due to receiving WaveInfo PDU previously),
+ * ignore the header and parse as a Wave PDU.
+ */
+ if (rdpsnd->next_pdu_is_wave) {
+ guac_rdpsnd_wave_handler(svc, input_stream, &header);
+ return;
+ }
+
+ /* Dispatch message to standard handlers */
+ switch (header.message_type) {
+
+ /* Server Audio Formats and Version PDU */
+ case SNDC_FORMATS:
+ guac_rdpsnd_formats_handler(svc, input_stream, &header);
+ break;
+
+ /* Training PDU */
+ case SNDC_TRAINING:
+ guac_rdpsnd_training_handler(svc, input_stream, &header);
+ break;
+
+ /* WaveInfo PDU */
+ case SNDC_WAVE:
+ guac_rdpsnd_wave_info_handler(svc, input_stream, &header);
+ break;
+
+ /* Close PDU */
+ case SNDC_CLOSE:
+ guac_rdpsnd_close_handler(svc, input_stream, &header);
+ break;
+
+ }
+
+}
+
+void guac_rdpsnd_process_connect(guac_rdp_common_svc* svc) {
+
+ guac_rdpsnd* rdpsnd = (guac_rdpsnd*) calloc(1, sizeof(guac_rdpsnd));
+ svc->data = rdpsnd;
+
+}
+
+void guac_rdpsnd_process_terminate(guac_rdp_common_svc* svc) {
+ guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
+ free(rdpsnd);
+}
+
+void guac_rdpsnd_load_plugin(rdpContext* context) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+
+ /* Load support for RDPSND */
+ if (guac_rdp_common_svc_load_plugin(context, "rdpsnd", 0,
+ guac_rdpsnd_process_connect, guac_rdpsnd_process_receive,
+ guac_rdpsnd_process_terminate)) {
+ guac_client_log(client, GUAC_LOG_WARNING, "Support for the RDPSND "
+ "channel (audio output) could not be loaded. Sound will not "
+ "work. Drive redirection and printing MAY not work.");
+ }
+
+}
+
diff --git a/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.h b/src/protocols/rdp/channels/rdpsnd/rdpsnd.h
similarity index 66%
rename from src/protocols/rdp/guac_rdpsnd/rdpsnd_service.h
rename to src/protocols/rdp/channels/rdpsnd/rdpsnd.h
index b6a9396..cd7fe23 100644
--- a/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.h
+++ b/src/protocols/rdp/channels/rdpsnd/rdpsnd.h
@@ -17,21 +17,14 @@
* under the License.
*/
+#ifndef GUAC_RDP_CHANNELS_RDPSND_H
+#define GUAC_RDP_CHANNELS_RDPSND_H
-#ifndef __GUAC_RDPSND_SERVICE_H
-#define __GUAC_RDPSND_SERVICE_H
+#include "channels/common-svc.h"
-#include "config.h"
-
-#include <freerdp/utils/svc_plugin.h>
+#include <freerdp/freerdp.h>
#include <guacamole/client.h>
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
/**
* The maximum number of PCM formats to accept during the initial RDPSND
* handshake with the RDP server.
@@ -42,7 +35,7 @@
* Abstract representation of a PCM format, including the sample rate, number
* of channels, and bits per sample.
*/
-typedef struct guac_pcm_format {
+typedef struct guac_rdpsnd_pcm_format {
/**
* The sample rate of this PCM format.
@@ -61,26 +54,13 @@
*/
int bps;
-} guac_pcm_format;
+} guac_rdpsnd_pcm_format;
/**
* Structure representing the current state of the Guacamole RDPSND plugin for
* FreeRDP.
*/
-typedef struct guac_rdpsndPlugin {
-
- /**
- * The FreeRDP parts of this plugin. This absolutely MUST be first.
- * FreeRDP depends on accessing this structure as if it were an instance
- * of rdpSvcPlugin.
- */
- rdpSvcPlugin plugin;
-
- /**
- * The Guacamole client associated with the guac_audio_stream that this
- * plugin should use to stream received audio packets.
- */
- guac_client* client;
+typedef struct guac_rdpsnd {
/**
* The block number of the last SNDC_WAVE (WaveInfo) PDU received.
@@ -116,36 +96,45 @@
* exchange. All of these formats will be PCM, which is the only format
* guaranteed to be supported (based on the official RDP documentation).
*/
- guac_pcm_format formats[GUAC_RDP_MAX_FORMATS];
+ guac_rdpsnd_pcm_format formats[GUAC_RDP_MAX_FORMATS];
/**
* The total number of formats.
*/
int format_count;
-} guac_rdpsndPlugin;
+} guac_rdpsnd;
/**
- * Handler called when this plugin is loaded by FreeRDP.
+ * Initializes audio output support for RDP and handling of the RDPSND channel.
+ * If failures occur, messages noting the specifics of those failures will be
+ * logged, and the RDP side of audio output support will not be functional.
+ *
+ * This MUST be called within the PreConnect callback of the freerdp instance
+ * for RDPSND support to be loaded.
+ *
+ * @param context
+ * The rdpContext associated with the FreeRDP side of the RDP connection.
*/
-void guac_rdpsnd_process_connect(rdpSvcPlugin* plugin);
+void guac_rdpsnd_load_plugin(rdpContext* context);
/**
- * Handler called when this plugin receives data along its designated channel.
+ * Handler which is invoked when the RDPSND channel is connected to the RDP
+ * server.
*/
-void guac_rdpsnd_process_receive(rdpSvcPlugin* plugin,
- wStream* input_stream);
+guac_rdp_common_svc_connect_handler guac_rdpsnd_process_connect;
/**
- * Handler called when this plugin is being unloaded.
+ * Handler which is invoked when the RDPSND channel has received data from the
+ * RDP server.
*/
-void guac_rdpsnd_process_terminate(rdpSvcPlugin* plugin);
+guac_rdp_common_svc_receive_handler guac_rdpsnd_process_receive;
/**
- * Handler called when this plugin receives an event. For the sake of RDPSND,
- * all events will be ignored and simply free'd.
+ * Handler which is invoked when the RDPSND channel has disconnected and is
+ * about to be freed.
*/
-void guac_rdpsnd_process_event(rdpSvcPlugin* plugin, wMessage* event);
+guac_rdp_common_svc_terminate_handler guac_rdpsnd_process_terminate;
#endif
diff --git a/src/protocols/rdp/client.c b/src/protocols/rdp/client.c
index 6b9a0a6..c79e59d 100644
--- a/src/protocols/rdp/client.c
+++ b/src/protocols/rdp/client.c
@@ -17,14 +17,16 @@
* under the License.
*/
-#include "config.h"
-
-#include "audio_input.h"
-#include "common/recording.h"
#include "client.h"
+#include "channels/audio-input/audio-buffer.h"
+#include "channels/cliprdr.h"
+#include "channels/disp.h"
+#include "common/recording.h"
+#include "config.h"
+#include "fs.h"
+#include "log.h"
#include "rdp.h"
-#include "rdp_disp.h"
-#include "rdp_fs.h"
+#include "settings.h"
#include "user.h"
#ifdef ENABLE_COMMON_SSH
@@ -33,26 +35,11 @@
#include "common-ssh/user.h"
#endif
-#include <freerdp/cache/cache.h>
-#include <freerdp/channels/channels.h>
-#include <freerdp/freerdp.h>
#include <guacamole/audio.h>
#include <guacamole/client.h>
-#include <guacamole/socket.h>
-
-#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
-#include <freerdp/client/cliprdr.h>
-#else
-#include "compat/client-cliprdr.h"
-#endif
-
-#ifdef HAVE_FREERDP_CLIENT_CHANNELS_H
-#include <freerdp/client/channels.h>
-#endif
#include <pthread.h>
#include <stdlib.h>
-#include <string.h>
int guac_client_init(guac_client* client, int argc, char** argv) {
@@ -64,19 +51,19 @@
client->data = rdp_client;
/* Init clipboard */
- rdp_client->clipboard = guac_common_clipboard_alloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
+ rdp_client->clipboard = guac_rdp_clipboard_alloc(client);
/* Init display update module */
rdp_client->disp = guac_rdp_disp_alloc();
+ /* Redirect FreeRDP log messages to guac_client_log() */
+ guac_rdp_redirect_wlog(client);
+
/* Recursive attribute for locks */
pthread_mutexattr_init(&(rdp_client->attributes));
pthread_mutexattr_settype(&(rdp_client->attributes),
PTHREAD_MUTEX_RECURSIVE);
- /* Init RDP lock */
- pthread_mutex_init(&(rdp_client->rdp_lock), &(rdp_client->attributes));
-
/* Set handlers */
client->join_handler = guac_rdp_user_join_handler;
client->free_handler = guac_rdp_client_free_handler;
@@ -100,6 +87,9 @@
if (rdp_client->settings != NULL)
guac_rdp_settings_free(rdp_client->settings);
+ /* Clean up clipboard */
+ guac_rdp_clipboard_free(rdp_client->clipboard);
+
/* Free display update module */
guac_rdp_disp_free(rdp_client->disp);
@@ -136,7 +126,6 @@
guac_rdp_audio_buffer_free(rdp_client->audio_input);
/* Free client data */
- guac_common_clipboard_free(rdp_client->clipboard);
free(rdp_client);
return 0;
diff --git a/src/protocols/rdp/client.h b/src/protocols/rdp/client.h
index 4f6c266..9acd33a 100644
--- a/src/protocols/rdp/client.h
+++ b/src/protocols/rdp/client.h
@@ -20,8 +20,6 @@
#ifndef GUAC_RDP_CLIENT_H
#define GUAC_RDP_CLIENT_H
-#include "config.h"
-
#include <guacamole/client.h>
/**
diff --git a/src/protocols/rdp/color.c b/src/protocols/rdp/color.c
new file mode 100644
index 0000000..964310a
--- /dev/null
+++ b/src/protocols/rdp/color.c
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "config.h"
+#include "settings.h"
+
+#include <freerdp/codec/color.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/gdi/gdi.h>
+#include <winpr/wtypes.h>
+
+#include <stdint.h>
+#include <string.h>
+
+UINT32 guac_rdp_get_native_pixel_format(BOOL alpha) {
+
+ uint32_t int_value;
+ uint8_t raw_bytes[4] = { 0x0A, 0x0B, 0x0C, 0x0D };
+
+ memcpy(&int_value, raw_bytes, sizeof(raw_bytes));
+
+ /* Local platform stores bytes in decreasing order of significance
+ * (big-endian) */
+ if (int_value == 0x0A0B0C0D)
+ return alpha ? PIXEL_FORMAT_ARGB32 : PIXEL_FORMAT_XRGB32;
+
+ /* Local platform stores bytes in increasing order of significance
+ * (little-endian) */
+ else
+ return alpha ? PIXEL_FORMAT_BGRA32 : PIXEL_FORMAT_BGRX32;
+
+}
+
+UINT32 guac_rdp_convert_color(rdpContext* context, UINT32 color) {
+
+ int depth = guac_rdp_get_depth(context->instance);
+ int src_format = gdi_get_pixel_format(depth);
+ int dst_format = guac_rdp_get_native_pixel_format(TRUE);
+ rdpGdi* gdi = context->gdi;
+
+ /* Convert provided color into the intermediate representation expected by
+ * FreeRDPConvertColor() */
+ UINT32 intermed = ReadColor((BYTE*) &color, src_format);
+
+ /* Convert color from RDP source format to the native format used by Cairo,
+ * still maintaining intermediate representation */
+#ifdef HAVE_FREERDPCONVERTCOLOR
+ intermed = FreeRDPConvertColor(intermed, src_format, dst_format,
+ &gdi->palette);
+#else
+ intermed = ConvertColor(intermed, src_format, dst_format, &gdi->palette);
+#endif
+
+ /* Convert color from intermediate representation to the actual desired
+ * format */
+ WriteColor((BYTE*) &color, dst_format, intermed);
+ return color;
+
+}
+
diff --git a/src/protocols/rdp/rdp_color.h b/src/protocols/rdp/color.h
similarity index 61%
rename from src/protocols/rdp/rdp_color.h
rename to src/protocols/rdp/color.h
index 40712ba..43910a5 100644
--- a/src/protocols/rdp/rdp_color.h
+++ b/src/protocols/rdp/color.h
@@ -21,12 +21,27 @@
#define GUAC_RDP_COLOR_H
#include <freerdp/freerdp.h>
-
-#ifdef ENABLE_WINPR
#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-wtypes.h"
-#endif
+
+/**
+ * Returns the FreeRDP pixel format ID corresponding to the 32-bit RGB format
+ * used by the Cairo library's image surfaces. Cairo handles colors in terms of
+ * integers in native endianness, with CAIRO_FORMAT_ARGB32 representing a color
+ * format where the alpha channel is stored in the most significant byte,
+ * followed by red, green, and blue. FreeRDP handles colors in terms of
+ * absolute byte order, with PIXEL_FORMAT_ARGB32 representing a color format
+ * where the alpha channel is in byte 0, followed by red at byte 1, etc.
+ *
+ * @param alpha
+ * TRUE if the returned FreeRDP pixel format should correspond to Cairo's
+ * CAIRO_FORMAT_ARGB32, FALSE if the returned format should correspond to
+ * Cairo's CAIRO_FORMAT_RGB24.
+ *
+ * @return
+ * The FreeRDP pixel format ID that corresponds to the 32-bit RGB format
+ * used by the Cairo library.
+ */
+UINT32 guac_rdp_get_native_pixel_format(BOOL alpha);
/**
* Converts the given color to ARGB32. The color given may be an index
diff --git a/src/protocols/rdp/compat/client-cliprdr.h b/src/protocols/rdp/compat/client-cliprdr.h
deleted file mode 100644
index f1e2aab..0000000
--- a/src/protocols/rdp/compat/client-cliprdr.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef __GUAC_CLIENT_CLIPRDR_COMPAT_H
-#define __GUAC_CLIENT_CLIPRDR_COMPAT_H
-
-#include "config.h"
-
-#include <freerdp/plugins/cliprdr.h>
-
-#define CliprdrChannel_Class RDP_EVENT_CLASS_CLIPRDR
-#define CliprdrChannel_FormatList RDP_EVENT_TYPE_CB_FORMAT_LIST
-#define CliprdrChannel_MonitorReady RDP_EVENT_TYPE_CB_MONITOR_READY
-#define CliprdrChannel_DataRequest RDP_EVENT_TYPE_CB_DATA_REQUEST
-#define CliprdrChannel_DataResponse RDP_EVENT_TYPE_CB_DATA_RESPONSE
-
-#endif
-
diff --git a/src/protocols/rdp/compat/rail.h b/src/protocols/rdp/compat/rail.h
deleted file mode 100644
index 32d75b4..0000000
--- a/src/protocols/rdp/compat/rail.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef __GUAC_RAIL_COMPAT_H
-#define __GUAC_RAIL_COMPAT_H
-
-#include "config.h"
-
-#include <freerdp/rail.h>
-
-#define RailChannel_Class RDP_EVENT_CLASS_RAIL
-#define RailChannel_ClientSystemParam RDP_EVENT_TYPE_RAIL_CLIENT_SET_SYSPARAMS
-#define RailChannel_GetSystemParam RDP_EVENT_TYPE_RAIL_CHANNEL_GET_SYSPARAMS
-#define RailChannel_ServerExecuteResult RDP_EVENT_TYPE_RAIL_CHANNEL_EXEC_RESULTS
-#define RailChannel_ServerSystemParam RDP_EVENT_TYPE_RAIL_CHANNEL_SERVER_SYSPARAM
-#define RailChannel_ServerMinMaxInfo RDP_EVENT_TYPE_RAIL_CHANNEL_SERVER_MINMAXINFO
-#define RailChannel_ServerLocalMoveSize RDP_EVENT_TYPE_RAIL_CHANNEL_SERVER_LOCALMOVESIZE
-#define RailChannel_ServerGetAppIdResponse RDP_EVENT_TYPE_RAIL_CHANNEL_APPID_RESP
-#define RailChannel_ServerLanguageBarInfo RDP_EVENT_TYPE_RAIL_CHANNEL_LANGBARINFO
-
-#endif
-
diff --git a/src/protocols/rdp/compat/winpr-stream.c b/src/protocols/rdp/compat/winpr-stream.c
deleted file mode 100644
index 8be4546..0000000
--- a/src/protocols/rdp/compat/winpr-stream.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "config.h"
-
-#include "winpr-stream.h"
-#include "winpr-wtypes.h"
-
-wStream* Stream_New(BYTE* buffer, size_t size) {
-
- /* If no buffer is provided, allocate a new stream of the given size */
- if (buffer == NULL)
- return stream_new(size);
-
- /* Otherwise allocate an empty stream and assign the given buffer */
- wStream* stream = stream_new(0);
- stream_attach(stream, buffer, size);
- return stream;
-
-}
-
-void Stream_Free(wStream* s, BOOL bFreeBuffer) {
-
- /* Disassociate buffer if it will be freed externally */
- if (!bFreeBuffer)
- stream_detach(s);
-
- stream_free(s);
-
-}
-
diff --git a/src/protocols/rdp/compat/winpr-stream.h b/src/protocols/rdp/compat/winpr-stream.h
deleted file mode 100644
index e2195c0..0000000
--- a/src/protocols/rdp/compat/winpr-stream.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef __GUAC_WINPR_STREAM_COMPAT_H
-#define __GUAC_WINPR_STREAM_COMPAT_H
-
-#include "config.h"
-
-#include "winpr-wtypes.h"
-
-#include <freerdp/utils/stream.h>
-
-#include <stddef.h>
-
-/* FreeRDP 1.0 streams */
-
-#define Stream_Write stream_write
-#define Stream_Write_UINT8 stream_write_uint8
-#define Stream_Write_UINT16 stream_write_uint16
-#define Stream_Write_UINT32 stream_write_uint32
-#define Stream_Write_UINT64 stream_write_uint64
-
-#define Stream_Read stream_read
-#define Stream_Read_UINT8 stream_read_uint8
-#define Stream_Read_UINT16 stream_read_uint16
-#define Stream_Read_UINT32 stream_read_uint32
-#define Stream_Read_UINT64 stream_read_uint64
-
-#define Stream_Seek stream_seek
-#define Stream_Seek_UINT8 stream_seek_uint8
-#define Stream_Seek_UINT16 stream_seek_uint16
-#define Stream_Seek_UINT32 stream_seek_uint32
-#define Stream_Seek_UINT64 stream_seek_uint64
-
-#define Stream_GetPointer stream_get_mark
-#define Stream_EnsureRemainingCapacity stream_check_size
-#define Stream_Write stream_write
-#define Stream_Zero stream_write_zero
-#define Stream_Fill stream_set_byte
-#define Stream_GetPosition stream_get_pos
-#define Stream_SetPosition stream_set_pos
-#define Stream_SetPointer stream_set_mark
-#define Stream_Buffer stream_get_head
-#define Stream_Pointer stream_get_tail
-#define Stream_Length stream_get_size
-
-#define wStream STREAM
-#define wMessage RDP_EVENT
-
-wStream* Stream_New(BYTE* buffer, size_t size);
-void Stream_Free(wStream* s, BOOL bFreeBuffer);
-
-#endif
-
diff --git a/src/protocols/rdp/compat/winpr-wtypes.h b/src/protocols/rdp/compat/winpr-wtypes.h
deleted file mode 100644
index e1d3e1b..0000000
--- a/src/protocols/rdp/compat/winpr-wtypes.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef __GUAC_WINPR_WTYPES_COMPAT_H
-#define __GUAC_WINPR_WTYPES_COMPAT_H
-
-#include "config.h"
-
-#include <freerdp/types.h>
-
-typedef uint8 BYTE;
-typedef uint8 UINT8;
-typedef uint16 UINT16;
-typedef uint32 UINT32;
-typedef uint64 UINT64;
-typedef boolean BOOL;
-
-#define TRUE true
-#define FALSE false
-
-#endif
-
diff --git a/src/protocols/rdp/decompose.c b/src/protocols/rdp/decompose.c
index 5f559d9..e42a769 100644
--- a/src/protocols/rdp/decompose.c
+++ b/src/protocols/rdp/decompose.c
@@ -17,7 +17,6 @@
* under the License.
*/
-#include "config.h"
#include "keyboard.h"
/**
diff --git a/src/protocols/rdp/doc/svc-example/.gitignore b/src/protocols/rdp/doc/svc-example/.gitignore
new file mode 100644
index 0000000..51e8bef
--- /dev/null
+++ b/src/protocols/rdp/doc/svc-example/.gitignore
@@ -0,0 +1,2 @@
+!Makefile
+*.exe
diff --git a/src/protocols/rdp/doc/svc-example/Makefile b/src/protocols/rdp/doc/svc-example/Makefile
new file mode 100644
index 0000000..8c38e59
--- /dev/null
+++ b/src/protocols/rdp/doc/svc-example/Makefile
@@ -0,0 +1,43 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# NOTE: Parts of this file (Makefile.am) are automatically transcluded verbatim
+# into Makefile.in. Though the build system (GNU Autotools) automatically adds
+# its own license boilerplate to the generated Makefile.in, that boilerplate
+# does not apply to the transcluded portions of Makefile.am which are licensed
+# to you by the ASF under the Apache License, Version 2.0, as described above.
+#
+
+LDFLAGS=-lwtsapi32
+
+# Requires at least Windows Vista (Windows Server 2008 qualifies)
+WINVER=0x600
+
+# Windows cross compiler (MinGW)
+CC=i686-w64-mingw32-gcc
+
+all: svc-example.exe
+
+clean:
+ $(RM) svc-example.exe
+
+svc-example.exe: svc-example.c
+ $(CC) svc-example.c $(LDFLAGS) \
+ -D_WIN32_WINNT=$(WINVER) \
+ -DWINVER=$(WINVER) -o svc-example.exe
+
diff --git a/src/protocols/rdp/doc/svc-example/README.md b/src/protocols/rdp/doc/svc-example/README.md
new file mode 100644
index 0000000..337a733
--- /dev/null
+++ b/src/protocols/rdp/doc/svc-example/README.md
@@ -0,0 +1,169 @@
+Static Virtual Channel example
+==============================
+
+Guacamole supports use of static virtual channels (SVCs) for transmission of
+arbitrary data between the JavaScript client and applications running within
+RDP sessions. This example is intended to demonstrate how bidirectional
+communication between the Guacamole client and applications within the RDP
+server can be accomplished.
+
+Arbitrary SVCs are enabled on RDP connections by specfying their names as the
+value of [the `static-channels`
+parameter](http://guacamole.apache.org/doc/gug/configuring-guacamole.html#rdp-device-redirection).
+Each name is limited to a maximum of 7 characters. Multiple names may be listed
+by separating those names with commas.
+
+This example consists of a single file, [`svc-example.c`](svc-example.c), which
+leverages the terminal server API exposed by Windows to:
+
+ 1. Open a channel called "EXAMPLE"
+ 2. Wait for blocks of data to be received
+ 3. Send each received block of data back, unmodified.
+
+Building the example
+--------------------
+
+A `Makefile` is provided which uses MinGW to build the `svc-example.exe`
+executable, and thus can be used to produce the example application on Linux.
+The `Makefile` is not platform-independent, and changes may be needed for
+`make` to succeed with your installation of MinGW. If not using MinGW, the C
+source itself is standard and should compile with other tools.
+
+To build on Linux using `make`:
+
+```console
+$ make
+i686-w64-mingw32-gcc svc-example.c -lwtsapi32 \
+ -D_WIN32_WINNT=0x600 \
+ -DWINVER=0x600 -o svc-example.exe
+$
+```
+
+You can then copy the resulting `svc-example.exe` to the remote desktop that
+you wish to test and run it within a command prompt within the remote desktop
+session.
+
+Using the example (and SVCs in general)
+---------------------------------------
+
+On the remote desktop server side (within the Windows application leveraging
+SVCs to communicate with Guacamole), the following functions are used
+specifically for reading/writing to the SVC:
+
+ * [`WTSVirtualChannelOpenEx()`](https://docs.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsvirtualchannelopenex)
+ * [`WTSVirtualChannelRead()`](https://docs.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsvirtualchannelread)
+ * [`WTSVirtualChannelWrite()`](https://docs.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsvirtualchannelwrite)
+ * [`WTSVirtualChannelClose()`](https://docs.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsvirtualchannelclose)
+
+On the Guacamole side, bidirectional communication is established using:
+
+ * The `static-channels` connection parameter (in the case of the example, this should be set to `EXAMPLE`).
+ * An [`onpipe`](http://guacamole.apache.org/doc/guacamole-common-js/Guacamole.Client.html#event:onpipe)
+ handler which handles inbound (server-to-client) pipe streams named identically
+ to the SVC. The inbound pipe stream will be received upon establishing the RDP
+ connection and is used to transmit any data sent along the SVC **from** within
+ the remote desktop session. For example:
+
+ ```js
+ client.onpipe = function pipeReceived(stream, mimetype, name) {
+
+ // Receive output of SVC
+ if (name === 'EXAMPLE') {
+
+ // Log start of stream
+ var reader = new Guacamole.StringReader(stream);
+ console.log('pipe: %s: stream begins', name);
+
+ // Log each received blob of text
+ reader.ontext = function textReceived(text) {
+ console.log('pipe: %s: \"%s\"', name, text);
+ };
+
+ // Log end of stream
+ reader.onend = function streamEnded() {
+ console.log('pipe: %s: stream ends', name);
+ };
+
+ }
+
+ // All other inbound pipe streams are unsupported
+ else
+ stream.sendAck('Pipe stream not supported.',
+ Guacamole.Status.Code.UNSUPPORTED);
+
+ };
+ ```
+
+ * Calls to [`createPipeStream()`](http://guacamole.apache.org/doc/guacamole-common-js/Guacamole.Client.html#createPipeStream)
+ as needed to establish outbound (client-to-server) pipe streams named
+ identically to the SVC. Outbound pipe streams with the same name as the SVC
+ will be automatically handled by the Guacamole server, with any received data
+ sent along the SVC **to** the remote desktop session. For example:
+
+ ```js
+ var example = new Guacamole.StringWriter(client.createPipeStream('text/plain', 'EXAMPLE'));
+ example.sendText('This is a test.');
+ example.sendEnd();
+ ```
+
+ These pipe streams may be created and destroyed as desired. As long as they
+ have the same name as the SVC, data sent along the pipe stream will be sent
+ along the SVC.
+
+Example output
+--------------
+
+If the `static-channels` parameter is set to `EXAMPLE`, the successful creation
+of the "EXAMPLE" channel should be logged by guacd when the connection is
+established:
+
+```
+guacd[12057]: INFO: Created static channel "EXAMPLE"...
+guacd[12057]: INFO: Static channel "EXAMPLE" connected.
+```
+
+On the client side, the `onpipe` handler should be invoked immediately. If
+using the example code shown above, receipt of the pipe stream for the
+"EXAMPLE" channel is logged:
+
+```
+pipe: EXAMPLE: stream begins
+```
+
+Running `svc-example.exe` within a command prompt inside the remote desktop
+session, the application logs that the "EXAMPLE" channel has been successfully
+opened:
+
+```
+Microsoft Windows [Version 10.0.17763.437]
+(c) 2018 Microsoft Corporation. All rights reserved.
+
+C:\Users\test>svc-example.exe
+SVC "EXAMPLE" open. Reading...
+```
+
+Once `createPipeStream()` has been invoked on the Guacamole client side and
+using the same name as the SVC (in this case, "EXAMPLE") guacd should log the
+inbound half the channel is now connected:
+
+```
+guacd[12057]: DEBUG: Inbound half of channel "EXAMPLE" connected.
+```
+
+Sending the string `This is a test.` along the client-to-server pipe (as shown
+in the example code above) results in `svc-example.exe` logging that it
+received those 15 bytes and has resent the same 15 bytes back along the SVC:
+
+```
+Received 15 bytes.
+Wrote 15 bytes.
+```
+
+The data sent from within the remote desktop session is received on the client
+side via the server-to-client pipe stream. If using the example code shown
+above, the received data is logged:
+
+```
+pipe: EXAMPLE: "This is a test."
+```
+
diff --git a/src/protocols/rdp/doc/svc-example/svc-example.c b/src/protocols/rdp/doc/svc-example/svc-example.c
new file mode 100644
index 0000000..681b3f9
--- /dev/null
+++ b/src/protocols/rdp/doc/svc-example/svc-example.c
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <windows.h>
+#include <winbase.h>
+#include <wtsapi32.h>
+
+#include <stdio.h>
+
+/**
+ * The name of the RDP static virtual channel (SVC).
+ */
+#define SVC_NAME "EXAMPLE"
+
+int main() {
+
+ ULONG bytes_read;
+ ULONG bytes_written;
+
+ char message[4096];
+
+ /* Open SVC */
+ HANDLE svc = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, SVC_NAME, 0);
+
+ /* Fail if we cannot open an SVC at all */
+ if (svc == NULL) {
+ printf("Cannot open SVC \"" SVC_NAME "\"\n");
+ return 0;
+ }
+
+ printf("SVC \"" SVC_NAME "\" open. Reading...\n");
+
+ /* Continuously read from SVC */
+ while (WTSVirtualChannelRead(svc, INFINITE, message, sizeof(message), &bytes_read)) {
+
+ printf("Received %i bytes.\n", bytes_read);
+
+ /* Write all received data back to the SVC, possibly spreading the data
+ * across multiple writes */
+ char* current = message;
+ while (bytes_read > 0 && WTSVirtualChannelWrite(svc, current,
+ bytes_read, &bytes_written)) {
+ printf("Wrote %i bytes.\n", bytes_written);
+ bytes_read -= bytes_written;
+ }
+
+ }
+
+ /* Close SVC */
+ WTSVirtualChannelClose(svc);
+ printf("SVC \"" SVC_NAME "\" closed.\n");
+ return 1;
+
+}
+
diff --git a/src/protocols/rdp/download.c b/src/protocols/rdp/download.c
new file mode 100644
index 0000000..34a8c8a
--- /dev/null
+++ b/src/protocols/rdp/download.c
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "common/json.h"
+#include "download.h"
+#include "fs.h"
+#include "ls.h"
+#include "rdp.h"
+
+#include <guacamole/client.h>
+#include <guacamole/object.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/stream.h>
+#include <guacamole/string.h>
+#include <guacamole/user.h>
+#include <winpr/nt.h>
+#include <winpr/shell.h>
+
+#include <stdlib.h>
+
+int guac_rdp_download_ack_handler(guac_user* user, guac_stream* stream,
+ char* message, guac_protocol_status status) {
+
+ guac_client* client = user->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_rdp_download_status* download_status = (guac_rdp_download_status*) stream->data;
+
+ /* Get filesystem, return error if no filesystem */
+ guac_rdp_fs* fs = rdp_client->filesystem;
+ if (fs == NULL) {
+ guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
+ GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+ guac_socket_flush(user->socket);
+ return 0;
+ }
+
+ /* If successful, read data */
+ if (status == GUAC_PROTOCOL_STATUS_SUCCESS) {
+
+ /* Attempt read into buffer */
+ char buffer[4096];
+ int bytes_read = guac_rdp_fs_read(fs,
+ download_status->file_id,
+ download_status->offset, buffer, sizeof(buffer));
+
+ /* If bytes read, send as blob */
+ if (bytes_read > 0) {
+ download_status->offset += bytes_read;
+ guac_protocol_send_blob(user->socket, stream,
+ buffer, bytes_read);
+ }
+
+ /* If EOF, send end */
+ else if (bytes_read == 0) {
+ guac_protocol_send_end(user->socket, stream);
+ guac_user_free_stream(user, stream);
+ free(download_status);
+ }
+
+ /* Otherwise, fail stream */
+ else {
+ guac_user_log(user, GUAC_LOG_ERROR,
+ "Error reading file for download");
+ guac_protocol_send_end(user->socket, stream);
+ guac_user_free_stream(user, stream);
+ free(download_status);
+ }
+
+ guac_socket_flush(user->socket);
+
+ }
+
+ /* Otherwise, return stream to user */
+ else
+ guac_user_free_stream(user, stream);
+
+ return 0;
+
+}
+
+int guac_rdp_download_get_handler(guac_user* user, guac_object* object,
+ char* name) {
+
+ guac_client* client = user->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ /* Get filesystem, ignore request if no filesystem */
+ guac_rdp_fs* fs = rdp_client->filesystem;
+ if (fs == NULL)
+ return 0;
+
+ /* Attempt to open file for reading */
+ int file_id = guac_rdp_fs_open(fs, name, GENERIC_READ, 0, FILE_OPEN, 0);
+ if (file_id < 0) {
+ guac_user_log(user, GUAC_LOG_INFO, "Unable to read file \"%s\"",
+ name);
+ return 0;
+ }
+
+ /* Get opened file */
+ guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
+ if (file == NULL) {
+ guac_client_log(fs->client, GUAC_LOG_DEBUG,
+ "%s: Successful open produced bad file_id: %i",
+ __func__, file_id);
+ return 0;
+ }
+
+ /* If directory, send contents of directory */
+ if (file->attributes & FILE_ATTRIBUTE_DIRECTORY) {
+
+ /* Create stream data */
+ guac_rdp_ls_status* ls_status = malloc(sizeof(guac_rdp_ls_status));
+ ls_status->fs = fs;
+ ls_status->file_id = file_id;
+ guac_strlcpy(ls_status->directory_name, name,
+ sizeof(ls_status->directory_name));
+
+ /* Allocate stream for body */
+ guac_stream* stream = guac_user_alloc_stream(user);
+ stream->ack_handler = guac_rdp_ls_ack_handler;
+ stream->data = ls_status;
+
+ /* Init JSON object state */
+ guac_common_json_begin_object(user, stream,
+ &ls_status->json_state);
+
+ /* Associate new stream with get request */
+ guac_protocol_send_body(user->socket, object, stream,
+ GUAC_USER_STREAM_INDEX_MIMETYPE, name);
+
+ }
+
+ /* Otherwise, send file contents */
+ else {
+
+ /* Create stream data */
+ guac_rdp_download_status* download_status = malloc(sizeof(guac_rdp_download_status));
+ download_status->file_id = file_id;
+ download_status->offset = 0;
+
+ /* Allocate stream for body */
+ guac_stream* stream = guac_user_alloc_stream(user);
+ stream->data = download_status;
+ stream->ack_handler = guac_rdp_download_ack_handler;
+
+ /* Associate new stream with get request */
+ guac_protocol_send_body(user->socket, object, stream,
+ "application/octet-stream", name);
+
+ }
+
+ guac_socket_flush(user->socket);
+ return 0;
+}
+
+void* guac_rdp_download_to_user(guac_user* user, void* data) {
+
+ /* Do not bother attempting the download if the user has left */
+ if (user == NULL)
+ return NULL;
+
+ guac_client* client = user->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_rdp_fs* filesystem = rdp_client->filesystem;
+
+ /* Ignore download if filesystem has been unloaded */
+ if (filesystem == NULL)
+ return NULL;
+
+ /* Attempt to open requested file */
+ char* path = (char*) data;
+ int file_id = guac_rdp_fs_open(filesystem, path,
+ FILE_READ_DATA, 0, FILE_OPEN, 0);
+
+ /* If file opened successfully, start stream */
+ if (file_id >= 0) {
+
+ /* Associate stream with transfer status */
+ guac_stream* stream = guac_user_alloc_stream(user);
+ guac_rdp_download_status* download_status = malloc(sizeof(guac_rdp_download_status));
+ stream->data = download_status;
+ stream->ack_handler = guac_rdp_download_ack_handler;
+ download_status->file_id = file_id;
+ download_status->offset = 0;
+
+ guac_user_log(user, GUAC_LOG_DEBUG, "%s: Initiating download "
+ "of \"%s\"", __func__, path);
+
+ /* Begin stream */
+ guac_protocol_send_file(user->socket, stream,
+ "application/octet-stream", guac_rdp_fs_basename(path));
+ guac_socket_flush(user->socket);
+
+ /* Download started successfully */
+ return stream;
+
+ }
+
+ /* Download failed */
+ guac_user_log(user, GUAC_LOG_ERROR, "Unable to download \"%s\"", path);
+ return NULL;
+
+}
+
diff --git a/src/protocols/rdp/download.h b/src/protocols/rdp/download.h
new file mode 100644
index 0000000..2989658
--- /dev/null
+++ b/src/protocols/rdp/download.h
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_DOWNLOAD_H
+#define GUAC_RDP_DOWNLOAD_H
+
+#include "common/json.h"
+
+#include <guacamole/protocol.h>
+#include <guacamole/stream.h>
+#include <guacamole/user.h>
+
+#include <stdint.h>
+
+/**
+ * The transfer status of a file being downloaded.
+ */
+typedef struct guac_rdp_download_status {
+
+ /**
+ * The file ID of the file being downloaded.
+ */
+ int file_id;
+
+ /**
+ * The current position within the file.
+ */
+ uint64_t offset;
+
+} guac_rdp_download_status;
+
+/**
+ * Handler for acknowledgements of receipt of data related to file downloads.
+ */
+guac_user_ack_handler guac_rdp_download_ack_handler;
+
+/**
+ * Handler for get messages. In context of downloads and the filesystem exposed
+ * via the Guacamole protocol, get messages request the body of a file within
+ * the filesystem.
+ */
+guac_user_get_handler guac_rdp_download_get_handler;
+
+/**
+ * Callback for guac_client_for_user() and similar functions which initiates a
+ * file download to a specific user if that user is still connected. The path
+ * for the file to be downloaded must be passed as the arbitrary data parameter
+ * for the function invoking this callback.
+ */
+guac_user_callback guac_rdp_download_to_user;
+
+#endif
+
diff --git a/src/protocols/rdp/dvc.c b/src/protocols/rdp/dvc.c
deleted file mode 100644
index 34406c9..0000000
--- a/src/protocols/rdp/dvc.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "config.h"
-#include "common/list.h"
-#include "dvc.h"
-#include "rdp.h"
-
-#include <freerdp/channels/channels.h>
-#include <freerdp/freerdp.h>
-#include <guacamole/client.h>
-
-#include <assert.h>
-#include <stdarg.h>
-
-guac_rdp_dvc_list* guac_rdp_dvc_list_alloc() {
-
- guac_rdp_dvc_list* list = malloc(sizeof(guac_rdp_dvc_list));
-
- /* Initialize with empty backing list */
- list->channels = guac_common_list_alloc();
- list->channel_count = 0;
-
- return list;
-
-}
-
-void guac_rdp_dvc_list_add(guac_rdp_dvc_list* list, const char* name, ...) {
-
- va_list args;
-
- guac_rdp_dvc* dvc = malloc(sizeof(guac_rdp_dvc));
-
- va_start(args, name);
-
- /* Count number of arguments (excluding terminating NULL) */
- dvc->argc = 1;
- while (va_arg(args, char*) != NULL)
- dvc->argc++;
-
- /* Reset va_list */
- va_end(args);
- va_start(args, name);
-
- /* Copy argument values into DVC entry */
- dvc->argv = malloc(sizeof(char*) * dvc->argc);
- dvc->argv[0] = strdup(name);
- int i;
- for (i = 1; i < dvc->argc; i++)
- dvc->argv[i] = strdup(va_arg(args, char*));
-
- va_end(args);
-
- /* Add entry to DVC list */
- guac_common_list_add(list->channels, dvc);
-
- /* Update channel count */
- list->channel_count++;
-
-}
-
-void guac_rdp_dvc_list_free(guac_rdp_dvc_list* list) {
-
- /* For each channel */
- guac_common_list_element* current = list->channels->head;
- while (current != NULL) {
-
- /* Free arguments declaration for current channel */
- guac_rdp_dvc* dvc = (guac_rdp_dvc*) current->data;
-
- /* Free the underlying arguments list if not delegated to FreeRDP */
- if (dvc->argv != NULL) {
-
- /* Free each argument value */
- for (int i = 0; i < dvc->argc; i++)
- free(dvc->argv[i]);
-
- free(dvc->argv);
- }
-
- free(dvc);
-
- current = current->next;
-
- }
-
- /* Free underlying list */
- guac_common_list_free(list->channels);
-
- /* Free the DVC list itself */
- free(list);
-
-}
-
-int guac_rdp_load_drdynvc(rdpContext* context, guac_rdp_dvc_list* list) {
-
- guac_client* client = ((rdp_freerdp_context*) context)->client;
- rdpChannels* channels = context->channels;
-
- /* Skip if no channels will be loaded */
- if (list->channel_count == 0)
- return 0;
-
-#ifndef HAVE_ADDIN_ARGV
- /* Allocate plugin data array */
- RDP_PLUGIN_DATA* all_plugin_data =
- calloc(list->channel_count + 1, sizeof(RDP_PLUGIN_DATA));
-
- RDP_PLUGIN_DATA* current_plugin_data = all_plugin_data;
-#endif
-
- /* For each channel */
- guac_common_list_element* current = list->channels->head;
- while (current != NULL) {
-
- /* Get channel arguments */
- guac_rdp_dvc* dvc = (guac_rdp_dvc*) current->data;
- current = current->next;
-
- /* guac_rdp_dvc_list_add() guarantees at one argument */
- assert(dvc->argc >= 1);
-
- /* guac_rdp_load_drdynvc() MUST only be invoked once */
- assert(dvc->argv != NULL);
-
- /* Log registration of plugin for current channel */
- guac_client_log(client, GUAC_LOG_DEBUG,
- "Registering DVC plugin \"%s\"", dvc->argv[0]);
-
-#ifdef HAVE_ADDIN_ARGV
- /* Register plugin with FreeRDP */
- ADDIN_ARGV* args = malloc(sizeof(ADDIN_ARGV));
- args->argc = dvc->argc;
- args->argv = dvc->argv;
- freerdp_dynamic_channel_collection_add(context->settings, args);
-#else
- /* Copy all arguments */
- for (int i = 0; i < dvc->argc; i++)
- current_plugin_data->data[i] = dvc->argv[i];
-
- /* Store size of entry */
- current_plugin_data->size = sizeof(*current_plugin_data);
-
- /* Advance to next set of plugin data */
- current_plugin_data++;
-#endif
-
- /* Rely on FreeRDP to free argv storage */
- dvc->argv = NULL;
-
- }
-
-#ifdef HAVE_ADDIN_ARGV
- /* Load virtual channel management plugin */
- return freerdp_channels_load_plugin(channels, context->instance->settings,
- "drdynvc", context->instance->settings);
-#else
- /* Terminate with empty RDP_PLUGIN_DATA element */
- current_plugin_data->size = 0;
-
- /* Load virtual channel management plugin */
- return freerdp_channels_load_plugin(channels, context->instance->settings,
- "drdynvc", all_plugin_data);
-#endif
-
-}
-
diff --git a/src/protocols/rdp/dvc.h b/src/protocols/rdp/dvc.h
deleted file mode 100644
index 63694f5..0000000
--- a/src/protocols/rdp/dvc.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#ifndef GUAC_RDP_DVC_H
-#define GUAC_RDP_DVC_H
-
-#include "config.h"
-#include "common/list.h"
-
-#include <freerdp/freerdp.h>
-
-/**
- * The set of all arguments that should be passed to a given dynamic virtual
- * channel plugin, including the name of that plugin.
- */
-typedef struct guac_rdp_dvc {
-
- /**
- * The number of arguments in the argv array. This MUST be at least 1.
- */
- int argc;
-
- /**
- * The argument values being passed to the dynamic virtual channel plugin.
- * The first entry in this array is always the name of the plugin. If
- * guac_rdp_load_drdynvc() has been invoked, and freeing the argument
- * values is being delegated to FreeRDP, this will be NULL.
- */
- char** argv;
-
-} guac_rdp_dvc;
-
-/**
- * A list of dynamic virtual channels which should be provided to the DRDYNVC
- * plugin once loaded via guac_rdp_load_drdynvc(). This interface exists purely
- * to bridge incompatibilities between differing versions of FreeRDP and its
- * DRDYNVC plugin. Any allocated guac_rdp_dvc_list is unlikely to be needed
- * after the DRDYNVC plugin has been loaded.
- */
-typedef struct guac_rdp_dvc_list {
-
- /**
- * Array of all dynamic virtual channels which should be registered with
- * the DRDYNVC plugin once loaded. Each list element will point to a
- * guac_rdp_dvc structure which must eventually be freed.
- */
- guac_common_list* channels;
-
- /**
- * The number of channels within the list.
- */
- int channel_count;
-
-} guac_rdp_dvc_list;
-
-/**
- * Allocates a new, empty list of dynamic virtual channels. New channels may
- * be added via guac_rdp_dvc_list_add(). The loading of those channels'
- * associated plugins will be deferred until guac_rdp_load_drdynvc() is
- * invoked.
- *
- * @return
- * A newly-allocated, empty list of dynamic virtual channels.
- */
-guac_rdp_dvc_list* guac_rdp_dvc_list_alloc();
-
-/**
- * Adds the given dynamic virtual channel plugin name and associated arguments
- * to the list. The provied arguments list is NOT optional and MUST be
- * NULL-terminated, even if there are no arguments for the named dynamic
- * virtual channel plugin. Though FreeRDP requires that the arguments for a
- * dynamic virtual channel plugin contain the name of the plugin itself as the
- * first argument, the name must be excluded from the arguments provided here.
- * This function will automatically take care of adding the plugin name to
- * the arguments.
- *
- * @param list
- * The guac_rdp_dvc_list to which the given plugin name and arguments
- * should be added, for later bulk registration via
- * guac_rdp_load_drdynvc().
- *
- * @param name
- * The name of the dynamic virtual channel plugin that should be given
- * the provided arguments when guac_rdp_load_drdynvc() is invoked.
- *
- * @param ...
- * The string (char*) arguments which should be passed to the dynamic
- * virtual channel plugin when it is loaded via guac_rdp_load_drdynvc(),
- * excluding the plugin name itself.
- */
-void guac_rdp_dvc_list_add(guac_rdp_dvc_list* list, const char* name, ...);
-
-/**
- * Frees the given list of dynamic virtual channels. Note that, while each
- * individual entry within this list will be freed, it is partially up to
- * FreeRDP to free the storage associated with the arguments passed to the
- * virtual channels.
- *
- * @param list
- * The list to free.
- */
-void guac_rdp_dvc_list_free(guac_rdp_dvc_list* list);
-
-/**
- * Loads FreeRDP's DRDYNVC plugin and registers the dynamic virtual channel
- * plugins described by the given guac_rdp_dvc_list. This function MUST be
- * invoked no more than once per RDP connection. Invoking this function
- * multiple times, even if the guac_rdp_dvc_list is different each time, will
- * result in undefined behavior.
- *
- * @param context
- * The rdpContext associated with the RDP connection for which the DRDYNVC
- * plugin should be loaded.
- *
- * @param list
- * A guac_rdp_dvc_list describing the dynamic virtual channel plugins that
- * should be registered with the DRDYNVC plugin, along with any arguments.
- */
-int guac_rdp_load_drdynvc(rdpContext* context, guac_rdp_dvc_list* list);
-
-#endif
-
diff --git a/src/protocols/rdp/error.c b/src/protocols/rdp/error.c
index e340362..de0e1cd 100644
--- a/src/protocols/rdp/error.c
+++ b/src/protocols/rdp/error.c
@@ -17,8 +17,6 @@
* under the License.
*/
-#include "config.h"
-
#include "error.h"
#include "rdp.h"
diff --git a/src/protocols/rdp/rdp_fs.c b/src/protocols/rdp/fs.c
similarity index 95%
rename from src/protocols/rdp/rdp_fs.c
rename to src/protocols/rdp/fs.c
index 24d7c3e..5223b5a 100644
--- a/src/protocols/rdp/rdp_fs.c
+++ b/src/protocols/rdp/fs.c
@@ -17,11 +17,19 @@
* under the License.
*/
-#include "config.h"
+#include "fs.h"
+#include "download.h"
+#include "upload.h"
-#include "rdp_fs.h"
-#include "rdp_status.h"
-#include "rdp_stream.h"
+#include <guacamole/client.h>
+#include <guacamole/object.h>
+#include <guacamole/pool.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/string.h>
+#include <guacamole/user.h>
+#include <winpr/file.h>
+#include <winpr/nt.h>
#include <dirent.h>
#include <errno.h>
@@ -32,16 +40,8 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
-#include <sys/types.h>
#include <unistd.h>
-#include <guacamole/client.h>
-#include <guacamole/object.h>
-#include <guacamole/pool.h>
-#include <guacamole/socket.h>
-#include <guacamole/string.h>
-#include <guacamole/user.h>
-
guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path,
int create_drive_path) {
@@ -244,16 +244,16 @@
}
/* Translate access into flags */
- if (access & ACCESS_GENERIC_ALL)
+ if (access & GENERIC_ALL)
flags = O_RDWR;
- else if ((access & ( ACCESS_GENERIC_WRITE
- | ACCESS_FILE_WRITE_DATA
- | ACCESS_FILE_APPEND_DATA))
- && (access & (ACCESS_GENERIC_READ | ACCESS_FILE_READ_DATA)))
+ else if ((access & ( GENERIC_WRITE
+ | FILE_WRITE_DATA
+ | FILE_APPEND_DATA))
+ && (access & (GENERIC_READ | FILE_READ_DATA)))
flags = O_RDWR;
- else if (access & ( ACCESS_GENERIC_WRITE
- | ACCESS_FILE_WRITE_DATA
- | ACCESS_FILE_APPEND_DATA))
+ else if (access & ( GENERIC_WRITE
+ | FILE_WRITE_DATA
+ | FILE_APPEND_DATA))
flags = O_WRONLY;
else
flags = O_RDONLY;
@@ -279,32 +279,32 @@
switch (create_disposition) {
/* Create if not exist, fail otherwise */
- case DISP_FILE_CREATE:
+ case FILE_CREATE:
flags |= O_CREAT | O_EXCL;
break;
/* Open file if exists and do not overwrite, fail otherwise */
- case DISP_FILE_OPEN:
+ case FILE_OPEN:
/* No flag necessary - default functionality of open */
break;
/* Open if exists, create otherwise */
- case DISP_FILE_OPEN_IF:
+ case FILE_OPEN_IF:
flags |= O_CREAT;
break;
/* Overwrite if exists, fail otherwise */
- case DISP_FILE_OVERWRITE:
+ case FILE_OVERWRITE:
flags |= O_TRUNC;
break;
/* Overwrite if exists, create otherwise */
- case DISP_FILE_OVERWRITE_IF:
+ case FILE_OVERWRITE_IF:
flags |= O_CREAT | O_TRUNC;
break;
/* Supersede (replace) if exists, otherwise create */
- case DISP_FILE_SUPERSEDE:
+ case FILE_SUPERSEDE:
unlink(real_path);
flags |= O_CREAT | O_TRUNC;
break;
@@ -605,6 +605,21 @@
}
+const char* guac_rdp_fs_basename(const char* path) {
+
+ for (const char* c = path; *c != '\0'; c++) {
+
+ /* Reset beginning of path if a path separator is found */
+ if (*c == '/' || *c == '\\')
+ path = c + 1;
+
+ }
+
+ /* path now points to the first character after the last path separator */
+ return path;
+
+}
+
int guac_rdp_fs_normalize_path(const char* path, char* abs_path) {
int path_depth = 0;
diff --git a/src/protocols/rdp/rdp_fs.h b/src/protocols/rdp/fs.h
similarity index 89%
rename from src/protocols/rdp/rdp_fs.h
rename to src/protocols/rdp/fs.h
index f1e4295..83a0b3b 100644
--- a/src/protocols/rdp/rdp_fs.h
+++ b/src/protocols/rdp/fs.h
@@ -17,25 +17,24 @@
* under the License.
*/
-
-#ifndef __GUAC_RDP_FS_H
-#define __GUAC_RDP_FS_H
+#ifndef GUAC_RDP_FS_H
+#define GUAC_RDP_FS_H
/**
* Functions and macros specific to filesystem handling and initialization
- * independent of RDP. The functions here may deal with the filesystem device
+ * independent of RDP. The functions here may deal with the filesystem device
* directly, but their semantics must not deal with RDP protocol messaging.
* Functions here represent a virtual Windows-style filesystem on top of UNIX
* system calls and structures, using the guac_rdp_fs structure as a home
* for common data.
*
- * @file rdp_fs.h
+ * @file fs.h
*/
-#include "config.h"
-
#include <guacamole/client.h>
+#include <guacamole/object.h>
#include <guacamole/pool.h>
+#include <guacamole/user.h>
#include <dirent.h>
#include <stdint.h>
@@ -109,73 +108,6 @@
*/
#define GUAC_RDP_FS_ENOTSUP -10
-/*
- * Access constants.
- */
-#define ACCESS_GENERIC_READ 0x80000000
-#define ACCESS_GENERIC_WRITE 0x40000000
-#define ACCESS_GENERIC_ALL 0x10000000
-#define ACCESS_FILE_READ_DATA 0x00000001
-#define ACCESS_FILE_WRITE_DATA 0x00000002
-#define ACCESS_FILE_APPEND_DATA 0x00000004
-#define ACCESS_DELETE 0x00010000
-
-/*
- * Create disposition constants.
- */
-
-#define DISP_FILE_SUPERSEDE 0x00000000
-#define DISP_FILE_OPEN 0x00000001
-#define DISP_FILE_CREATE 0x00000002
-#define DISP_FILE_OPEN_IF 0x00000003
-#define DISP_FILE_OVERWRITE 0x00000004
-#define DISP_FILE_OVERWRITE_IF 0x00000005
-
-/*
- * Information constants.
- * FreeRDP 1.1+ already defines those constants
- */
-#ifdef LEGACY_FREERDP
-
-#define FILE_SUPERSEDED 0x00000000
-#define FILE_OPENED 0x00000001
-#define FILE_OVERWRITTEN 0x00000003
-
-#endif
-
-/*
- * File attributes.
- */
-
-#define FILE_ATTRIBUTE_READONLY 0x00000001
-#define FILE_ATTRIBUTE_HIDDEN 0x00000002
-#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
-#define FILE_ATTRIBUTE_ARCHIVE 0x00000020
-#define FILE_ATTRIBUTE_NORMAL 0x00000080
-
-/*
- * Filesystem attributes.
- */
-
-#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
-#define FILE_CASE_PRESERVED_NAMES 0x00000002
-#define FILE_UNICODE_ON_DISK 0x00000004
-
-/*
- * File create options.
- */
-
-#define FILE_DIRECTORY_FILE 0x00000001
-#define FILE_NON_DIRECTORY_FILE 0x00000040
-
-/*
- * File device types.
- */
-
-#define FILE_DEVICE_DISK 0x00000007
-
-#define SEC_TO_UNIX_EPOCH 11644473600
-
/**
* Converts a Windows timestamp (100 nanosecond intervals since Jan 1, 1601
* UTC) to UNIX timestamp (seconds since Jan 1, 1970 UTC).
@@ -438,10 +370,10 @@
* The absolute path to the file within the simulated filesystem.
*
* @param access
- * A bitwise-OR of various RDPDR access flags, such as ACCESS_GENERIC_ALL
- * or ACCESS_GENERIC_WRITE. This value will ultimately be translated to a
- * standard O_RDWR, O_WRONLY, etc. value when opening the real file on the
- * local filesystem.
+ * A bitwise-OR of various RDPDR access flags, such as GENERIC_ALL or
+ * GENERIC_WRITE. This value will ultimately be translated to a standard
+ * O_RDWR, O_WRONLY, etc. value when opening the real file on the local
+ * filesystem.
*
* @param file_attributes
* The attributes to apply to the file, if created. This parameter is
@@ -449,9 +381,9 @@
*
* @param create_disposition
* Any one of several RDPDR file creation dispositions, such as
- * DISP_FILE_CREATE, DISP_FILE_OPEN_IF, etc. The creation disposition
- * dictates whether a new file should be created, whether the file can
- * already exist, whether existing contents should be truncated, etc.
+ * FILE_CREATE, FILE_OPEN_IF, etc. The creation disposition dictates
+ * whether a new file should be created, whether the file can already
+ * exist, whether existing contents should be truncated, etc.
*
* @param create_options
* A bitwise-OR of various RDPDR options dictating how a file is to be
@@ -593,6 +525,20 @@
void guac_rdp_fs_close(guac_rdp_fs* fs, int file_id);
/**
+ * Given an arbitrary path, returns a pointer to the first character following
+ * the last path separator in the path (the basename of the path). For example,
+ * given "/foo/bar/baz" or "\foo\bar\baz", this function would return a pointer
+ * to "baz".
+ *
+ * @param path
+ * The path to determine the basename of.
+ *
+ * @return
+ * A pointer to the first character of the basename within the path.
+ */
+const char* guac_rdp_fs_basename(const char* path);
+
+/**
* Given an arbitrary path, which may contain ".." and ".", creates an
* absolute path which does NOT contain ".." or ".". The given path MUST
* be absolute.
diff --git a/src/protocols/rdp/rdp_gdi.c b/src/protocols/rdp/gdi.c
similarity index 61%
rename from src/protocols/rdp/rdp_gdi.c
rename to src/protocols/rdp/gdi.c
index 59fe64c..feb6505 100644
--- a/src/protocols/rdp/rdp_gdi.c
+++ b/src/protocols/rdp/gdi.c
@@ -17,27 +17,22 @@
* under the License.
*/
-#include "config.h"
-
-#include "client.h"
+#include "bitmap.h"
+#include "common/display.h"
#include "common/surface.h"
#include "rdp.h"
-#include "rdp_bitmap.h"
-#include "rdp_color.h"
-#include "rdp_settings.h"
+#include "settings.h"
#include <cairo/cairo.h>
#include <freerdp/freerdp.h>
+#include <freerdp/graphics.h>
+#include <freerdp/primary.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
-
-#ifdef ENABLE_WINPR
#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-wtypes.h"
-#endif
#include <stddef.h>
+#include <stddef.h>
guac_transfer_function guac_rdp_rop3_transfer_function(guac_client* client,
int rop3) {
@@ -97,7 +92,7 @@
}
-void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) {
+BOOL guac_rdp_gdi_dstblt(rdpContext* context, const DSTBLT_ORDER* dstblt) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
@@ -140,77 +135,11 @@
}
-}
-
-void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) {
-
- /*
- * Note that this is not a full implementation of PATBLT. This is a
- * fallback implementation which only renders a solid block of background
- * color using the specified ROP3 operation, ignoring whatever brush
- * was actually specified.
- *
- * As libguac-client-rdp explicitly tells the server not to send PATBLT,
- * well-behaved RDP servers will not use this operation at all, while
- * others will at least have a fallback.
- */
-
- /* Get client and current layer */
- guac_client* client = ((rdp_freerdp_context*) context)->client;
- guac_common_surface* current_surface =
- ((guac_rdp_client*) client->data)->current_surface;
-
- int x = patblt->nLeftRect;
- int y = patblt->nTopRect;
- int w = patblt->nWidth;
- int h = patblt->nHeight;
-
- /*
- * Warn that rendering is a fallback, as the server should not be sending
- * this order.
- */
- guac_client_log(client, GUAC_LOG_INFO, "Using fallback PATBLT (server is ignoring "
- "negotiated client capabilities)");
-
- /* Render rectangle based on ROP */
- switch (patblt->bRop) {
-
- /* If blackness, send black rectangle */
- case 0x00:
- guac_common_surface_set(current_surface, x, y, w, h,
- 0x00, 0x00, 0x00, 0xFF);
- break;
-
- /* If NOP, do nothing */
- case 0xAA:
- break;
-
- /* If operation is just a copy, send foreground only */
- case 0xCC:
- case 0xF0:
- guac_common_surface_set(current_surface, x, y, w, h,
- (patblt->foreColor >> 16) & 0xFF,
- (patblt->foreColor >> 8 ) & 0xFF,
- (patblt->foreColor ) & 0xFF,
- 0xFF);
- break;
-
- /* If whiteness, send white rectangle */
- case 0xFF:
- guac_common_surface_set(current_surface, x, y, w, h,
- 0xFF, 0xFF, 0xFF, 0xFF);
- break;
-
- /* Otherwise, invert entire rect */
- default:
- guac_common_surface_transfer(current_surface, x, y, w, h,
- GUAC_TRANSFER_BINARY_NDEST, current_surface, x, y);
-
- }
+ return TRUE;
}
-void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt) {
+BOOL guac_rdp_gdi_scrblt(rdpContext* context, const SCRBLT_ORDER* scrblt) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
@@ -229,9 +158,11 @@
guac_common_surface_copy(rdp_client->display->default_surface,
x_src, y_src, w, h, current_surface, x, y);
+ return TRUE;
+
}
-void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
+BOOL guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
@@ -248,7 +179,7 @@
/* Make sure that the recieved bitmap is not NULL before processing */
if (bitmap == NULL) {
guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in memblt instruction.");
- return;
+ return TRUE;
}
switch (memblt->bRop) {
@@ -321,98 +252,11 @@
}
-}
-
-void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect) {
-
- /* Get client data */
- guac_client* client = ((rdp_freerdp_context*) context)->client;
-
- UINT32 color = guac_rdp_convert_color(context, opaque_rect->color);
-
- guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
-
- int x = opaque_rect->nLeftRect;
- int y = opaque_rect->nTopRect;
- int w = opaque_rect->nWidth;
- int h = opaque_rect->nHeight;
-
- guac_common_surface_set(current_surface, x, y, w, h,
- (color >> 16) & 0xFF,
- (color >> 8 ) & 0xFF,
- (color ) & 0xFF,
- 0xFF);
+ return TRUE;
}
-/**
- * Updates the palette within a FreeRDP CLRCONV object using the new palette
- * entries provided by an RDP palette update.
- *
- * @param clrconv
- * The FreeRDP CLRCONV object to update.
- *
- * @param palette
- * An RDP palette update message containing the palette to store within the
- * given CLRCONV object.
- */
-static void guac_rdp_update_clrconv(CLRCONV* clrconv,
- PALETTE_UPDATE* palette) {
-
- clrconv->palette->count = palette->number;
-#ifdef LEGACY_RDPPALETTE
- clrconv->palette->entries = palette->entries;
-#else
- memcpy(clrconv->palette->entries, palette->entries,
- sizeof(palette->entries));
-#endif
-
-}
-
-/**
- * Updates a raw ARGB32 palette using the new palette entries provided by an
- * RDP palette update.
- *
- * @param guac_palette
- * An array of 256 ARGB32 colors, with each entry corresponding to an
- * entry in the color palette.
- *
- * @param palette
- * An RDP palette update message containing the palette to store within the
- * given array of ARGB32 colors.
- */
-static void guac_rdp_update_palette(UINT32* guac_palette,
- PALETTE_UPDATE* palette) {
-
- PALETTE_ENTRY* entry = palette->entries;
- int i;
-
- /* Copy each palette entry as ARGB32 */
- for (i=0; i < palette->number; i++) {
-
- *guac_palette = 0xFF000000
- | (entry->red << 16)
- | (entry->green << 8)
- | entry->blue;
-
- guac_palette++;
- entry++;
- }
-
-}
-
-void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette) {
-
- CLRCONV* clrconv = ((rdp_freerdp_context*) context)->clrconv;
- UINT32* guac_palette = ((rdp_freerdp_context*) context)->palette;
-
- /* Update internal palette representations */
- guac_rdp_update_clrconv(clrconv, palette);
- guac_rdp_update_palette(guac_palette, palette);
-
-}
-
-void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds) {
+BOOL guac_rdp_gdi_set_bounds(rdpContext* context, const rdpBounds* bounds) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
@@ -428,13 +272,16 @@
bounds->right - bounds->left + 1,
bounds->bottom - bounds->top + 1);
+ return TRUE;
+
}
-void guac_rdp_gdi_end_paint(rdpContext* context) {
+BOOL guac_rdp_gdi_end_paint(rdpContext* context) {
/* IGNORE */
+ return TRUE;
}
-void guac_rdp_gdi_desktop_resize(rdpContext* context) {
+BOOL guac_rdp_gdi_desktop_resize(rdpContext* context) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
@@ -449,6 +296,8 @@
guac_rdp_get_width(context->instance),
guac_rdp_get_height(context->instance));
+ return TRUE;
+
}
diff --git a/src/protocols/rdp/rdp_gdi.h b/src/protocols/rdp/gdi.h
similarity index 63%
rename from src/protocols/rdp/rdp_gdi.h
rename to src/protocols/rdp/gdi.h
index cbe3c75..9ad5713 100644
--- a/src/protocols/rdp/rdp_gdi.h
+++ b/src/protocols/rdp/gdi.h
@@ -17,9 +17,8 @@
* under the License.
*/
-
-#ifndef _GUAC_RDP_RDP_GDI_H
-#define _GUAC_RDP_RDP_GDI_H
+#ifndef GUAC_RDP_GDI_H
+#define GUAC_RDP_GDI_H
#include "config.h"
@@ -46,71 +45,58 @@
int rop3);
/**
- * Handler for RDP DSTBLT update.
+ * Handler for the DstBlt Primary Drawing Order. A DstBlt Primary Drawing Order
+ * paints a rectangle of image data using a raster operation which considers
+ * the destination only. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegdi/87ea30df-59d6-438e-a735-83f0225fbf91
*
* @param context
* The rdpContext associated with the current RDP session.
*
* @param dstblt
* The DSTBLT update to handle.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt);
+BOOL guac_rdp_gdi_dstblt(rdpContext* context, const DSTBLT_ORDER* dstblt);
/**
- * Handler for RDP PATBLT update.
+ * Handler for the ScrBlt Primary Drawing Order. A ScrBlt Primary Drawing Order
+ * paints a rectangle of image data using a raster operation which considers
+ * the source and destination. See:
*
- * @param context
- * The rdpContext associated with the current RDP session.
- *
- * @param patblt
- * The PATBLT update to handle.
- */
-void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt);
-
-/**
- * Handler for RDP SCRBLT update.
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegdi/a4e322b0-cd64-4dfc-8e1a-f24dc0edc99d
*
* @param context
* The rdpContext associated with the current RDP session.
*
* @param scrblt
* The SCRBLT update to handle.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt);
+BOOL guac_rdp_gdi_scrblt(rdpContext* context, const SCRBLT_ORDER* scrblt);
/**
- * Handler for RDP MEMBLT update.
+ * Handler for the MemBlt Primary Drawing Order. A MemBlt Primary Drawing Order
+ * paints a rectangle of cached image data from a cached surface to the screen
+ * using a raster operation which considers the source and destination. See:
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegdi/84c2ec2f-f776-405b-9b48-6894a28b1b14
*
* @param context
* The rdpContext associated with the current RDP session.
*
* @param memblt
* The MEMBLT update to handle.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt);
-
-/**
- * Handler for RDP OPAQUE RECT update.
- *
- * @param context
- * The rdpContext associated with the current RDP session.
- *
- * @param opaque_rect
- * The OPAQUE RECT update to handle.
- */
-void guac_rdp_gdi_opaquerect(rdpContext* context,
- OPAQUE_RECT_ORDER* opaque_rect);
-
-/**
- * Handler called when the remote color palette is changing.
- *
- * @param context
- * The rdpContext associated with the current RDP session.
- *
- * @param palette
- * The PALETTE update containing the new palette.
- */
-void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette);
+BOOL guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt);
/**
* Handler called prior to calling the handlers for specific updates when
@@ -124,8 +110,11 @@
* @param bounds
* The clipping rectangle to set, or NULL to remove any applied clipping
* rectangle.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds);
+BOOL guac_rdp_gdi_set_bounds(rdpContext* context, const rdpBounds* bounds);
/**
* Handler called when a paint operation is complete. We don't actually
@@ -133,8 +122,11 @@
*
* @param context
* The rdpContext associated with the current RDP session.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_gdi_end_paint(rdpContext* context);
+BOOL guac_rdp_gdi_end_paint(rdpContext* context);
/**
* Handler called when the desktop dimensions change, either from a
@@ -147,7 +139,10 @@
*
* @param context
* The rdpContext associated with the current RDP session.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_gdi_desktop_resize(rdpContext* context);
+BOOL guac_rdp_gdi_desktop_resize(rdpContext* context);
#endif
diff --git a/src/protocols/rdp/rdp_glyph.c b/src/protocols/rdp/glyph.c
similarity index 77%
rename from src/protocols/rdp/rdp_glyph.c
rename to src/protocols/rdp/glyph.c
index 8ba1087..d841384 100644
--- a/src/protocols/rdp/rdp_glyph.c
+++ b/src/protocols/rdp/glyph.c
@@ -17,25 +17,16 @@
* under the License.
*/
-#include "config.h"
-
-#include "client.h"
+#include "color.h"
#include "common/surface.h"
+#include "config.h"
+#include "glyph.h"
#include "rdp.h"
-#include "rdp_color.h"
-#include "rdp_glyph.h"
-#include "rdp_settings.h"
#include <freerdp/freerdp.h>
#include <guacamole/client.h>
-
-#ifdef ENABLE_WINPR
#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-wtypes.h"
-#endif
-#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
@@ -44,7 +35,7 @@
#define cairo_format_stride_for_width(format, width) (width*4)
#endif
-void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph) {
+BOOL guac_rdp_glyph_new(rdpContext* context, const rdpGlyph* glyph) {
int x, y, i;
int stride;
@@ -95,9 +86,15 @@
((guac_rdp_glyph*) glyph)->surface = cairo_image_surface_create_for_data(
image_buffer, CAIRO_FORMAT_ARGB32, width, height, stride);
+ return TRUE;
+
}
-void guac_rdp_glyph_draw(rdpContext* context, rdpGlyph* glyph, int x, int y) {
+BOOL guac_rdp_glyph_draw(rdpContext* context, const rdpGlyph* glyph,
+ GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
+ GLYPH_CALLBACK_INT32 w, GLYPH_CALLBACK_INT32 h,
+ GLYPH_CALLBACK_INT32 sx, GLYPH_CALLBACK_INT32 sy,
+ BOOL redundant) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
@@ -110,6 +107,8 @@
(fgcolor & 0x00FF00) >> 8,
fgcolor & 0x0000FF);
+ return TRUE;
+
}
void guac_rdp_glyph_free(rdpContext* context, rdpGlyph* glyph) {
@@ -121,17 +120,26 @@
cairo_surface_destroy(((guac_rdp_glyph*) glyph)->surface);
free(image_buffer);
+ /* NOTE: FreeRDP-allocated memory for the rdpGlyph will NOT be
+ * automatically released after this free handler is invoked, thus we must
+ * do so manually here */
+
+ free(glyph->aj);
+ free(glyph);
+
}
-void guac_rdp_glyph_begindraw(rdpContext* context,
- int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor) {
+BOOL guac_rdp_glyph_begindraw(rdpContext* context,
+ GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
+ GLYPH_CALLBACK_INT32 width, GLYPH_CALLBACK_INT32 height,
+ UINT32 fgcolor, UINT32 bgcolor, BOOL redundant) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client =
(guac_rdp_client*) client->data;
/* Fill background with color if specified */
- if (width != 0 && height != 0) {
+ if (width != 0 && height != 0 && !redundant) {
/* Convert background color */
bgcolor = guac_rdp_convert_color(context, bgcolor);
@@ -148,10 +156,15 @@
/* Convert foreground color */
rdp_client->glyph_color = guac_rdp_convert_color(context, fgcolor);
+ return TRUE;
+
}
-void guac_rdp_glyph_enddraw(rdpContext* context,
- int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor) {
+BOOL guac_rdp_glyph_enddraw(rdpContext* context,
+ GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
+ GLYPH_CALLBACK_INT32 width, GLYPH_CALLBACK_INT32 height,
+ UINT32 fgcolor, UINT32 bgcolor) {
/* IGNORE */
+ return TRUE;
}
diff --git a/src/protocols/rdp/rdp_glyph.h b/src/protocols/rdp/glyph.h
similarity index 71%
rename from src/protocols/rdp/rdp_glyph.h
rename to src/protocols/rdp/glyph.h
index 0fe0eef..85f2080 100644
--- a/src/protocols/rdp/rdp_glyph.h
+++ b/src/protocols/rdp/glyph.h
@@ -17,19 +17,28 @@
* under the License.
*/
-
-#ifndef _GUAC_RDP_RDP_GLYPH_H
-#define _GUAC_RDP_RDP_GLYPH_H
+#ifndef GUAC_RDP_GLYPH_H
+#define GUAC_RDP_GLYPH_H
#include "config.h"
#include <cairo/cairo.h>
#include <freerdp/freerdp.h>
-
-#ifdef ENABLE_WINPR
+#include <freerdp/graphics.h>
#include <winpr/wtypes.h>
+
+#ifdef FREERDP_GLYPH_CALLBACKS_ACCEPT_INT32
+/**
+ * FreeRDP 2.0.0-rc4 and newer requires INT32 for all integer arguments of
+ * glyph callbacks.
+ */
+#define GLYPH_CALLBACK_INT32 INT32
#else
-#include "compat/winpr-wtypes.h"
+/**
+ * FreeRDP 2.0.0-rc3 and older requires UINT32 for all integer arguments of
+ * glyph callbacks.
+ */
+#define GLYPH_CALLBACK_INT32 UINT32
#endif
/**
@@ -58,8 +67,11 @@
*
* @param glyph
* The glyph to cache.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph);
+BOOL guac_rdp_glyph_new(rdpContext* context, const rdpGlyph* glyph);
/**
* Draws a previously-cached glyph at the given coordinates within the current
@@ -76,8 +88,32 @@
*
* @param y
* The destination Y coordinate of the upper-left corner of the glyph.
+ *
+ * @param w
+ * The width of the glyph being drawn.
+ *
+ * @param h
+ * The height of the glyph being drawn.
+ *
+ * @param sx
+ * The X coordinare of the upper-left corner of the glyph within the source
+ * cache surface containing the glyph.
+ *
+ * @param sy
+ * The Y coordinare of the upper-left corner of the glyph within the source
+ * cache surface containing the glyph.
+ *
+ * @param redundant
+ * Whether the background rectangle specified is redundant (transparent).
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_glyph_draw(rdpContext* context, rdpGlyph* glyph, int x, int y);
+BOOL guac_rdp_glyph_draw(rdpContext* context, const rdpGlyph* glyph,
+ GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
+ GLYPH_CALLBACK_INT32 w, GLYPH_CALLBACK_INT32 h,
+ GLYPH_CALLBACK_INT32 sx, GLYPH_CALLBACK_INT32 sy,
+ BOOL redundant);
/**
* Frees any Guacamole-specific data associated with the given glyph, such that
@@ -125,9 +161,17 @@
* colorspace of the RDP session, and may even be a palette index, and must
* be translated via guac_rdp_convert_color(). If the background is
* transparent, this value is undefined.
+ *
+ * @param redundant
+ * Whether the background rectangle specified is redundant (transparent).
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_glyph_begindraw(rdpContext* context,
- int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor);
+BOOL guac_rdp_glyph_begindraw(rdpContext* context,
+ GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
+ GLYPH_CALLBACK_INT32 width, GLYPH_CALLBACK_INT32 height,
+ UINT32 fgcolor, UINT32 bgcolor, BOOL redundant);
/**
* Called immediately after rendering a series of glyphs. Unlike
@@ -162,8 +206,13 @@
* colorspace of the RDP session, and may even be a palette index, and must
* be translated via guac_rdp_convert_color(). If the background is
* transparent, this value is undefined.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_glyph_enddraw(rdpContext* context,
- int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor);
+BOOL guac_rdp_glyph_enddraw(rdpContext* context,
+ GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
+ GLYPH_CALLBACK_INT32 width, GLYPH_CALLBACK_INT32 height,
+ UINT32 fgcolor, UINT32 bgcolor);
#endif
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.c b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.c
deleted file mode 100644
index 54d3086..0000000
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.c
+++ /dev/null
@@ -1,529 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "config.h"
-
-#include "rdpdr_fs_messages_dir_info.h"
-#include "rdpdr_fs_messages_file_info.h"
-#include "rdpdr_fs_messages.h"
-#include "rdpdr_fs_messages_vol_info.h"
-#include "rdpdr_messages.h"
-#include "rdpdr_service.h"
-#include "rdp_fs.h"
-#include "rdp_status.h"
-#include "unicode.h"
-
-#include <freerdp/utils/svc_plugin.h>
-#include <guacamole/client.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-stream.h"
-#include "compat/winpr-wtypes.h"
-#endif
-
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
- wStream* input_stream, int completion_id) {
-
- wStream* output_stream;
- int file_id;
-
- int desired_access, file_attributes;
- int create_disposition, create_options, path_length;
- char path[GUAC_RDP_FS_MAX_PATH];
-
- /* Read "create" information */
- Stream_Read_UINT32(input_stream, desired_access);
- Stream_Seek_UINT64(input_stream); /* allocation size */
- Stream_Read_UINT32(input_stream, file_attributes);
- Stream_Seek_UINT32(input_stream); /* shared access */
- Stream_Read_UINT32(input_stream, create_disposition);
- Stream_Read_UINT32(input_stream, create_options);
- Stream_Read_UINT32(input_stream, path_length);
-
- /* Convert path to UTF-8 */
- guac_rdp_utf16_to_utf8(Stream_Pointer(input_stream), path_length/2 - 1,
- path, sizeof(path));
-
- /* Open file */
- file_id = guac_rdp_fs_open((guac_rdp_fs*) device->data, path,
- desired_access, file_attributes,
- create_disposition, create_options);
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i] "
- "desired_access=0x%x, file_attributes=0x%x, "
- "create_disposition=0x%x, create_options=0x%x, path=\"%s\"",
- __func__, file_id,
- desired_access, file_attributes,
- create_disposition, create_options, path);
-
- /* If an error occurred, notify server */
- if (file_id < 0) {
- guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
- "File open refused (%i): \"%s\"", file_id, path);
-
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- guac_rdp_fs_get_status(file_id), 5);
- Stream_Write_UINT32(output_stream, 0); /* fileId */
- Stream_Write_UINT8(output_stream, 0); /* information */
- }
-
- /* Otherwise, open succeeded */
- else {
-
- guac_rdp_fs_file* file;
-
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- STATUS_SUCCESS, 5);
- Stream_Write_UINT32(output_stream, file_id); /* fileId */
- Stream_Write_UINT8(output_stream, 0); /* information */
-
- /* Create \Download if it doesn't exist */
- file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
- if (file != NULL && strcmp(file->absolute_path, "\\") == 0) {
- int download_id =
- guac_rdp_fs_open((guac_rdp_fs*) device->data, "\\Download",
- ACCESS_GENERIC_READ, 0,
- DISP_FILE_OPEN_IF, FILE_DIRECTORY_FILE);
-
- if (download_id >= 0)
- guac_rdp_fs_close((guac_rdp_fs*) device->data, download_id);
- }
-
- }
-
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
-void guac_rdpdr_fs_process_read(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id) {
-
- UINT32 length;
- UINT64 offset;
- char* buffer;
- int bytes_read;
-
- wStream* output_stream;
-
- /* Read packet */
- Stream_Read_UINT32(input_stream, length);
- Stream_Read_UINT64(input_stream, offset);
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i] length=%i, offset=%" PRIu64,
- __func__, file_id, length, (uint64_t) offset);
-
- /* Ensure buffer size does not exceed a safe maximum */
- if (length > GUAC_RDP_MAX_READ_BUFFER)
- length = GUAC_RDP_MAX_READ_BUFFER;
-
- /* Allocate buffer */
- buffer = malloc(length);
-
- /* Attempt read */
- bytes_read = guac_rdp_fs_read((guac_rdp_fs*) device->data, file_id, offset,
- buffer, length);
-
- /* If error, return invalid parameter */
- if (bytes_read < 0) {
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- guac_rdp_fs_get_status(bytes_read), 4);
- Stream_Write_UINT32(output_stream, 0); /* Length */
- }
-
- /* Otherwise, send bytes read */
- else {
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- STATUS_SUCCESS, 4+bytes_read);
- Stream_Write_UINT32(output_stream, bytes_read); /* Length */
- Stream_Write(output_stream, buffer, bytes_read); /* ReadData */
- }
-
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
- free(buffer);
-
-}
-
-void guac_rdpdr_fs_process_write(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id) {
-
- UINT32 length;
- UINT64 offset;
- int bytes_written;
-
- wStream* output_stream;
-
- /* Read packet */
- Stream_Read_UINT32(input_stream, length);
- Stream_Read_UINT64(input_stream, offset);
- Stream_Seek(input_stream, 20); /* Padding */
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i] length=%i, offset=%" PRIu64,
- __func__, file_id, length, (uint64_t) offset);
-
- /* Attempt write */
- bytes_written = guac_rdp_fs_write((guac_rdp_fs*) device->data, file_id,
- offset, Stream_Pointer(input_stream), length);
-
- /* If error, return invalid parameter */
- if (bytes_written < 0) {
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- guac_rdp_fs_get_status(bytes_written), 5);
- Stream_Write_UINT32(output_stream, 0); /* Length */
- Stream_Write_UINT8(output_stream, 0); /* Padding */
- }
-
- /* Otherwise, send success */
- else {
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- STATUS_SUCCESS, 5);
- Stream_Write_UINT32(output_stream, bytes_written); /* Length */
- Stream_Write_UINT8(output_stream, 0); /* Padding */
- }
-
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
-void guac_rdpdr_fs_process_close(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id) {
-
- wStream* output_stream;
- guac_rdp_fs_file* file;
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i]",
- __func__, file_id);
-
- /* Get file */
- file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
- if (file == NULL)
- return;
-
- /* If file was written to, and it's in the \Download folder, start stream */
- if (file->bytes_written > 0 &&
- strncmp(file->absolute_path, "\\Download\\", 10) == 0) {
- guac_rdpdr_start_download(device, file->absolute_path);
- guac_rdp_fs_delete((guac_rdp_fs*) device->data, file_id);
- }
-
- /* Close file */
- guac_rdp_fs_close((guac_rdp_fs*) device->data, file_id);
-
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- STATUS_SUCCESS, 4);
- Stream_Write(output_stream, "\0\0\0\0", 4); /* Padding */
-
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
-void guac_rdpdr_fs_process_volume_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id) {
-
- int fs_information_class;
-
- Stream_Read_UINT32(input_stream, fs_information_class);
-
- /* Dispatch to appropriate class-specific handler */
- switch (fs_information_class) {
-
- case FileFsVolumeInformation:
- guac_rdpdr_fs_process_query_volume_info(device, input_stream,
- file_id, completion_id);
- break;
-
- case FileFsSizeInformation:
- guac_rdpdr_fs_process_query_size_info(device, input_stream,
- file_id, completion_id);
- break;
-
- case FileFsDeviceInformation:
- guac_rdpdr_fs_process_query_device_info(device, input_stream,
- file_id, completion_id);
- break;
-
- case FileFsAttributeInformation:
- guac_rdpdr_fs_process_query_attribute_info(device, input_stream,
- file_id, completion_id);
- break;
-
- case FileFsFullSizeInformation:
- guac_rdpdr_fs_process_query_full_size_info(device, input_stream,
- file_id, completion_id);
- break;
-
- default:
- guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
- "Unknown volume information class: 0x%x", fs_information_class);
- }
-
-}
-
-void guac_rdpdr_fs_process_file_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id) {
-
- int fs_information_class;
-
- Stream_Read_UINT32(input_stream, fs_information_class);
-
- /* Dispatch to appropriate class-specific handler */
- switch (fs_information_class) {
-
- case FileBasicInformation:
- guac_rdpdr_fs_process_query_basic_info(device, input_stream,
- file_id, completion_id);
- break;
-
- case FileStandardInformation:
- guac_rdpdr_fs_process_query_standard_info(device, input_stream,
- file_id, completion_id);
- break;
-
- case FileAttributeTagInformation:
- guac_rdpdr_fs_process_query_attribute_tag_info(device, input_stream,
- file_id, completion_id);
- break;
-
- default:
- guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
- "Unknown file information class: 0x%x", fs_information_class);
- }
-
-}
-
-void guac_rdpdr_fs_process_set_volume_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id) {
-
- wStream* output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_NOT_SUPPORTED, 0);
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i] Set volume info not supported",
- __func__, file_id);
-
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
-void guac_rdpdr_fs_process_set_file_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id) {
-
- int fs_information_class;
- int length;
-
- Stream_Read_UINT32(input_stream, fs_information_class);
- Stream_Read_UINT32(input_stream, length); /* Length */
- Stream_Seek(input_stream, 24); /* Padding */
-
- /* Dispatch to appropriate class-specific handler */
- switch (fs_information_class) {
-
- case FileBasicInformation:
- guac_rdpdr_fs_process_set_basic_info(device, input_stream,
- file_id, completion_id, length);
- break;
-
- case FileEndOfFileInformation:
- guac_rdpdr_fs_process_set_end_of_file_info(device, input_stream,
- file_id, completion_id, length);
- break;
-
- case FileDispositionInformation:
- guac_rdpdr_fs_process_set_disposition_info(device, input_stream,
- file_id, completion_id, length);
- break;
-
- case FileRenameInformation:
- guac_rdpdr_fs_process_set_rename_info(device, input_stream,
- file_id, completion_id, length);
- break;
-
- case FileAllocationInformation:
- guac_rdpdr_fs_process_set_allocation_info(device, input_stream,
- file_id, completion_id, length);
- break;
-
- default:
- guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
- "Unknown file information class: 0x%x",
- fs_information_class);
- }
-
-}
-
-void guac_rdpdr_fs_process_device_control(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id) {
-
- wStream* output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_INVALID_PARAMETER, 4);
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i] IGNORED",
- __func__, file_id);
-
- /* No content for now */
- Stream_Write_UINT32(output_stream, 0);
-
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
-void guac_rdpdr_fs_process_notify_change_directory(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id) {
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i] Not implemented",
- __func__, file_id);
-
-}
-
-void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id) {
-
- wStream* output_stream;
-
- guac_rdp_fs_file* file;
- int fs_information_class, initial_query;
- int path_length;
-
- const char* entry_name;
-
- /* Get file */
- file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
- if (file == NULL)
- return;
-
- /* Read main header */
- Stream_Read_UINT32(input_stream, fs_information_class);
- Stream_Read_UINT8(input_stream, initial_query);
- Stream_Read_UINT32(input_stream, path_length);
-
- /* If this is the first query, the path is included after padding */
- if (initial_query) {
-
- Stream_Seek(input_stream, 23); /* Padding */
-
- /* Convert path to UTF-8 */
- guac_rdp_utf16_to_utf8(Stream_Pointer(input_stream), path_length/2 - 1,
- file->dir_pattern, sizeof(file->dir_pattern));
-
- }
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i] initial_query=%i, dir_pattern=\"%s\"",
- __func__, file_id, initial_query, file->dir_pattern);
-
- /* Find first matching entry in directory */
- while ((entry_name = guac_rdp_fs_read_dir((guac_rdp_fs*) device->data,
- file_id)) != NULL) {
-
- /* Convert to absolute path */
- char entry_path[GUAC_RDP_FS_MAX_PATH];
- if (guac_rdp_fs_convert_path(file->absolute_path,
- entry_name, entry_path) == 0) {
-
- int entry_file_id;
-
- /* Pattern defined and match fails, continue with next file */
- if (guac_rdp_fs_matches(entry_path, file->dir_pattern))
- continue;
-
- /* Open directory entry */
- entry_file_id = guac_rdp_fs_open((guac_rdp_fs*) device->data,
- entry_path, ACCESS_FILE_READ_DATA, 0, DISP_FILE_OPEN, 0);
-
- if (entry_file_id >= 0) {
-
- /* Dispatch to appropriate class-specific handler */
- switch (fs_information_class) {
-
- case FileDirectoryInformation:
- guac_rdpdr_fs_process_query_directory_info(device,
- entry_name, entry_file_id, completion_id);
- break;
-
- case FileFullDirectoryInformation:
- guac_rdpdr_fs_process_query_full_directory_info(device,
- entry_name, entry_file_id, completion_id);
- break;
-
- case FileBothDirectoryInformation:
- guac_rdpdr_fs_process_query_both_directory_info(device,
- entry_name, entry_file_id, completion_id);
- break;
-
- case FileNamesInformation:
- guac_rdpdr_fs_process_query_names_info(device,
- entry_name, entry_file_id, completion_id);
- break;
-
- default:
- guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
- "Unknown dir information class: 0x%x",
- fs_information_class);
- }
-
- guac_rdp_fs_close((guac_rdp_fs*) device->data, entry_file_id);
- return;
-
- } /* end if file exists */
- } /* end if path valid */
- } /* end if entry exists */
-
- /*
- * Handle errors as a lack of files.
- */
-
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- STATUS_NO_MORE_FILES, 5);
-
- Stream_Write_UINT32(output_stream, 0); /* Length */
- Stream_Write_UINT8(output_stream, 0); /* Padding */
-
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
-void guac_rdpdr_fs_process_lock_control(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id) {
-
- wStream* output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_NOT_SUPPORTED, 5);
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i] Lock not supported",
- __func__, file_id);
-
- Stream_Zero(output_stream, 5); /* Padding */
-
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.h b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.h
deleted file mode 100644
index 4e484b4..0000000
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef __GUAC_RDPDR_FS_MESSAGES_H
-#define __GUAC_RDPDR_FS_MESSAGES_H
-
-/**
- * Handlers for core drive I/O requests. Requests handled here may be simple
- * messages handled directly, or more complex multi-type messages handled
- * elsewhere.
- *
- * @file rdpdr_fs_messages.h
- */
-
-#include "config.h"
-
-#include "rdpdr_service.h"
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-/**
- * Handles a Server Create Drive Request. Despite its name, this request opens
- * a file.
- */
-void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
- wStream* input_stream, int completion_id);
-
-/**
- * Handles a Server Close Drive Reqiest. This request closes an open file.
- */
-void guac_rdpdr_fs_process_close(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id);
-
-/**
- * Handles a Server Drive Read Request. This request reads from a file.
- */
-void guac_rdpdr_fs_process_read(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id);
-
-/**
- * Handles a Server Drive Write Request. This request writes to a file.
- */
-void guac_rdpdr_fs_process_write(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id);
-
-/**
- * Handles a Server Drive Control Request. This request handles one of any
- * number of Windows FSCTL_* control functions.
- */
-void guac_rdpdr_fs_process_device_control(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
-
-/**
- * Handles a Server Drive Query Volume Information Request. This request
- * queries information about the redirected volume (drive). This request
- * has several query types which have their own handlers defined in a
- * separate file.
- */
-void guac_rdpdr_fs_process_volume_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
-
-/**
- * Handles a Server Drive Set Volume Information Request. Currently, this
- * RDPDR implementation does not support setting of volume information.
- */
-void guac_rdpdr_fs_process_set_volume_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
-
-/**
- * Handles a Server Drive Query Information Request. This request queries
- * information about a specific file. This request has several query types
- * which have their own handlers defined in a separate file.
- */
-void guac_rdpdr_fs_process_file_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
-
-/**
- * Handles a Server Drive Set Information Request. This request sets
- * information about a specific file. Currently, this RDPDR implementation does
- * not support setting of file information.
- */
-void guac_rdpdr_fs_process_set_file_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
-
-/**
- * Handles a Server Drive Query Directory Request. This request queries
- * information about a specific directory. This request has several query types
- * which have their own handlers defined in a separate file.
- */
-void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
-
-/**
- * Handles a Server Drive NotifyChange Directory Request. This request requests
- * directory change notification.
- */
-void guac_rdpdr_fs_process_notify_change_directory(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id);
-
-/**
- * Handles a Server Drive Lock Control Request. This request locks or unlocks
- * portions of a file.
- */
-void guac_rdpdr_fs_process_lock_control(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
-
-#endif
-
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.h b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.h
deleted file mode 100644
index 06e2074..0000000
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef __GUAC_RDPDR_FS_MESSAGES_DIR_INFO_H
-#define __GUAC_RDPDR_FS_MESSAGES_DIR_INFO_H
-
-/**
- * Handlers for directory queries received over the RDPDR channel via the
- * IRP_MJ_DIRECTORY_CONTROL major function and the IRP_MN_QUERY_DIRECTORY minor
- * function.
- *
- * @file rdpdr_fs_messages_dir_info.h
- */
-
-#include "config.h"
-
-#include "rdpdr_service.h"
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-/**
- * Processes a query request for FileDirectoryInformation. From the
- * documentation this is "defined as the file's name, time stamp, and size, or its
- * attributes."
- */
-void guac_rdpdr_fs_process_query_directory_info(guac_rdpdr_device* device,
- const char* entry_name, int file_id, int completion_id);
-
-/**
- * Processes a query request for FileFullDirectoryInformation. From the
- * documentation, this is "defined as all the basic information, plus extended
- * attribute size."
- */
-void guac_rdpdr_fs_process_query_full_directory_info(guac_rdpdr_device* device,
- const char* entry_name, int file_id, int completion_id);
-
-/**
- * Processes a query request for FileBothDirectoryInformation. From the
- * documentation, this absurdly-named request is "basic information plus
- * extended attribute size and short name about a file or directory."
- */
-void guac_rdpdr_fs_process_query_both_directory_info(guac_rdpdr_device* device,
- const char* entry_name, int file_id, int completion_id);
-
-/**
- * Processes a query request for FileNamesInformation. From the documentation,
- * this is "detailed information on the names of files in a directory."
- */
-void guac_rdpdr_fs_process_query_names_info(guac_rdpdr_device* device,
- const char* entry_name, int file_id, int completion_id);
-
-#endif
-
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_file_info.c b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_file_info.c
deleted file mode 100644
index b23e7bd..0000000
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_file_info.c
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "config.h"
-
-#include "rdpdr_service.h"
-#include "rdp_fs.h"
-#include "rdp_status.h"
-#include "unicode.h"
-
-#include <freerdp/utils/svc_plugin.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-stream.h"
-#include "compat/winpr-wtypes.h"
-#endif
-
-#include <inttypes.h>
-#include <stdint.h>
-#include <string.h>
-
-void guac_rdpdr_fs_process_query_basic_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id) {
-
- wStream* output_stream;
- guac_rdp_fs_file* file;
-
- /* Get file */
- file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
- if (file == NULL)
- return;
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i]",
- __func__, file_id);
-
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- STATUS_SUCCESS, 40);
-
- Stream_Write_UINT32(output_stream, 36);
- Stream_Write_UINT64(output_stream, file->ctime); /* CreationTime */
- Stream_Write_UINT64(output_stream, file->atime); /* LastAccessTime */
- Stream_Write_UINT64(output_stream, file->mtime); /* LastWriteTime */
- Stream_Write_UINT64(output_stream, file->mtime); /* ChangeTime */
- Stream_Write_UINT32(output_stream, file->attributes); /* FileAttributes */
-
- /* Reserved field must not be sent */
-
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
-void guac_rdpdr_fs_process_query_standard_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id) {
-
- wStream* output_stream;
- guac_rdp_fs_file* file;
- BOOL is_directory = FALSE;
-
- /* Get file */
- file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
- if (file == NULL)
- return;
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i]",
- __func__, file_id);
-
- if (file->attributes & FILE_ATTRIBUTE_DIRECTORY)
- is_directory = TRUE;
-
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- STATUS_SUCCESS, 26);
-
- Stream_Write_UINT32(output_stream, 22);
- Stream_Write_UINT64(output_stream, file->size); /* AllocationSize */
- Stream_Write_UINT64(output_stream, file->size); /* EndOfFile */
- Stream_Write_UINT32(output_stream, 1); /* NumberOfLinks */
- Stream_Write_UINT8(output_stream, 0); /* DeletePending */
- Stream_Write_UINT8(output_stream, is_directory); /* Directory */
-
- /* Reserved field must not be sent */
-
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
-void guac_rdpdr_fs_process_query_attribute_tag_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id) {
-
- wStream* output_stream;
- guac_rdp_fs_file* file;
-
- /* Get file */
- file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
- if (file == NULL)
- return;
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i]",
- __func__, file_id);
-
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- STATUS_SUCCESS, 12);
-
- Stream_Write_UINT32(output_stream, 8);
- Stream_Write_UINT32(output_stream, file->attributes); /* FileAttributes */
- Stream_Write_UINT32(output_stream, 0); /* ReparseTag */
-
- /* Reserved field must not be sent */
-
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
-void guac_rdpdr_fs_process_set_rename_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id, int length) {
-
- int result;
- int filename_length;
- wStream* output_stream;
- char destination_path[GUAC_RDP_FS_MAX_PATH];
-
- /* Read structure */
- Stream_Seek_UINT8(input_stream); /* ReplaceIfExists */
- Stream_Seek_UINT8(input_stream); /* RootDirectory */
- Stream_Read_UINT32(input_stream, filename_length); /* FileNameLength */
-
- /* Convert name to UTF-8 */
- guac_rdp_utf16_to_utf8(Stream_Pointer(input_stream), filename_length/2,
- destination_path, sizeof(destination_path));
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i] destination_path=\"%s\"",
- __func__, file_id, destination_path);
-
- /* If file moving to \Download folder, start stream, do not move */
- if (strncmp(destination_path, "\\Download\\", 10) == 0) {
-
- guac_rdp_fs_file* file;
-
- /* Get file */
- file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
- if (file == NULL)
- return;
-
- /* Initiate download, pretend move succeeded */
- guac_rdpdr_start_download(device, file->absolute_path);
- output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_SUCCESS, 4);
-
- }
-
- /* Otherwise, rename as requested */
- else {
-
- result = guac_rdp_fs_rename((guac_rdp_fs*) device->data, file_id,
- destination_path);
- if (result < 0)
- output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, guac_rdp_fs_get_status(result), 4);
- else
- output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_SUCCESS, 4);
-
- }
-
- Stream_Write_UINT32(output_stream, length);
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
-void guac_rdpdr_fs_process_set_allocation_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id, int length) {
-
- int result;
- UINT64 size;
- wStream* output_stream;
-
- /* Read new size */
- Stream_Read_UINT64(input_stream, size); /* AllocationSize */
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i] size=%" PRIu64,
- __func__, file_id, (uint64_t) size);
-
- /* Truncate file */
- result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, file_id, size);
- if (result < 0)
- output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, guac_rdp_fs_get_status(result), 4);
- else
- output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_SUCCESS, 4);
-
- Stream_Write_UINT32(output_stream, length);
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
-void guac_rdpdr_fs_process_set_disposition_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id, int length) {
-
- wStream* output_stream;
-
- /* Delete file */
- int result = guac_rdp_fs_delete((guac_rdp_fs*) device->data, file_id);
- if (result < 0)
- output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, guac_rdp_fs_get_status(result), 4);
- else
- output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_SUCCESS, 4);
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i]",
- __func__, file_id);
-
- Stream_Write_UINT32(output_stream, length);
-
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
-void guac_rdpdr_fs_process_set_end_of_file_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id, int length) {
-
- int result;
- UINT64 size;
- wStream* output_stream;
-
- /* Read new size */
- Stream_Read_UINT64(input_stream, size); /* AllocationSize */
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i] size=%" PRIu64,
- __func__, file_id, (uint64_t) size);
-
- /* Truncate file */
- result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, file_id, size);
- if (result < 0)
- output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, guac_rdp_fs_get_status(result), 4);
- else
- output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_SUCCESS, 4);
-
- Stream_Write_UINT32(output_stream, length);
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
-void guac_rdpdr_fs_process_set_basic_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id, int length) {
-
- wStream* output_stream = guac_rdpdr_new_io_completion(device,
- completion_id, STATUS_SUCCESS, 4);
-
- /* Currently do nothing, just respond */
- Stream_Write_UINT32(output_stream, length);
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
- "%s: [file_id=%i] IGNORED",
- __func__, file_id);
-
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
-
-}
-
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_file_info.h b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_file_info.h
deleted file mode 100644
index aa53ead..0000000
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_file_info.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef __GUAC_RDPDR_FS_MESSAGES_FILE_INFO_H
-#define __GUAC_RDPDR_FS_MESSAGES_FILE_INFO_H
-
-/**
- * Handlers for file queries received over the RDPDR channel via the
- * IRP_MJ_QUERY_INFORMATION major function.
- *
- * @file rdpdr_fs_messages_file_info.h
- */
-
-#include "config.h"
-
-#include "rdpdr_service.h"
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-/**
- * Processes a query for FileBasicInformation. From the documentation, this is
- * "used to query a file for the times of creation, last access, last write,
- * and change, in addition to file attribute information."
- */
-void guac_rdpdr_fs_process_query_basic_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
-
-/**
- * Processes a query for FileStandardInformation. From the documentation, this
- * is "used to query for file information such as allocation size, end-of-file
- * position, and number of links."
- */
-void guac_rdpdr_fs_process_query_standard_info(guac_rdpdr_device* device, wStream* input_stream,
- int file_id, int completion_id);
-
-/**
- * Processes a query for FileAttributeTagInformation. From the documentation
- * this is "used to query for file attribute and reparse tag information."
- */
-void guac_rdpdr_fs_process_query_attribute_tag_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id);
-
-/**
- * Process a set operation for FileRenameInformation. From the documentation,
- * this operation is used to rename a file.
- */
-void guac_rdpdr_fs_process_set_rename_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id, int length);
-
-/**
- * Process a set operation for FileAllocationInformation. From the
- * documentation, this operation is used to set a file's allocation size.
- */
-void guac_rdpdr_fs_process_set_allocation_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id, int length);
-
-/**
- * Process a set operation for FileDispositionInformation. From the
- * documentation, this operation is used to mark a file for deletion.
- */
-void guac_rdpdr_fs_process_set_disposition_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id, int length);
-
-/**
- * Process a set operation for FileEndOfFileInformation. From the
- * documentation, this operation is used "to set end-of-file information for
- * a file."
- */
-void guac_rdpdr_fs_process_set_end_of_file_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id, int length);
-
-/**
- * Process a set operation for FileBasicInformation. From the documentation,
- * this is "used to set file information such as the times of creation, last
- * access, last write, and change, in addition to file attributes."
- */
-void guac_rdpdr_fs_process_set_basic_info(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id, int length);
-
-#endif
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_messages.c b/src/protocols/rdp/guac_rdpdr/rdpdr_messages.c
deleted file mode 100644
index 66626ad..0000000
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_messages.c
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "config.h"
-
-#include "rdp.h"
-#include "rdpdr_messages.h"
-#include "rdpdr_service.h"
-#include "unicode.h"
-
-#include <freerdp/utils/svc_plugin.h>
-#include <guacamole/client.h>
-#include <guacamole/unicode.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-static void guac_rdpdr_send_client_announce_reply(guac_rdpdrPlugin* rdpdr,
- unsigned int major, unsigned int minor, unsigned int client_id) {
-
- wStream* output_stream = Stream_New(NULL, 12);
-
- /* Write header */
- Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
- Stream_Write_UINT16(output_stream, PAKID_CORE_CLIENTID_CONFIRM);
-
- /* Write content */
- Stream_Write_UINT16(output_stream, major);
- Stream_Write_UINT16(output_stream, minor);
- Stream_Write_UINT32(output_stream, client_id);
-
- svc_plugin_send((rdpSvcPlugin*) rdpdr, output_stream);
-
-}
-
-static void guac_rdpdr_send_client_name_request(guac_rdpdrPlugin* rdpdr, const char* name) {
-
- int name_bytes = strlen(name) + 1;
- wStream* output_stream = Stream_New(NULL, 16 + name_bytes);
-
- /* Write header */
- Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
- Stream_Write_UINT16(output_stream, PAKID_CORE_CLIENT_NAME);
-
- /* Write content */
- Stream_Write_UINT32(output_stream, 0); /* ASCII */
- Stream_Write_UINT32(output_stream, 0); /* 0 required by RDPDR spec */
- Stream_Write_UINT32(output_stream, name_bytes);
- Stream_Write(output_stream, name, name_bytes);
-
- svc_plugin_send((rdpSvcPlugin*) rdpdr, output_stream);
-
-}
-
-static void guac_rdpdr_send_client_capability(guac_rdpdrPlugin* rdpdr) {
-
- wStream* output_stream = Stream_New(NULL, 256);
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Sending capabilities...");
-
- /* Write header */
- Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
- Stream_Write_UINT16(output_stream, PAKID_CORE_CLIENT_CAPABILITY);
-
- /* Capability count + padding */
- Stream_Write_UINT16(output_stream, 3);
- Stream_Write_UINT16(output_stream, 0); /* Padding */
-
- /* General capability header */
- Stream_Write_UINT16(output_stream, CAP_GENERAL_TYPE);
- Stream_Write_UINT16(output_stream, 44);
- Stream_Write_UINT32(output_stream, GENERAL_CAPABILITY_VERSION_02);
-
- /* General capability data */
- Stream_Write_UINT32(output_stream, GUAC_OS_TYPE); /* osType - required to be ignored */
- Stream_Write_UINT32(output_stream, 0); /* osVersion */
- Stream_Write_UINT16(output_stream, RDP_CLIENT_MAJOR_ALL); /* protocolMajor */
- Stream_Write_UINT16(output_stream, RDP_CLIENT_MINOR_5_2); /* protocolMinor */
- Stream_Write_UINT32(output_stream, 0xFFFF); /* ioCode1 */
- Stream_Write_UINT32(output_stream, 0); /* ioCode2 */
- Stream_Write_UINT32(output_stream,
- RDPDR_DEVICE_REMOVE_PDUS
- | RDPDR_CLIENT_DISPLAY_NAME
- | RDPDR_USER_LOGGEDON_PDU); /* extendedPDU */
- Stream_Write_UINT32(output_stream, 0); /* extraFlags1 */
- Stream_Write_UINT32(output_stream, 0); /* extraFlags2 */
- Stream_Write_UINT32(output_stream, 0); /* SpecialTypeDeviceCap */
-
- /* Printer support header */
- Stream_Write_UINT16(output_stream, CAP_PRINTER_TYPE);
- Stream_Write_UINT16(output_stream, 8);
- Stream_Write_UINT32(output_stream, PRINT_CAPABILITY_VERSION_01);
-
- /* Drive support header */
- Stream_Write_UINT16(output_stream, CAP_DRIVE_TYPE);
- Stream_Write_UINT16(output_stream, 8);
- Stream_Write_UINT32(output_stream, DRIVE_CAPABILITY_VERSION_02);
-
- svc_plugin_send((rdpSvcPlugin*) rdpdr, output_stream);
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Capabilities sent.");
-
-}
-
-static void guac_rdpdr_send_client_device_list_announce_request(guac_rdpdrPlugin* rdpdr) {
-
- /* Calculate number of bytes needed for the stream */
- int streamBytes = 16;
- for (int i=0; i < rdpdr->devices_registered; i++)
- streamBytes += rdpdr->devices[i].device_announce_len;
-
- /* Allocate the stream */
- wStream* output_stream = Stream_New(NULL, streamBytes);
-
- /* Write header */
- Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
- Stream_Write_UINT16(output_stream, PAKID_CORE_DEVICELIST_ANNOUNCE);
-
- /* Get the stream for each of the devices. */
- Stream_Write_UINT32(output_stream, rdpdr->devices_registered);
- for (int i=0; i<rdpdr->devices_registered; i++) {
-
- Stream_Write(output_stream,
- Stream_Buffer(rdpdr->devices[i].device_announce),
- rdpdr->devices[i].device_announce_len);
-
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Registered device %i (%s)",
- rdpdr->devices[i].device_id, rdpdr->devices[i].device_name);
-
- }
-
- svc_plugin_send((rdpSvcPlugin*) rdpdr, output_stream);
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "All supported devices sent.");
-
-}
-
-void guac_rdpdr_process_server_announce(guac_rdpdrPlugin* rdpdr,
- wStream* input_stream) {
-
- unsigned int major, minor, client_id;
-
- Stream_Read_UINT16(input_stream, major);
- Stream_Read_UINT16(input_stream, minor);
- Stream_Read_UINT32(input_stream, client_id);
-
- /* Must choose own client ID if minor not >= 12 */
- if (minor < 12)
- client_id = random() & 0xFFFF;
-
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Connected to RDPDR %u.%u as client 0x%04x", major, minor, client_id);
-
- /* Respond to announce */
- guac_rdpdr_send_client_announce_reply(rdpdr, major, minor, client_id);
-
- /* Name request */
- guac_rdpdr_send_client_name_request(rdpdr, ((guac_rdp_client *)rdpdr->client->data)->settings->client_name);
-
-}
-
-void guac_rdpdr_process_clientid_confirm(guac_rdpdrPlugin* rdpdr, wStream* input_stream) {
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Client ID confirmed");
-}
-
-void guac_rdpdr_process_device_reply(guac_rdpdrPlugin* rdpdr, wStream* input_stream) {
-
- unsigned int device_id, ntstatus;
- int severity, c, n, facility, code;
-
- Stream_Read_UINT32(input_stream, device_id);
- Stream_Read_UINT32(input_stream, ntstatus);
-
- severity = (ntstatus & 0xC0000000) >> 30;
- c = (ntstatus & 0x20000000) >> 29;
- n = (ntstatus & 0x10000000) >> 28;
- facility = (ntstatus & 0x0FFF0000) >> 16;
- code = ntstatus & 0x0000FFFF;
-
- /* Log error / information */
- if (device_id < rdpdr->devices_registered) {
-
- if (severity == 0x0)
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Device %i (%s) connected successfully",
- device_id, rdpdr->devices[device_id].device_name);
-
- else
- guac_client_log(rdpdr->client, GUAC_LOG_ERROR, "Problem connecting device %i (%s): "
- "severity=0x%x, c=0x%x, n=0x%x, facility=0x%x, code=0x%x",
- device_id, rdpdr->devices[device_id].device_name,
- severity, c, n, facility, code);
-
- }
-
- else
- guac_client_log(rdpdr->client, GUAC_LOG_ERROR, "Unknown device ID: 0x%08x", device_id);
-
-}
-
-void guac_rdpdr_process_device_iorequest(guac_rdpdrPlugin* rdpdr, wStream* input_stream) {
-
- int device_id, file_id, completion_id, major_func, minor_func;
-
- /* Read header */
- Stream_Read_UINT32(input_stream, device_id);
- Stream_Read_UINT32(input_stream, file_id);
- Stream_Read_UINT32(input_stream, completion_id);
- Stream_Read_UINT32(input_stream, major_func);
- Stream_Read_UINT32(input_stream, minor_func);
-
- /* If printer, run printer handlers */
- if (device_id >= 0 && device_id < rdpdr->devices_registered) {
-
- /* Call handler on device */
- guac_rdpdr_device* device = &(rdpdr->devices[device_id]);
- device->iorequest_handler(device, input_stream,
- file_id, completion_id, major_func, minor_func);
-
- }
-
- else
- guac_client_log(rdpdr->client, GUAC_LOG_ERROR, "Unknown device ID: 0x%08x", device_id);
-
-}
-
-void guac_rdpdr_process_server_capability(guac_rdpdrPlugin* rdpdr, wStream* input_stream) {
-
- int count;
- int i;
-
- /* Read header */
- Stream_Read_UINT16(input_stream, count);
- Stream_Seek(input_stream, 2);
-
- /* Parse capabilities */
- for (i=0; i<count; i++) {
-
- int type;
- int length;
-
- Stream_Read_UINT16(input_stream, type);
- Stream_Read_UINT16(input_stream, length);
-
- /* Ignore all for now */
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Ignoring server capability set type=0x%04x, length=%i", type, length);
- Stream_Seek(input_stream, length - 4);
-
- }
-
- /* Send own capabilities */
- guac_rdpdr_send_client_capability(rdpdr);
-
-}
-
-void guac_rdpdr_process_user_loggedon(guac_rdpdrPlugin* rdpdr, wStream* input_stream) {
-
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "User logged on");
- guac_rdpdr_send_client_device_list_announce_request(rdpdr);
-
-}
-
-void guac_rdpdr_process_prn_cache_data(guac_rdpdrPlugin* rdpdr, wStream* input_stream) {
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Ignoring printer cached configuration data");
-}
-
-void guac_rdpdr_process_prn_using_xps(guac_rdpdrPlugin* rdpdr, wStream* input_stream) {
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Printer unexpectedly switched to XPS mode");
-}
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_messages.h b/src/protocols/rdp/guac_rdpdr/rdpdr_messages.h
deleted file mode 100644
index 1e48924..0000000
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_messages.h
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef __GUAC_RDPDR_MESSAGES_H
-#define __GUAC_RDPDR_MESSAGES_H
-
-#include "config.h"
-
-#include "rdpdr_service.h"
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-#include <stdint.h>
-
-/**
- * Identifies the "core" component of RDPDR as the destination of the received
- * packet.
- */
-#define RDPDR_CTYP_CORE 0x4472
-
-/**
- * Identifies the printing component of RDPDR as the destination of the
- * received packet.
- */
-#define RDPDR_CTYP_PRN 0x5052
-
-/*
- * Packet IDs as required by the RDP spec (see: [MS-RDPEFS].pdf)
- */
-
-#define PAKID_CORE_SERVER_ANNOUNCE 0x496E
-#define PAKID_CORE_CLIENTID_CONFIRM 0x4343
-#define PAKID_CORE_CLIENT_NAME 0x434E
-#define PAKID_CORE_DEVICELIST_ANNOUNCE 0x4441
-#define PAKID_CORE_DEVICE_REPLY 0x6472
-#define PAKID_CORE_DEVICE_IOREQUEST 0x4952
-#define PAKID_CORE_DEVICE_IOCOMPLETION 0x4943
-#define PAKID_CORE_SERVER_CAPABILITY 0x5350
-#define PAKID_CORE_CLIENT_CAPABILITY 0x4350
-#define PAKID_CORE_DEVICELIST_REMOVE 0x444D
-#define PAKID_PRN_CACHE_DATA 0x5043
-#define PAKID_CORE_USER_LOGGEDON 0x554C
-#define PAKID_PRN_USING_XPS 0x5543
-
-/**
- * A 32-bit arbitrary value for the osType field of certain requests. As this
- * value is defined as completely arbitrary and required to be ignored by the
- * server, we send "GUAC" as an integer.
- */
-#define GUAC_OS_TYPE (*((uint32_t*) "GUAC"))
-
-/**
- * Name of the printer driver that should be used on the server.
- */
-#define GUAC_PRINTER_DRIVER "M\0S\0 \0P\0u\0b\0l\0i\0s\0h\0e\0r\0 \0I\0m\0a\0g\0e\0s\0e\0t\0t\0e\0r\0\0\0"
-#define GUAC_PRINTER_DRIVER_LENGTH 50
-
-/**
- * Label of the filesystem.
- */
-#define GUAC_FILESYSTEM_LABEL "G\0U\0A\0C\0F\0I\0L\0E\0"
-#define GUAC_FILESYSTEM_LABEL_LENGTH 16
-
-/*
- * Capability types
- */
-
-#define CAP_GENERAL_TYPE 1
-#define CAP_PRINTER_TYPE 2
-#define CAP_PORT_TYPE 3
-#define CAP_DRIVE_TYPE 4
-#define CAP_SMARTCARD_TYPE 5
-
-/*
- * General capability header versions.
- */
-
-#define GENERAL_CAPABILITY_VERSION_01 1
-#define GENERAL_CAPABILITY_VERSION_02 2
-
-/*
- * Print capability header versions.
- */
-
-#define PRINT_CAPABILITY_VERSION_01 1
-
-/*
- * Drive capability header versions.
- */
-#define DRIVE_CAPABILITY_VERSION_01 1
-#define DRIVE_CAPABILITY_VERSION_02 2
-
-/*
- * Legal client major version numbers.
- */
-
-#define RDP_CLIENT_MAJOR_ALL 1
-
-/*
- * Legal client minor version numbers.
- */
-
-#define RDP_CLIENT_MINOR_6_1 0xC
-#define RDP_CLIENT_MINOR_5_2 0xA
-#define RDP_CLIENT_MINOR_5_1 0x5
-#define RDP_CLIENT_MINOR_5_0 0x2
-
-/*
- * PDU flags used by the extendedPDU field.
- */
-
-#define RDPDR_DEVICE_REMOVE_PDUS 0x1
-#define RDPDR_CLIENT_DISPLAY_NAME 0x2
-#define RDPDR_USER_LOGGEDON_PDU 0x4
-
-/*
- * Device types.
- */
-
-#define RDPDR_DTYP_SERIAL 0x00000001
-#define RDPDR_DTYP_PARALLEL 0x00000002
-#define RDPDR_DTYP_PRINT 0x00000004
-#define RDPDR_DTYP_FILESYSTEM 0x00000008
-#define RDPDR_DTYP_SMARTCARD 0x00000020
-
-/*
- * Printer flags.
- */
-
-#define RDPDR_PRINTER_ANNOUNCE_FLAG_ASCII 0x00000001
-#define RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER 0x00000002
-#define RDPDR_PRINTER_ANNOUNCE_FLAG_NETWORKPRINTER 0x00000004
-#define RDPDR_PRINTER_ANNOUNCE_FLAG_TSPRINTER 0x00000008
-#define RDPDR_PRINTER_ANNOUNCE_FLAG_XPSFORMAT 0x00000010
-
-/*
- * I/O requests.
- */
-
-#define IRP_MJ_CREATE 0x00000000
-#define IRP_MJ_CLOSE 0x00000002
-#define IRP_MJ_READ 0x00000003
-#define IRP_MJ_WRITE 0x00000004
-#define IRP_MJ_DEVICE_CONTROL 0x0000000E
-#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0000000A
-#define IRP_MJ_SET_VOLUME_INFORMATION 0x0000000B
-#define IRP_MJ_QUERY_INFORMATION 0x00000005
-#define IRP_MJ_SET_INFORMATION 0x00000006
-#define IRP_MJ_DIRECTORY_CONTROL 0x0000000C
-#define IRP_MJ_LOCK_CONTROL 0x00000011
-
-#define IRP_MN_QUERY_DIRECTORY 0x00000001
-#define IRP_MN_NOTIFY_CHANGE_DIRECTORY 0x00000002
-
-/*
- * Volume information constants.
- */
-
-#define FileFsVolumeInformation 0x00000001
-#define FileFsSizeInformation 0x00000003
-#define FileFsDeviceInformation 0x00000004
-#define FileFsAttributeInformation 0x00000005
-#define FileFsFullSizeInformation 0x00000007
-
-/*
- * File information constants.
- */
-
-#define FileBasicInformation 0x00000004
-#define FileStandardInformation 0x00000005
-#define FileRenameInformation 0x0000000A
-#define FileDispositionInformation 0x0000000D
-#define FileAllocationInformation 0x00000013
-#define FileEndOfFileInformation 0x00000014
-#define FileAttributeTagInformation 0x00000023
-
-/*
- * Directory information constants.
- */
-
-#define FileDirectoryInformation 0x00000001
-#define FileFullDirectoryInformation 0x00000002
-#define FileBothDirectoryInformation 0x00000003
-#define FileNamesInformation 0x0000000C
-
-/*
- * Message handlers.
- */
-
-void guac_rdpdr_process_server_announce(guac_rdpdrPlugin* rdpdr, wStream* input_stream);
-void guac_rdpdr_process_clientid_confirm(guac_rdpdrPlugin* rdpdr, wStream* input_stream);
-void guac_rdpdr_process_device_reply(guac_rdpdrPlugin* rdpdr, wStream* input_stream);
-void guac_rdpdr_process_device_iorequest(guac_rdpdrPlugin* rdpdr, wStream* input_stream);
-void guac_rdpdr_process_server_capability(guac_rdpdrPlugin* rdpdr, wStream* input_stream);
-void guac_rdpdr_process_user_loggedon(guac_rdpdrPlugin* rdpdr, wStream* input_stream);
-void guac_rdpdr_process_prn_cache_data(guac_rdpdrPlugin* rdpdr, wStream* input_stream);
-void guac_rdpdr_process_prn_using_xps(guac_rdpdrPlugin* rdpdr, wStream* input_stream);
-
-#endif
-
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h
deleted file mode 100644
index 9dbeb42..0000000
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef __GUAC_RDPDR_PRINTER_H
-#define __GUAC_RDPDR_PRINTER_H
-
-#include "config.h"
-
-#include "rdpdr_service.h"
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-/**
- * Registers a new printer device within the RDPDR plugin. This must be done
- * before RDPDR connection finishes.
- *
- * @param rdpdr
- * The RDP device redirection plugin where the device is registered.
- *
- * @param printer_name
- * The name of the printer that will be registered with the RDP
- * connection and passed through to the server.
- */
-void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr, char* printer_name);
-
-#endif
-
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_service.c b/src/protocols/rdp/guac_rdpdr/rdpdr_service.c
deleted file mode 100644
index 4901dec..0000000
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_service.c
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "config.h"
-
-#include "rdp.h"
-#include "rdp_fs.h"
-#include "rdp_settings.h"
-#include "rdp_stream.h"
-#include "rdpdr_fs_service.h"
-#include "rdpdr_messages.h"
-#include "rdpdr_printer.h"
-#include "rdpdr_service.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <freerdp/constants.h>
-#include <freerdp/utils/svc_plugin.h>
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-#include <guacamole/socket.h>
-#include <guacamole/stream.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-/**
- * Entry point for RDPDR virtual channel.
- */
-int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) {
-
- /* Allocate plugin */
- guac_rdpdrPlugin* rdpdr =
- (guac_rdpdrPlugin*) calloc(1, sizeof(guac_rdpdrPlugin));
-
- /* Init channel def */
- strcpy(rdpdr->plugin.channel_def.name, "rdpdr");
- rdpdr->plugin.channel_def.options =
- CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
-
- /* Set callbacks */
- rdpdr->plugin.connect_callback = guac_rdpdr_process_connect;
- rdpdr->plugin.receive_callback = guac_rdpdr_process_receive;
- rdpdr->plugin.event_callback = guac_rdpdr_process_event;
- rdpdr->plugin.terminate_callback = guac_rdpdr_process_terminate;
-
- /* Finish init */
- svc_plugin_init((rdpSvcPlugin*) rdpdr, pEntryPoints);
- return 1;
-
-}
-
-/*
- * Service Handlers
- */
-
-void guac_rdpdr_process_connect(rdpSvcPlugin* plugin) {
-
- /* Get RDPDR plugin */
- guac_rdpdrPlugin* rdpdr = (guac_rdpdrPlugin*) plugin;
-
- /* Get client from plugin parameters */
- guac_client* client = (guac_client*)
- plugin->channel_entry_points.pExtendedData;
-
- /* NULL out pExtendedData so we don't lose our guac_client due to an
- * automatic free() within libfreerdp */
- plugin->channel_entry_points.pExtendedData = NULL;
-
- /* Get data from client */
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
-
- /* Init plugin */
- rdpdr->client = client;
- rdpdr->devices_registered = 0;
-
- /* Register printer if enabled */
- if (rdp_client->settings->printing_enabled)
- guac_rdpdr_register_printer(rdpdr, rdp_client->settings->printer_name);
-
- /* Register drive if enabled */
- if (rdp_client->settings->drive_enabled)
- guac_rdpdr_register_fs(rdpdr, rdp_client->settings->drive_name);
-
- /* Log that printing, etc. has been loaded */
- guac_client_log(client, GUAC_LOG_INFO, "guacdr connected.");
-
-}
-
-void guac_rdpdr_process_terminate(rdpSvcPlugin* plugin) {
-
- guac_rdpdrPlugin* rdpdr = (guac_rdpdrPlugin*) plugin;
- int i;
-
- for (i=0; i<rdpdr->devices_registered; i++) {
- guac_rdpdr_device* device = &(rdpdr->devices[i]);
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Unloading device %i (%s)",
- device->device_id, device->device_name);
- device->free_handler(device);
- }
-
- free(plugin);
-}
-
-void guac_rdpdr_process_event(rdpSvcPlugin* plugin, wMessage* event) {
- freerdp_event_free(event);
-}
-
-void guac_rdpdr_process_receive(rdpSvcPlugin* plugin,
- wStream* input_stream) {
-
- guac_rdpdrPlugin* rdpdr = (guac_rdpdrPlugin*) plugin;
-
- int component;
- int packet_id;
-
- /* Read header */
- Stream_Read_UINT16(input_stream, component);
- Stream_Read_UINT16(input_stream, packet_id);
-
- /* Core component */
- if (component == RDPDR_CTYP_CORE) {
-
- /* Dispatch handlers based on packet ID */
- switch (packet_id) {
-
- case PAKID_CORE_SERVER_ANNOUNCE:
- guac_rdpdr_process_server_announce(rdpdr, input_stream);
- break;
-
- case PAKID_CORE_CLIENTID_CONFIRM:
- guac_rdpdr_process_clientid_confirm(rdpdr, input_stream);
- break;
-
- case PAKID_CORE_DEVICE_REPLY:
- guac_rdpdr_process_device_reply(rdpdr, input_stream);
- break;
-
- case PAKID_CORE_DEVICE_IOREQUEST:
- guac_rdpdr_process_device_iorequest(rdpdr, input_stream);
- break;
-
- case PAKID_CORE_SERVER_CAPABILITY:
- guac_rdpdr_process_server_capability(rdpdr, input_stream);
- break;
-
- case PAKID_CORE_USER_LOGGEDON:
- guac_rdpdr_process_user_loggedon(rdpdr, input_stream);
- break;
-
- default:
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Ignoring RDPDR core packet with unexpected ID: 0x%04x", packet_id);
-
- }
-
- } /* end if core */
-
- /* Printer component */
- else if (component == RDPDR_CTYP_PRN) {
-
- /* Dispatch handlers based on packet ID */
- switch (packet_id) {
-
- case PAKID_PRN_CACHE_DATA:
- guac_rdpdr_process_prn_cache_data(rdpdr, input_stream);
- break;
-
- case PAKID_PRN_USING_XPS:
- guac_rdpdr_process_prn_using_xps(rdpdr, input_stream);
- break;
-
- default:
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Ignoring RDPDR printer packet with unexpected ID: 0x%04x", packet_id);
-
- }
-
- } /* end if printer */
-
- else
- guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Ignoring packet for unknown RDPDR component: 0x%04x", component);
-
-}
-
-wStream* guac_rdpdr_new_io_completion(guac_rdpdr_device* device,
- int completion_id, int status, int size) {
-
- wStream* output_stream = Stream_New(NULL, 16+size);
-
- /* Write header */
- Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
- Stream_Write_UINT16(output_stream, PAKID_CORE_DEVICE_IOCOMPLETION);
-
- /* Write content */
- Stream_Write_UINT32(output_stream, device->device_id);
- Stream_Write_UINT32(output_stream, completion_id);
- Stream_Write_UINT32(output_stream, status);
-
- return output_stream;
-
-}
-
-/**
- * Callback invoked on the current connection owner (if any) when a file
- * download is being initiated using the magic "Download" folder.
- *
- * @param owner
- * The guac_user that is the owner of the connection, or NULL if the
- * connection owner has left.
- *
- * @param data
- * The full absolute path to the file that should be downloaded.
- *
- * @return
- * The stream allocated for the file download, or NULL if the download has
- * failed to start.
- */
-static void* guac_rdpdr_download_to_owner(guac_user* owner, void* data) {
-
- /* Do not bother attempting the download if the owner has left */
- if (owner == NULL)
- return NULL;
-
- guac_client* client = owner->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- guac_rdp_fs* filesystem = rdp_client->filesystem;
-
- /* Ignore download if filesystem has been unloaded */
- if (filesystem == NULL)
- return NULL;
-
- /* Attempt to open requested file */
- char* path = (char*) data;
- int file_id = guac_rdp_fs_open(filesystem, path,
- ACCESS_FILE_READ_DATA, 0, DISP_FILE_OPEN, 0);
-
- /* If file opened successfully, start stream */
- if (file_id >= 0) {
-
- guac_rdp_stream* rdp_stream;
- const char* basename;
-
- int i;
- char c;
-
- /* Associate stream with transfer status */
- guac_stream* stream = guac_user_alloc_stream(owner);
- stream->data = rdp_stream = malloc(sizeof(guac_rdp_stream));
- stream->ack_handler = guac_rdp_download_ack_handler;
- rdp_stream->type = GUAC_RDP_DOWNLOAD_STREAM;
- rdp_stream->download_status.file_id = file_id;
- rdp_stream->download_status.offset = 0;
-
- /* Get basename from absolute path */
- i=0;
- basename = path;
- do {
-
- c = path[i];
- if (c == '/' || c == '\\')
- basename = &(path[i+1]);
-
- i++;
-
- } while (c != '\0');
-
- guac_user_log(owner, GUAC_LOG_DEBUG, "%s: Initiating download "
- "of \"%s\"", __func__, path);
-
- /* Begin stream */
- guac_protocol_send_file(owner->socket, stream,
- "application/octet-stream", basename);
- guac_socket_flush(owner->socket);
-
- /* Download started successfully */
- return stream;
-
- }
-
- /* Download failed */
- guac_user_log(owner, GUAC_LOG_ERROR, "Unable to download \"%s\"", path);
- return NULL;
-
-}
-
-void guac_rdpdr_start_download(guac_rdpdr_device* device, char* path) {
-
- guac_client* client = device->rdpdr->client;
-
- /* Initiate download to the owner of the connection */
- guac_client_for_owner(client, guac_rdpdr_download_to_owner, path);
-
-}
-
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_service.h b/src/protocols/rdp/guac_rdpdr/rdpdr_service.h
deleted file mode 100644
index ea5cb57..0000000
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_service.h
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef __GUAC_RDPDR_SERVICE_H
-#define __GUAC_RDPDR_SERVICE_H
-
-#include "config.h"
-
-#include <freerdp/utils/svc_plugin.h>
-#include <guacamole/client.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-/**
- * The maximum number of bytes to allow for a device read.
- */
-#define GUAC_RDP_MAX_READ_BUFFER 4194304
-
-typedef struct guac_rdpdrPlugin guac_rdpdrPlugin;
-typedef struct guac_rdpdr_device guac_rdpdr_device;
-
-/**
- * Handler for client device list announce. Each implementing device must write
- * its announcement header and data to the given output stream.
- */
-typedef void guac_rdpdr_device_announce_handler(guac_rdpdr_device* device, wStream* output_stream,
- int device_id);
-
-/**
- * Handler for device I/O requests.
- */
-typedef void guac_rdpdr_device_iorequest_handler(guac_rdpdr_device* device,
- wStream* input_stream, int file_id, int completion_id, int major_func, int minor_func);
-
-/**
- * Handler for cleaning up the dynamically-allocated portions of a device.
- */
-typedef void guac_rdpdr_device_free_handler(guac_rdpdr_device* device);
-
-/**
- * Arbitrary device forwarded over the RDPDR channel.
- */
-struct guac_rdpdr_device {
-
- /**
- * The RDPDR plugin owning this device.
- */
- guac_rdpdrPlugin* rdpdr;
-
- /**
- * The ID assigned to this device by the RDPDR plugin.
- */
- int device_id;
-
- /**
- * Device name, used for logging and for passthrough to the
- * server.
- */
- const char* device_name;
-
- /**
- * The type of RDPDR device that this represents.
- */
- uint32_t device_type;
-
- /**
- * The DOS name of the device. Max 8 bytes, including terminator.
- */
- const char *dos_name;
-
- /**
- * The stream that stores the RDPDR device announcement for this device.
- */
- wStream* device_announce;
-
- /**
- * The length of the device_announce wStream.
- */
- int device_announce_len;
-
- /**
- * Handler which should be called for every I/O request received.
- */
- guac_rdpdr_device_iorequest_handler* iorequest_handler;
-
- /**
- * Handler which should be called when the device is being freed.
- */
- guac_rdpdr_device_free_handler* free_handler;
-
- /**
- * Arbitrary data, used internally by the handlers for this device.
- */
- void* data;
-
-};
-
-/**
- * Structure representing the current state of the Guacamole RDPDR plugin for
- * FreeRDP.
- */
-struct guac_rdpdrPlugin {
-
- /**
- * The FreeRDP parts of this plugin. This absolutely MUST be first.
- * FreeRDP depends on accessing this structure as if it were an instance
- * of rdpSvcPlugin.
- */
- rdpSvcPlugin plugin;
-
- /**
- * Reference to the client owning this instance of the RDPDR plugin.
- */
- guac_client* client;
-
- /**
- * The number of devices registered within the devices array.
- */
- int devices_registered;
-
- /**
- * Array of registered devices.
- */
- guac_rdpdr_device devices[8];
-
-};
-
-/**
- * Handler called when this plugin is loaded by FreeRDP.
- */
-void guac_rdpdr_process_connect(rdpSvcPlugin* plugin);
-
-/**
- * Handler called when this plugin receives data along its designated channel.
- */
-void guac_rdpdr_process_receive(rdpSvcPlugin* plugin,
- wStream* input_stream);
-
-/**
- * Handler called when this plugin is being unloaded.
- */
-void guac_rdpdr_process_terminate(rdpSvcPlugin* plugin);
-
-/**
- * Handler called when this plugin receives an event. For the sake of RDPDR,
- * all events will be ignored and simply free'd.
- */
-void guac_rdpdr_process_event(rdpSvcPlugin* plugin, wMessage* event);
-
-/**
- * Creates a new stream which contains the common DR_DEVICE_IOCOMPLETION header
- * used for virtually all responses.
- */
-wStream* guac_rdpdr_new_io_completion(guac_rdpdr_device* device,
- int completion_id, int status, int size);
-
-/**
- * Begins streaming the given file to the user via a Guacamole file stream.
- */
-void guac_rdpdr_start_download(guac_rdpdr_device* device, char* path);
-
-#endif
-
diff --git a/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.c b/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.c
deleted file mode 100644
index 45de442..0000000
--- a/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "config.h"
-
-#include "rdpsnd_service.h"
-#include "rdpsnd_messages.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <freerdp/constants.h>
-#include <freerdp/utils/svc_plugin.h>
-#include <guacamole/client.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-/**
- * Entry point for RDPSND virtual channel.
- */
-int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) {
-
- /* Allocate plugin */
- guac_rdpsndPlugin* rdpsnd =
- (guac_rdpsndPlugin*) calloc(1, sizeof(guac_rdpsndPlugin));
-
- /* Init channel def */
- strcpy(rdpsnd->plugin.channel_def.name, "rdpsnd");
- rdpsnd->plugin.channel_def.options =
- CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP;
-
- /* Set callbacks */
- rdpsnd->plugin.connect_callback = guac_rdpsnd_process_connect;
- rdpsnd->plugin.receive_callback = guac_rdpsnd_process_receive;
- rdpsnd->plugin.event_callback = guac_rdpsnd_process_event;
- rdpsnd->plugin.terminate_callback = guac_rdpsnd_process_terminate;
-
- /* Finish init */
- svc_plugin_init((rdpSvcPlugin*) rdpsnd, pEntryPoints);
- return 1;
-
-}
-
-/*
- * Service Handlers
- */
-
-void guac_rdpsnd_process_connect(rdpSvcPlugin* plugin) {
-
- guac_rdpsndPlugin* rdpsnd = (guac_rdpsndPlugin*) plugin;
-
- /* Get client from plugin parameters */
- guac_client* client = rdpsnd->client =
- (guac_client*) plugin->channel_entry_points.pExtendedData;
-
- /* NULL out pExtendedData so we don't lose our guac_client due to an
- * automatic free() within libfreerdp */
- plugin->channel_entry_points.pExtendedData = NULL;
-
-#ifdef RDPSVCPLUGIN_INTERVAL_MS
- /* Update every 10 ms */
- plugin->interval_ms = 10;
-#endif
-
- /* Log that sound has been loaded */
- guac_client_log(client, GUAC_LOG_INFO, "guacsnd connected.");
-
-}
-
-void guac_rdpsnd_process_terminate(rdpSvcPlugin* plugin) {
- free(plugin);
-}
-
-void guac_rdpsnd_process_event(rdpSvcPlugin* plugin, wMessage* event) {
- freerdp_event_free(event);
-}
-
-void guac_rdpsnd_process_receive(rdpSvcPlugin* plugin,
- wStream* input_stream) {
-
- guac_rdpsndPlugin* rdpsnd = (guac_rdpsndPlugin*) plugin;
- guac_rdpsnd_pdu_header header;
-
- /* Read RDPSND PDU header */
- Stream_Read_UINT8(input_stream, header.message_type);
- Stream_Seek_UINT8(input_stream);
- Stream_Read_UINT16(input_stream, header.body_size);
-
- /*
- * If next PDU is SNDWAVE (due to receiving WaveInfo PDU previously),
- * ignore the header and parse as a Wave PDU.
- */
- if (rdpsnd->next_pdu_is_wave)
- guac_rdpsnd_wave_handler(rdpsnd, input_stream, &header);
-
- /* Dispatch message to standard handlers */
- else {
- switch (header.message_type) {
-
- /* Server Audio Formats and Version PDU */
- case SNDC_FORMATS:
- guac_rdpsnd_formats_handler(rdpsnd, input_stream, &header);
- break;
-
- /* Training PDU */
- case SNDC_TRAINING:
- guac_rdpsnd_training_handler(rdpsnd, input_stream, &header);
- break;
-
- /* WaveInfo PDU */
- case SNDC_WAVE:
- guac_rdpsnd_wave_info_handler(rdpsnd, input_stream, &header);
- break;
-
- /* Close PDU */
- case SNDC_CLOSE:
- guac_rdpsnd_close_handler(rdpsnd, input_stream, &header);
- break;
-
- }
- }
-
- Stream_Free(input_stream, TRUE);
-}
-
diff --git a/src/protocols/rdp/guac_svc/svc_service.c b/src/protocols/rdp/guac_svc/svc_service.c
deleted file mode 100644
index c40c6c5..0000000
--- a/src/protocols/rdp/guac_svc/svc_service.c
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "config.h"
-
-#include "svc_service.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <freerdp/constants.h>
-#include <freerdp/utils/svc_plugin.h>
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-#include <guacamole/socket.h>
-#include <guacamole/string.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-/**
- * Entry point for arbitrary SVC.
- */
-int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) {
-
- /* Gain access to plugin data */
- CHANNEL_ENTRY_POINTS_FREERDP* entry_points_ex =
- (CHANNEL_ENTRY_POINTS_FREERDP*) pEntryPoints;
-
- /* Allocate plugin */
- guac_svcPlugin* svc_plugin =
- (guac_svcPlugin*) calloc(1, sizeof(guac_svcPlugin));
-
- /* Get SVC descriptor from plugin parameters */
- guac_rdp_svc* svc = (guac_rdp_svc*) entry_points_ex->pExtendedData;
-
- /* Init channel def */
- guac_strlcpy(svc_plugin->plugin.channel_def.name, svc->name,
- GUAC_RDP_SVC_MAX_LENGTH);
- svc_plugin->plugin.channel_def.options =
- CHANNEL_OPTION_INITIALIZED
- | CHANNEL_OPTION_ENCRYPT_RDP
- | CHANNEL_OPTION_COMPRESS_RDP;
-
- /* Init plugin */
- svc_plugin->svc = svc;
-
- /* Set callbacks */
- svc_plugin->plugin.connect_callback = guac_svc_process_connect;
- svc_plugin->plugin.receive_callback = guac_svc_process_receive;
- svc_plugin->plugin.event_callback = guac_svc_process_event;
- svc_plugin->plugin.terminate_callback = guac_svc_process_terminate;
-
- /* Store plugin reference in SVC */
- svc->plugin = (rdpSvcPlugin*) svc_plugin;
-
- /* Finish init */
- svc_plugin_init((rdpSvcPlugin*) svc_plugin, pEntryPoints);
- return 1;
-
-}
-
-/*
- * Service Handlers
- */
-
-void guac_svc_process_connect(rdpSvcPlugin* plugin) {
-
- /* Get corresponding guac_rdp_svc */
- guac_svcPlugin* svc_plugin = (guac_svcPlugin*) plugin;
- guac_rdp_svc* svc = svc_plugin->svc;
-
- /* NULL out pExtendedData so we don't lose our guac_rdp_svc due to an
- * automatic free() within libfreerdp */
- plugin->channel_entry_points.pExtendedData = NULL;
-
- /* Create pipe */
- svc->output_pipe = guac_client_alloc_stream(svc->client);
-
- /* Notify of pipe's existence */
- guac_rdp_svc_send_pipe(svc->client->socket, svc);
-
- /* Log connection to static channel */
- guac_client_log(svc->client, GUAC_LOG_INFO,
- "Static channel \"%s\" connected.", svc->name);
-
-}
-
-void guac_svc_process_terminate(rdpSvcPlugin* plugin) {
-
- /* Get corresponding guac_rdp_svc */
- guac_svcPlugin* svc_plugin = (guac_svcPlugin*) plugin;
- guac_rdp_svc* svc = svc_plugin->svc;
-
- /* Remove and free SVC */
- guac_client_log(svc->client, GUAC_LOG_INFO, "Closing channel \"%s\"...", svc->name);
- guac_rdp_remove_svc(svc->client, svc->name);
- free(svc);
-
- free(plugin);
-
-}
-
-void guac_svc_process_event(rdpSvcPlugin* plugin, wMessage* event) {
- freerdp_event_free(event);
-}
-
-void guac_svc_process_receive(rdpSvcPlugin* plugin,
- wStream* input_stream) {
-
- /* Get corresponding guac_rdp_svc */
- guac_svcPlugin* svc_plugin = (guac_svcPlugin*) plugin;
- guac_rdp_svc* svc = svc_plugin->svc;
-
- /* Fail if output not created */
- if (svc->output_pipe == NULL) {
- guac_client_log(svc->client, GUAC_LOG_ERROR,
- "Output for channel \"%s\" dropped.",
- svc->name);
- return;
- }
-
- /* Send blob */
- guac_protocol_send_blob(svc->client->socket, svc->output_pipe,
- Stream_Buffer(input_stream),
- Stream_Length(input_stream));
-
- guac_socket_flush(svc->client->socket);
-
-}
-
diff --git a/src/protocols/rdp/guac_svc/svc_service.h b/src/protocols/rdp/guac_svc/svc_service.h
deleted file mode 100644
index 219aaec..0000000
--- a/src/protocols/rdp/guac_svc/svc_service.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#ifndef __GUAC_SVC_SERVICE_H
-#define __GUAC_SVC_SERVICE_H
-
-#include "config.h"
-#include "rdp_svc.h"
-
-#include <freerdp/utils/svc_plugin.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-/**
- * Structure representing the current state of an arbitrary static virtual
- * channel.
- */
-typedef struct guac_svcPlugin {
-
- /**
- * The FreeRDP parts of this plugin. This absolutely MUST be first.
- * FreeRDP depends on accessing this structure as if it were an instance
- * of rdpSvcPlugin.
- */
- rdpSvcPlugin plugin;
-
- /**
- * The Guacamole-specific SVC structure describing the channel this
- * instance represents.
- */
- guac_rdp_svc* svc;
-
-} guac_svcPlugin;
-
-/**
- * Handler called when this plugin is loaded by FreeRDP.
- */
-void guac_svc_process_connect(rdpSvcPlugin* plugin);
-
-/**
- * Handler called when this plugin receives data along its designated channel.
- */
-void guac_svc_process_receive(rdpSvcPlugin* plugin,
- wStream* input_stream);
-
-/**
- * Handler called when this plugin is being unloaded.
- */
-void guac_svc_process_terminate(rdpSvcPlugin* plugin);
-
-/**
- * Handler called when this plugin receives an event.
- */
-void guac_svc_process_event(rdpSvcPlugin* plugin, wMessage* event);
-
-#endif
-
diff --git a/src/protocols/rdp/input.c b/src/protocols/rdp/input.c
index 96507db..8f77dc0 100644
--- a/src/protocols/rdp/input.c
+++ b/src/protocols/rdp/input.c
@@ -17,20 +17,20 @@
* under the License.
*/
-#include "config.h"
-
-#include "client.h"
+#include "channels/disp.h"
+#include "common/cursor.h"
+#include "common/display.h"
#include "common/recording.h"
#include "input.h"
#include "keyboard.h"
#include "rdp.h"
-#include "rdp_disp.h"
+#include "settings.h"
#include <freerdp/freerdp.h>
#include <freerdp/input.h>
#include <guacamole/client.h>
+#include <guacamole/user.h>
-#include <pthread.h>
#include <stdlib.h>
int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
@@ -38,14 +38,10 @@
guac_client* client = user->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- pthread_mutex_lock(&(rdp_client->rdp_lock));
-
/* Skip if not yet connected */
freerdp* rdp_inst = rdp_client->rdp_inst;
- if (rdp_inst == NULL) {
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
+ if (rdp_inst == NULL)
return 0;
- }
/* Store current mouse location/state */
guac_common_cursor_update(rdp_client->display->cursor, user, x, y, mask);
@@ -118,8 +114,6 @@
rdp_client->mouse_button_mask = mask;
}
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
-
return 0;
}
@@ -155,9 +149,7 @@
height = height * settings->resolution / user->info.optimal_resolution;
/* Send display update */
- pthread_mutex_lock(&(rdp_client->rdp_lock));
guac_rdp_disp_set_size(rdp_client->disp, settings, rdp_inst, width, height);
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
return 0;
diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c
index 88cf2da..42cc4db 100644
--- a/src/protocols/rdp/keyboard.c
+++ b/src/protocols/rdp/keyboard.c
@@ -17,19 +17,15 @@
* under the License.
*/
-#include "config.h"
-
-#include "client.h"
#include "decompose.h"
#include "keyboard.h"
+#include "keymap.h"
#include "rdp.h"
-#include "rdp_keymap.h"
#include <freerdp/freerdp.h>
#include <freerdp/input.h>
#include <guacamole/client.h>
-#include <pthread.h>
#include <stdlib.h>
/**
@@ -102,21 +98,15 @@
else
pressed_flags = KBD_FLAGS_RELEASE;
- pthread_mutex_lock(&(rdp_client->rdp_lock));
-
/* Skip if not yet connected */
freerdp* rdp_inst = rdp_client->rdp_inst;
- if (rdp_inst == NULL) {
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
+ if (rdp_inst == NULL)
return;
- }
/* Send actual key */
rdp_inst->input->KeyboardEvent(rdp_inst->input,
flags | pressed_flags, scancode);
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
-
}
/**
@@ -136,22 +126,16 @@
static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client,
int codepoint) {
- pthread_mutex_lock(&(rdp_client->rdp_lock));
-
/* Skip if not yet connected */
freerdp* rdp_inst = rdp_client->rdp_inst;
- if (rdp_inst == NULL) {
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
+ if (rdp_inst == NULL)
return;
- }
/* Send Unicode event */
rdp_inst->input->UnicodeKeyboardEvent(
rdp_inst->input,
0, codepoint);
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
-
}
/**
@@ -171,20 +155,14 @@
static void guac_rdp_send_synchronize_event(guac_rdp_client* rdp_client,
int flags) {
- pthread_mutex_lock(&(rdp_client->rdp_lock));
-
/* Skip if not yet connected */
freerdp* rdp_inst = rdp_client->rdp_inst;
- if (rdp_inst == NULL) {
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
+ if (rdp_inst == NULL)
return;
- }
/* Synchronize lock key states */
rdp_inst->input->SynchronizeEvent(rdp_inst->input, flags);
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
-
}
/**
diff --git a/src/protocols/rdp/keyboard.h b/src/protocols/rdp/keyboard.h
index 79eed75..6296a71 100644
--- a/src/protocols/rdp/keyboard.h
+++ b/src/protocols/rdp/keyboard.h
@@ -20,7 +20,7 @@
#ifndef GUAC_RDP_KEYBOARD_H
#define GUAC_RDP_KEYBOARD_H
-#include "rdp_keymap.h"
+#include "keymap.h"
#include <guacamole/client.h>
@@ -94,7 +94,7 @@
/**
* The local state of all keys, as well as the necessary information to
- * translate received keysyms into scancodes or sequences of scancodes for
+ * translate received keysyms into scancodes or sequences of scancodes for
* RDP. The state of each key is updated based on received Guacamole key
* events, while the information describing the behavior and scancode
* mapping of each key is populated based on an associated keymap.
@@ -180,7 +180,7 @@
/**
* For every keysym in the given NULL-terminated array of keysyms, send the RDP
* key events required to update the remote state of those keys as specified,
- * depending on the current local state of those keysyms. For each key in the
+ * depending on the current local state of those keysyms. For each key in the
* "from" state, that key will be updated to the "to" state. The locally-stored
* state of each key is remains untouched.
*
diff --git a/src/protocols/rdp/rdp_keymap.c b/src/protocols/rdp/keymap.c
similarity index 97%
rename from src/protocols/rdp/rdp_keymap.c
rename to src/protocols/rdp/keymap.c
index 3be48a7..b71b402 100644
--- a/src/protocols/rdp/rdp_keymap.c
+++ b/src/protocols/rdp/keymap.c
@@ -17,9 +17,7 @@
* under the License.
*/
-#include "config.h"
-
-#include "rdp_keymap.h"
+#include "keymap.h"
#include <string.h>
diff --git a/src/protocols/rdp/rdp_keymap.h b/src/protocols/rdp/keymap.h
similarity index 96%
rename from src/protocols/rdp/rdp_keymap.h
rename to src/protocols/rdp/keymap.h
index f15ddea..bac1a7f 100644
--- a/src/protocols/rdp/rdp_keymap.h
+++ b/src/protocols/rdp/keymap.h
@@ -17,17 +17,10 @@
* under the License.
*/
+#ifndef GUAC_RDP_KEYMAP_H
+#define GUAC_RDP_KEYMAP_H
-#ifndef _GUAC_RDP_RDP_KEYMAP_H
-#define _GUAC_RDP_RDP_KEYMAP_H
-
-#include "config.h"
-
-#ifdef ENABLE_WINPR
#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-wtypes.h"
-#endif
/**
* Represents a keysym-to-scancode mapping for RDP, with extra information
diff --git a/src/protocols/rdp/keymaps/generate.pl b/src/protocols/rdp/keymaps/generate.pl
index 263b616..f1059a7 100755
--- a/src/protocols/rdp/keymaps/generate.pl
+++ b/src/protocols/rdp/keymaps/generate.pl
@@ -43,14 +43,9 @@
open OUTPUT, ">", "_generated_keymaps.c";
print OUTPUT
'#include "config.h"' . "\n"
- . '#include "rdp_keymap.h"' . "\n"
+ . '#include "keymap.h"' . "\n"
. '#include <freerdp/input.h>' . "\n"
- . "\n"
- . '#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H' . "\n"
. '#include <freerdp/locale/keyboard.h>' . "\n"
- . '#else' . "\n"
- . '#include <freerdp/kbd/layouts.h>' . "\n"
- . '#endif' . "\n"
. "\n"
. '#include <stddef.h>' . "\n"
. "\n";
diff --git a/src/protocols/rdp/log.c b/src/protocols/rdp/log.c
new file mode 100644
index 0000000..29414c1
--- /dev/null
+++ b/src/protocols/rdp/log.c
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <guacamole/client.h>
+#include <winpr/wlog.h>
+#include <winpr/wtypes.h>
+
+#include <stddef.h>
+
+/**
+ * The guac_client that should be used within this process for FreeRDP log
+ * messages. As all Guacamole connections are isolated at the process level,
+ * this will only ever be set to the guac_client of the current process'
+ * connection.
+ */
+static guac_client* current_client = NULL;
+
+/**
+ * Logs the text data within the given message to the logging facilities of the
+ * guac_client currently stored under current_client (the guac_client of the
+ * current process).
+ *
+ * @param message
+ * The message to log.
+ *
+ * @return
+ * TRUE if the message was successfully logged, FALSE otherwise.
+ */
+static BOOL guac_rdp_wlog_text_message(const wLogMessage* message) {
+
+ /* Fail if log not yet redirected */
+ if (current_client == NULL)
+ return FALSE;
+
+ /* Log all received messages at the debug level */
+ guac_client_log(current_client, GUAC_LOG_DEBUG, "%s", message->TextString);
+ return TRUE;
+
+}
+
+void guac_rdp_redirect_wlog(guac_client* client) {
+
+ wLogCallbacks callbacks = {
+ .message = guac_rdp_wlog_text_message
+ };
+
+ current_client = client;
+
+ /* Reconfigure root logger to use callback appender */
+ wLog* root = WLog_GetRoot();
+ WLog_SetLogAppenderType(root, WLOG_APPENDER_CALLBACK);
+
+ /* Set appender callbacks to our own */
+ wLogAppender* appender = WLog_GetLogAppender(root);
+ WLog_ConfigureAppender(appender, "callbacks", &callbacks);
+
+}
+
diff --git a/src/protocols/rdp/ptr_string.c b/src/protocols/rdp/log.h
similarity index 69%
copy from src/protocols/rdp/ptr_string.c
copy to src/protocols/rdp/log.h
index 5ec1b62..3b40aee 100644
--- a/src/protocols/rdp/ptr_string.c
+++ b/src/protocols/rdp/log.h
@@ -17,29 +17,19 @@
* under the License.
*/
-#include "config.h"
-#include "ptr_string.h"
+#ifndef GUAC_RDP_LOG_H
+#define GUAC_RDP_LOG_H
#include <guacamole/client.h>
-#include <stdio.h>
-#include <stdlib.h>
+/**
+ * Redirects the core FreeRDP logging facility, wLog, such that it logs all
+ * messages at the debug level using guac_client_log().
+ *
+ * @param client
+ * The guac_client that should receive all log messages.
+ */
+void guac_rdp_redirect_wlog(guac_client* client);
-void guac_rdp_ptr_to_string(void* data, char* str) {
-
- /* Convert pointer to string */
- sprintf(str, "%p", data);
-
-}
-
-void* guac_rdp_string_to_ptr(const char* str) {
-
- void* data;
-
- /* Convert string to pointer */
- sscanf(str, "%p", &data);
-
- return data;
-
-}
+#endif
diff --git a/src/protocols/rdp/ls.c b/src/protocols/rdp/ls.c
new file mode 100644
index 0000000..300bf93
--- /dev/null
+++ b/src/protocols/rdp/ls.c
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "fs.h"
+#include "ls.h"
+
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/stream.h>
+#include <guacamole/user.h>
+#include <winpr/nt.h>
+#include <winpr/shell.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+int guac_rdp_ls_ack_handler(guac_user* user, guac_stream* stream,
+ char* message, guac_protocol_status status) {
+
+ int blob_written = 0;
+ const char* filename;
+
+ guac_rdp_ls_status* ls_status = (guac_rdp_ls_status*) stream->data;
+
+ /* If unsuccessful, free stream and abort */
+ if (status != GUAC_PROTOCOL_STATUS_SUCCESS) {
+ guac_rdp_fs_close(ls_status->fs, ls_status->file_id);
+ guac_user_free_stream(user, stream);
+ free(ls_status);
+ return 0;
+ }
+
+ /* While directory entries remain */
+ while ((filename = guac_rdp_fs_read_dir(ls_status->fs,
+ ls_status->file_id)) != NULL
+ && !blob_written) {
+
+ char absolute_path[GUAC_RDP_FS_MAX_PATH];
+
+ /* Skip current and parent directory entries */
+ if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
+ continue;
+
+ /* Concatenate into absolute path - skip if invalid */
+ if (!guac_rdp_fs_append_filename(absolute_path,
+ ls_status->directory_name, filename)) {
+
+ guac_user_log(user, GUAC_LOG_DEBUG,
+ "Skipping filename \"%s\" - filename is invalid or "
+ "resulting path is too long", filename);
+
+ continue;
+ }
+
+ /* Attempt to open file to determine type */
+ int file_id = guac_rdp_fs_open(ls_status->fs, absolute_path,
+ GENERIC_READ, 0, FILE_OPEN, 0);
+ if (file_id < 0)
+ continue;
+
+ /* Get opened file */
+ guac_rdp_fs_file* file = guac_rdp_fs_get_file(ls_status->fs, file_id);
+ if (file == NULL) {
+ guac_user_log(user, GUAC_LOG_DEBUG, "%s: Successful open produced "
+ "bad file_id: %i", __func__, file_id);
+ return 0;
+ }
+
+ /* Determine mimetype */
+ const char* mimetype;
+ if (file->attributes & FILE_ATTRIBUTE_DIRECTORY)
+ mimetype = GUAC_USER_STREAM_INDEX_MIMETYPE;
+ else
+ mimetype = "application/octet-stream";
+
+ /* Write entry */
+ blob_written |= guac_common_json_write_property(user, stream,
+ &ls_status->json_state, absolute_path, mimetype);
+
+ guac_rdp_fs_close(ls_status->fs, file_id);
+
+ }
+
+ /* Complete JSON and cleanup at end of directory */
+ if (filename == NULL) {
+
+ /* Complete JSON object */
+ guac_common_json_end_object(user, stream, &ls_status->json_state);
+ guac_common_json_flush(user, stream, &ls_status->json_state);
+
+ /* Clean up resources */
+ guac_rdp_fs_close(ls_status->fs, ls_status->file_id);
+ free(ls_status);
+
+ /* Signal of stream */
+ guac_protocol_send_end(user->socket, stream);
+ guac_user_free_stream(user, stream);
+
+ }
+
+ guac_socket_flush(user->socket);
+ return 0;
+
+}
+
diff --git a/src/protocols/rdp/ls.h b/src/protocols/rdp/ls.h
new file mode 100644
index 0000000..be6a10c
--- /dev/null
+++ b/src/protocols/rdp/ls.h
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_LS_H
+#define GUAC_RDP_LS_H
+
+#include "common/json.h"
+#include "fs.h"
+
+#include <guacamole/protocol.h>
+#include <guacamole/stream.h>
+#include <guacamole/user.h>
+
+#include <stdint.h>
+
+/**
+ * The current state of a directory listing operation.
+ */
+typedef struct guac_rdp_ls_status {
+
+ /**
+ * The filesystem associated with the directory being listed.
+ */
+ guac_rdp_fs* fs;
+
+ /**
+ * The file ID of the directory being listed.
+ */
+ int file_id;
+
+ /**
+ * The absolute path of the directory being listed.
+ */
+ char directory_name[GUAC_RDP_FS_MAX_PATH];
+
+ /**
+ * The current state of the JSON directory object being written.
+ */
+ guac_common_json_state json_state;
+
+} guac_rdp_ls_status;
+
+/**
+ * Handler for ack messages received due to receipt of a "body" or "blob"
+ * instruction associated with a directory list operation.
+ */
+guac_user_ack_handler guac_rdp_ls_ack_handler;
+
+#endif
+
diff --git a/src/protocols/rdp/plugins/channels.c b/src/protocols/rdp/plugins/channels.c
new file mode 100644
index 0000000..0048b89
--- /dev/null
+++ b/src/protocols/rdp/plugins/channels.c
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "plugins/channels.h"
+#include "rdp.h"
+
+#include <freerdp/channels/channels.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/addin.h>
+#include <winpr/wtypes.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+int guac_rdp_wrapped_entry_ex_count = 0;
+
+int guac_rdp_wrapped_entry_count = 0;
+
+PVIRTUALCHANNELENTRYEX guac_rdp_wrapped_entry_ex[GUAC_RDP_MAX_CHANNELS] = { NULL };
+
+PVIRTUALCHANNELENTRY guac_rdp_wrapped_entry[GUAC_RDP_MAX_CHANNELS] = { NULL };
+
+PVIRTUALCHANNELENTRYEX guac_rdp_plugin_wrap_entry_ex(guac_client* client,
+ PVIRTUALCHANNELENTRYEX entry_ex) {
+
+ /* Do not wrap if there is insufficient space to store the wrapped
+ * function */
+ if (guac_rdp_wrapped_entry_ex_count == GUAC_RDP_MAX_CHANNELS) {
+ guac_client_log(client, GUAC_LOG_WARNING, "Maximum number of static "
+ "channels has been reached. Further FreeRDP plugins and "
+ "channel support may fail to load.");
+ return entry_ex;
+ }
+
+ /* Generate wrapped version of provided entry point */
+ PVIRTUALCHANNELENTRYEX wrapper = guac_rdp_entry_ex_wrappers[guac_rdp_wrapped_entry_ex_count];
+ guac_rdp_wrapped_entry_ex[guac_rdp_wrapped_entry_ex_count] = entry_ex;
+ guac_rdp_wrapped_entry_ex_count++;
+
+ return wrapper;
+
+}
+
+PVIRTUALCHANNELENTRY guac_rdp_plugin_wrap_entry(guac_client* client,
+ PVIRTUALCHANNELENTRY entry) {
+
+ /* Do not wrap if there is insufficient space to store the wrapped
+ * function */
+ if (guac_rdp_wrapped_entry_count == GUAC_RDP_MAX_CHANNELS) {
+ guac_client_log(client, GUAC_LOG_WARNING, "Maximum number of static "
+ "channels has been reached. Further FreeRDP plugins and "
+ "channel support may fail to load.");
+ return entry;
+ }
+
+ /* Generate wrapped version of provided entry point */
+ PVIRTUALCHANNELENTRY wrapper = guac_rdp_entry_wrappers[guac_rdp_wrapped_entry_count];
+ guac_rdp_wrapped_entry[guac_rdp_wrapped_entry_count] = entry;
+ guac_rdp_wrapped_entry_count++;
+
+ return wrapper;
+
+}
+
+int guac_freerdp_channels_load_plugin(rdpContext* context,
+ const char* name, void* data) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+
+ /* Load plugin using "ex" version of the channel plugin entry point, if it exists */
+ PVIRTUALCHANNELENTRYEX entry_ex = (PVIRTUALCHANNELENTRYEX) (void*) freerdp_load_channel_addin_entry(name,
+ NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC | FREERDP_ADDIN_CHANNEL_ENTRYEX);
+
+ if (entry_ex != NULL) {
+ entry_ex = guac_rdp_plugin_wrap_entry_ex(client, entry_ex);
+ return freerdp_channels_client_load_ex(context->channels, context->settings, entry_ex, data);
+ }
+
+ /* Lacking the "ex" entry point, attempt to load using the non-ex version */
+ PVIRTUALCHANNELENTRY entry = freerdp_load_channel_addin_entry(name,
+ NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC);
+
+ if (entry != NULL) {
+ entry = guac_rdp_plugin_wrap_entry(client, entry);
+ return freerdp_channels_client_load(context->channels, context->settings, entry, data);
+ }
+
+ /* The plugin does not exist / cannot be loaded */
+ return 1;
+
+}
+
+void guac_freerdp_dynamic_channel_collection_add(rdpSettings* settings,
+ const char* name, ...) {
+
+ va_list args;
+
+ ADDIN_ARGV* freerdp_args = malloc(sizeof(ADDIN_ARGV));
+
+ va_start(args, name);
+
+ /* Count number of arguments (excluding terminating NULL) */
+ freerdp_args->argc = 1;
+ while (va_arg(args, char*) != NULL)
+ freerdp_args->argc++;
+
+ /* Reset va_list */
+ va_end(args);
+ va_start(args, name);
+
+ /* Copy argument values into DVC entry */
+ freerdp_args->argv = malloc(sizeof(char*) * freerdp_args->argc);
+ freerdp_args->argv[0] = strdup(name);
+ int i;
+ for (i = 1; i < freerdp_args->argc; i++)
+ freerdp_args->argv[i] = strdup(va_arg(args, char*));
+
+ va_end(args);
+
+ /* Register plugin with FreeRDP */
+ settings->SupportDynamicChannels = TRUE;
+ freerdp_dynamic_channel_collection_add(settings, freerdp_args);
+
+}
+
diff --git a/src/protocols/rdp/plugins/channels.h b/src/protocols/rdp/plugins/channels.h
new file mode 100644
index 0000000..b1207e2
--- /dev/null
+++ b/src/protocols/rdp/plugins/channels.h
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_PLUGINS_CHANNELS_H
+#define GUAC_RDP_PLUGINS_CHANNELS_H
+
+#include <freerdp/channels/channels.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/settings.h>
+#include <guacamole/client.h>
+#include <winpr/wtsapi.h>
+
+/**
+ * The maximum number of static channels supported by Guacamole's RDP support.
+ * This value should be given a value which is at least the value of FreeRDP's
+ * CHANNEL_MAX_COUNT.
+ *
+ * NOTE: The value of this macro must be specified statically (not as a
+ * reference to CHANNEL_MAX_COUNT), as its value is extracted and used by the
+ * entry point wrapper code generator (generate-entry-wrappers.pl).
+ */
+#define GUAC_RDP_MAX_CHANNELS 64
+
+/* Validate GUAC_RDP_MAX_CHANNELS is sane at compile time */
+#if GUAC_RDP_MAX_CHANNELS < CHANNEL_MAX_COUNT
+#error "GUAC_RDP_MAX_CHANNELS must not be less than CHANNEL_MAX_COUNT"
+#endif
+
+/** Loads the FreeRDP plugin having the given name. With the exception that
+ * this function requires the rdpContext rather than rdpChannels and
+ * rdpSettings, this function is essentially a drop-in replacement for
+ * freerdp_channels_load_plugin() which additionally loads plugins implementing
+ * the PVIRTUALCHANNELENTRYEX version of the channel plugin entry point. The
+ * freerdp_channels_load_plugin() function which is part of FreeRDP can load
+ * only plugins which implement the PVIRTUALCHANNELENTRY version of the entry
+ * point.
+ *
+ * This MUST be called within the PreConnect callback of the freerdp instance
+ * for the referenced plugin to be loaded correctly.
+ *
+ * @param context
+ * The rdpContext associated with the active RDP session.
+ *
+ * @param name
+ * The name of the plugin to load. If the plugin is not statically built
+ * into FreeRDP, this name will determine the filename of the library to be
+ * loaded dynamically. For a plugin named "NAME", the library called
+ * "libNAME-client" will be loaded from the "freerdp2" subdirectory of the
+ * main directory containing the FreeRDP libraries.
+ *
+ * @param data
+ * Arbitrary data to be passed to the plugin entry point. For most plugins
+ * which are built into FreeRDP, this will be another reference to the
+ * rdpSettings struct. The source of the relevant plugin must be consulted
+ * to determine the proper value to pass here.
+ *
+ * @return
+ * Zero if the plugin was loaded successfully, non-zero if the plugin could
+ * not be loaded.
+ */
+int guac_freerdp_channels_load_plugin(rdpContext* context,
+ const char* name, void* data);
+
+/**
+ * Schedules loading of the FreeRDP dynamic virtual channel plugin having the
+ * given name. This function is essentially a wrapper for
+ * freerdp_dynamic_channel_collection_add() which additionally takes care of
+ * housekeeping tasks which would otherwise need to be performed manually:
+ *
+ * - The ADDIN_ARGV structure used to pass arguments to dynamic virtual
+ * channel plugins is automatically allocated and populated with any given
+ * arguments.
+ * - The SupportDynamicChannels member of the rdpSettings structure is
+ * automatically set to TRUE.
+ *
+ * The "drdynvc" plugin must still eventually be loaded for this function to
+ * have any effect, as it is the "drdynvc" plugin which processes the
+ * collection this function manipulates.
+ *
+ * This MUST be called within the PreConnect callback of the freerdp instance
+ * and the "drdynvc" plugin MUST be loaded at some point after this function is
+ * called for the referenced dynamic channel plugin to be loaded correctly.
+ *
+ * @param settings
+ * The rdpSettings structure associated with the FreeRDP instance, already
+ * populated with any settings applicable to the plugin being loaded.
+ *
+ * @param name
+ * The name of the plugin to load. If the plugin is not statically built
+ * into FreeRDP, this name will determine the filename of the library to be
+ * loaded dynamically. For a plugin named "NAME", the library called
+ * "libNAME-client" will be loaded from the "freerdp2" subdirectory of the
+ * main directory containing the FreeRDP libraries.
+ *
+ * @param ...
+ * Arbitrary arguments to be passed to the plugin entry point. For most
+ * plugins which are built into FreeRDP, this will be another reference to
+ * the rdpSettings struct or NULL. The source of the relevant plugin must
+ * be consulted to determine the proper value(s) to pass here.
+ */
+void guac_freerdp_dynamic_channel_collection_add(rdpSettings* settings,
+ const char* name, ...);
+
+/**
+ * The number of wrapped channel entry points currently stored within
+ * guac_rdp_wrapped_entry_ex.
+ */
+extern int guac_rdp_wrapped_entry_ex_count;
+
+/**
+ * All currently wrapped entry points that use the PVIRTUALCHANNELENTRYEX
+ * variant.
+ */
+extern PVIRTUALCHANNELENTRYEX guac_rdp_wrapped_entry_ex[GUAC_RDP_MAX_CHANNELS];
+
+/**
+ * Lookup table of wrapper functions for PVIRTUALCHANNELENTRYEX entry points.
+ * Each function within this array is generated at compile time by the entry
+ * point wrapper code generator (generate-entry-wrappers.pl) and automatically
+ * invokes the corresponding wrapped entry point stored within
+ * guac_rdp_wrapped_entry_ex.
+ */
+extern PVIRTUALCHANNELENTRYEX guac_rdp_entry_ex_wrappers[GUAC_RDP_MAX_CHANNELS];
+
+/**
+ * Wraps the provided entry point function, returning a different entry point
+ * which simply invokes the original. As long as this function is not invoked
+ * more than GUAC_RDP_MAX_CHANNELS times, each returned entry point will be
+ * unique, even if the provided entry point is not. As FreeRDP will refuse to
+ * load a plugin if its entry point is already loaded, this allows a single
+ * FreeRDP plugin to be loaded multiple times.
+ *
+ * @param client
+ * The guac_client associated with the relevant RDP session.
+ *
+ * @param entry_ex
+ * The entry point function to wrap.
+ *
+ * @return
+ * A wrapped version of the provided entry point, or the unwrapped entry
+ * point if there is insufficient space remaining within
+ * guac_rdp_entry_ex_wrappers to wrap the entry point.
+ */
+PVIRTUALCHANNELENTRYEX guac_rdp_plugin_wrap_entry_ex(guac_client* client,
+ PVIRTUALCHANNELENTRYEX entry_ex);
+
+/**
+ * The number of wrapped channel entry points currently stored within
+ * guac_rdp_wrapped_entry.
+ */
+extern int guac_rdp_wrapped_entry_count;
+
+/**
+ * All currently wrapped entry points that use the PVIRTUALCHANNELENTRY
+ * variant.
+ */
+extern PVIRTUALCHANNELENTRY guac_rdp_wrapped_entry[GUAC_RDP_MAX_CHANNELS];
+
+/**
+ * Lookup table of wrapper functions for PVIRTUALCHANNELENTRY entry points.
+ * Each function within this array is generated at compile time by the entry
+ * point wrapper code generator (generate-entry-wrappers.pl) and automatically
+ * invokes the corresponding wrapped entry point stored within
+ * guac_rdp_wrapped_entry.
+ */
+extern PVIRTUALCHANNELENTRY guac_rdp_entry_wrappers[GUAC_RDP_MAX_CHANNELS];
+
+/**
+ * Wraps the provided entry point function, returning a different entry point
+ * which simply invokes the original. As long as this function is not invoked
+ * more than GUAC_RDP_MAX_CHANNELS times, each returned entry point will be
+ * unique, even if the provided entry point is not. As FreeRDP will refuse to
+ * load a plugin if its entry point is already loaded, this allows a single
+ * FreeRDP plugin to be loaded multiple times.
+ *
+ * @param client
+ * The guac_client associated with the relevant RDP session.
+ *
+ * @param entry
+ * The entry point function to wrap.
+ *
+ * @return
+ * A wrapped version of the provided entry point, or the unwrapped entry
+ * point if there is insufficient space remaining within
+ * guac_rdp_entry_wrappers to wrap the entry point.
+ */
+PVIRTUALCHANNELENTRY guac_rdp_plugin_wrap_entry(guac_client* client,
+ PVIRTUALCHANNELENTRY entry);
+
+#endif
+
diff --git a/src/protocols/rdp/plugins/generate-entry-wrappers.pl b/src/protocols/rdp/plugins/generate-entry-wrappers.pl
new file mode 100755
index 0000000..f3fc003
--- /dev/null
+++ b/src/protocols/rdp/plugins/generate-entry-wrappers.pl
@@ -0,0 +1,77 @@
+#!/usr/bin/env perl
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#
+# generate-entry-wrappers.pl
+#
+# Generates C source which defines wrapper functions for FreeRDP plugin entry
+# points, allowing multiple instances of the same plugin to be loaded despite
+# otherwise always having the same entry point.
+#
+# The resulting source is stored within "_generated_channel_entry_wrappers.c".
+#
+
+use strict;
+
+##
+## The maximum number of static channels supported by Guacamole's RDP support.
+##
+my $GUAC_RDP_MAX_CHANNELS;
+
+# Extract value of GUAC_RDP_MAX_CHANNELS macro from provided source
+while (<>) {
+ if ((my $value) = m/^\s*#define\s+GUAC_RDP_MAX_CHANNELS\s+(\d+)\s*$/) {
+ $GUAC_RDP_MAX_CHANNELS = $value;
+ }
+}
+
+open OUTPUT, ">", "_generated_channel_entry_wrappers.c";
+
+# Generate required headers
+print OUTPUT <<"EOF";
+#include "plugins/channels.h"
+#include <freerdp/channels/channels.h>
+#include <freerdp/freerdp.h>
+EOF
+
+# Generate wrapper definitions for PVIRTUALCHANNELENTRYEX entry point variant
+print OUTPUT <<"EOF" for (1..$GUAC_RDP_MAX_CHANNELS);
+static BOOL guac_rdp_plugin_entry_ex_wrapper$_(PCHANNEL_ENTRY_POINTS_EX entry_points_ex, PVOID init_handle) {
+ return guac_rdp_wrapped_entry_ex[$_ - 1](entry_points_ex, init_handle);
+}
+EOF
+
+# Generate wrapper definitions for PVIRTUALCHANNELENTRY entry point variant
+print OUTPUT <<"EOF" for (1..$GUAC_RDP_MAX_CHANNELS);
+static BOOL guac_rdp_plugin_entry_wrapper$_(PCHANNEL_ENTRY_POINTS entry_points) {
+ return guac_rdp_wrapped_entry[$_ - 1](entry_points);
+}
+EOF
+
+# Populate lookup table of PVIRTUALCHANNELENTRYEX wrapper functions
+print OUTPUT "PVIRTUALCHANNELENTRYEX guac_rdp_entry_ex_wrappers[$GUAC_RDP_MAX_CHANNELS] = {\n";
+print OUTPUT " guac_rdp_plugin_entry_ex_wrapper$_,\n" for (1..$GUAC_RDP_MAX_CHANNELS);
+print OUTPUT "};\n";
+
+# Populate lookup table of PVIRTUALCHANNELENTRY wrapper functions
+print OUTPUT "PVIRTUALCHANNELENTRY guac_rdp_entry_wrappers[$GUAC_RDP_MAX_CHANNELS] = {\n";
+print OUTPUT " guac_rdp_plugin_entry_wrapper$_,\n" for (1..$GUAC_RDP_MAX_CHANNELS);
+print OUTPUT "};\n";
+
diff --git a/src/protocols/rdp/plugins/guac-common-svc/guac-common-svc.c b/src/protocols/rdp/plugins/guac-common-svc/guac-common-svc.c
new file mode 100644
index 0000000..fbba484
--- /dev/null
+++ b/src/protocols/rdp/plugins/guac-common-svc/guac-common-svc.c
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "channels/common-svc.h"
+
+#include <freerdp/svc.h>
+#include <guacamole/client.h>
+#include <winpr/stream.h>
+#include <winpr/wtsapi.h>
+#include <winpr/wtypes.h>
+
+#include <stdlib.h>
+
+/**
+ * Event handler for events which deal with data transmitted over an open SVC.
+ * This specific implementation of the event handler currently handles only the
+ * CHANNEL_EVENT_DATA_RECEIVED event, delegating actual handling of that event
+ * to guac_rdp_common_svc_process_receive().
+ *
+ * The FreeRDP requirements for this function follow those of the
+ * VirtualChannelOpenEventEx callback defined within Microsoft's RDP API:
+ *
+ * https://docs.microsoft.com/en-us/previous-versions/windows/embedded/aa514754%28v%3dmsdn.10%29
+ *
+ * @param user_param
+ * The pointer to arbitrary data originally passed via the first parameter
+ * of the pVirtualChannelInitEx() function call when the associated channel
+ * was initialized. The pVirtualChannelInitEx() function is exposed within
+ * the channel entry points structure.
+ *
+ * @param open_handle
+ * The handle which identifies the channel itself, typically referred to
+ * within the FreeRDP source as OpenHandle.
+ *
+ * @param event
+ * An integer representing the event that should be handled. This will be
+ * either CHANNEL_EVENT_DATA_RECEIVED, CHANNEL_EVENT_WRITE_CANCELLED, or
+ * CHANNEL_EVENT_WRITE_COMPLETE.
+ *
+ * @param data
+ * The data received, for CHANNEL_EVENT_DATA_RECEIVED events, and the value
+ * passed as user data to pVirtualChannelWriteEx() for
+ * CHANNEL_EVENT_WRITE_* events (note that user data for
+ * pVirtualChannelWriteEx() as implemented by FreeRDP MUST either be NULL
+ * or a wStream containing the data written).
+ *
+ * @param data_length
+ * The number of bytes of event-specific data.
+ *
+ * @param total_length
+ * The total number of bytes expected to be received from the RDP server
+ * due to this single write (from the server's perspective). Each write may
+ * actually be split into multiple chunks, thus resulting in multiple
+ * receive events for the same logical block of data. The relationship
+ * between chunks is indicated with the CHANNEL_FLAG_FIRST and
+ * CHANNEL_FLAG_LAST flags.
+ *
+ * @param data_flags
+ * The result of a bitwise OR of the CHANNEL_FLAG_* flags which apply to
+ * the data received. This value is relevant only to
+ * CHANNEL_EVENT_DATA_RECEIVED events. Valid flags are CHANNEL_FLAG_FIRST,
+ * CHANNEL_FLAG_LAST, and CHANNEL_FLAG_ONLY. The flag CHANNEL_FLAG_MIDDLE
+ * is not itself a flag, but the absence of both CHANNEL_FLAG_FIRST and
+ * CHANNEL_FLAG_LAST.
+ */
+static VOID guac_rdp_common_svc_handle_open_event(LPVOID user_param,
+ DWORD open_handle, UINT event, LPVOID data, UINT32 data_length,
+ UINT32 total_length, UINT32 data_flags) {
+
+ /* Ignore all events except for received data */
+ if (event != CHANNEL_EVENT_DATA_RECEIVED)
+ return;
+
+ guac_rdp_common_svc* svc = (guac_rdp_common_svc*) user_param;
+
+ /* Validate relevant handle matches that of SVC */
+ if (open_handle != svc->_open_handle) {
+ guac_client_log(svc->client, GUAC_LOG_WARNING, "%i bytes of data "
+ "received from within the remote desktop session for SVC "
+ "\"%s\" are being dropped because the relevant open handle "
+ "(0x%X) does not match the open handle of the SVC (0x%X).",
+ data_length, svc->name, open_handle, svc->_open_handle);
+ return;
+ }
+
+ /* If receiving first chunk, allocate sufficient space for all remaining
+ * chunks */
+ if (data_flags & CHANNEL_FLAG_FIRST) {
+
+ /* Limit maximum received size */
+ if (total_length > GUAC_SVC_MAX_ASSEMBLED_LENGTH) {
+ guac_client_log(svc->client, GUAC_LOG_WARNING, "RDP server has "
+ "requested to send a sequence of %i bytes, but this "
+ "exceeds the maximum buffer space of %i bytes. Received "
+ "data may be truncated.", total_length,
+ GUAC_SVC_MAX_ASSEMBLED_LENGTH);
+ total_length = GUAC_SVC_MAX_ASSEMBLED_LENGTH;
+ }
+
+ svc->_input_stream = Stream_New(NULL, total_length);
+ }
+
+ /* Add chunk to buffer only if sufficient space remains */
+ if (Stream_EnsureRemainingCapacity(svc->_input_stream, data_length))
+ Stream_Write(svc->_input_stream, data, data_length);
+ else
+ guac_client_log(svc->client, GUAC_LOG_WARNING, "%i bytes of data "
+ "received from within the remote desktop session for SVC "
+ "\"%s\" are being dropped because the maximum available "
+ "space for received data has been exceeded.", data_length,
+ svc->name);
+
+ /* Fire event once last chunk has been received */
+ if (data_flags & CHANNEL_FLAG_LAST) {
+
+ Stream_SealLength(svc->_input_stream);
+ Stream_SetPosition(svc->_input_stream, 0);
+
+ /* Handle channel-specific data receipt tasks, if any */
+ if (svc->_receive_handler)
+ svc->_receive_handler(svc, svc->_input_stream);
+
+ Stream_Free(svc->_input_stream, TRUE);
+
+ }
+
+}
+
+/**
+ * Processes a CHANNEL_EVENT_CONNECTED event, completing the
+ * connection/initialization process of the channel.
+ *
+ * @param rdpsnd
+ * The guac_rdp_common_svc structure representing the channel.
+ */
+static void guac_rdp_common_svc_process_connect(guac_rdp_common_svc* svc) {
+
+ /* Open FreeRDP side of connected channel */
+ UINT32 open_status =
+ svc->_entry_points.pVirtualChannelOpenEx(svc->_init_handle,
+ &svc->_open_handle, svc->_channel_def.name,
+ guac_rdp_common_svc_handle_open_event);
+
+ /* Warn if the channel cannot be opened after all */
+ if (open_status != CHANNEL_RC_OK) {
+ guac_client_log(svc->client, GUAC_LOG_WARNING, "SVC \"%s\" could not "
+ "be opened: %s (error %i)", svc->name,
+ WTSErrorToString(open_status), open_status);
+ return;
+ }
+
+ /* Handle channel-specific connect tasks, if any */
+ if (svc->_connect_handler)
+ svc->_connect_handler(svc);
+
+ /* Channel is now ready */
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "SVC \"%s\" connected.",
+ svc->name);
+
+}
+
+/**
+ * Processes a CHANNEL_EVENT_TERMINATED event, freeing all resources associated
+ * with the channel.
+ *
+ * @param svc
+ * The guac_rdp_common_svc structure representing the channel.
+ */
+static void guac_rdp_common_svc_process_terminate(guac_rdp_common_svc* svc) {
+
+ /* Handle channel-specific termination tasks, if any */
+ if (svc->_terminate_handler)
+ svc->_terminate_handler(svc);
+
+ guac_client_log(svc->client, GUAC_LOG_DEBUG, "SVC \"%s\" disconnected.",
+ svc->name);
+ free(svc);
+
+}
+
+/**
+ * Event handler for events which deal with the overall lifecycle of an SVC.
+ * This specific implementation of the event handler currently handles only
+ * CHANNEL_EVENT_CONNECTED and CHANNEL_EVENT_TERMINATED events, delegating
+ * actual handling of those events to guac_rdp_common_svc_process_connect() and
+ * guac_rdp_common_svc_process_terminate() respectively.
+ *
+ * The FreeRDP requirements for this function follow those of the
+ * VirtualChannelInitEventEx callback defined within Microsoft's RDP API:
+ *
+ * https://docs.microsoft.com/en-us/previous-versions/windows/embedded/aa514727%28v%3dmsdn.10%29
+ *
+ * @param user_param
+ * The pointer to arbitrary data originally passed via the first parameter
+ * of the pVirtualChannelInitEx() function call when the associated channel
+ * was initialized. The pVirtualChannelInitEx() function is exposed within
+ * the channel entry points structure.
+ *
+ * @param init_handle
+ * The handle which identifies the client connection, typically referred to
+ * within the FreeRDP source as pInitHandle.
+ *
+ * @param event
+ * An integer representing the event that should be handled. This will be
+ * either CHANNEL_EVENT_CONNECTED, CHANNEL_EVENT_DISCONNECTED,
+ * CHANNEL_EVENT_INITIALIZED, CHANNEL_EVENT_TERMINATED, or
+ * CHANNEL_EVENT_V1_CONNECTED.
+ *
+ * @param data
+ * NULL in all cases except the CHANNEL_EVENT_CONNECTED event, in which
+ * case this is a null-terminated string containing the name of the server.
+ *
+ * @param data_length
+ * The number of bytes of data, if any.
+ */
+static VOID guac_rdp_common_svc_handle_init_event(LPVOID user_param,
+ LPVOID init_handle, UINT event, LPVOID data, UINT data_length) {
+
+ guac_rdp_common_svc* svc = (guac_rdp_common_svc*) user_param;
+
+ /* Validate relevant handle matches that of SVC */
+ if (init_handle != svc->_init_handle) {
+ guac_client_log(svc->client, GUAC_LOG_WARNING, "An init event (#%i) "
+ "for SVC \"%s\" has been dropped because the relevant init "
+ "handle (0x%X) does not match the init handle of the SVC "
+ "(0x%X).", event, svc->name, init_handle, svc->_init_handle);
+ return;
+ }
+
+ switch (event) {
+
+ /* The remote desktop side of the SVC has been connected */
+ case CHANNEL_EVENT_CONNECTED:
+ guac_rdp_common_svc_process_connect(svc);
+ break;
+
+ /* The channel has disconnected and now must be cleaned up */
+ case CHANNEL_EVENT_TERMINATED:
+ guac_rdp_common_svc_process_terminate(svc);
+ break;
+
+ }
+
+}
+
+/**
+ * Entry point for FreeRDP plugins. This function is automatically invoked when
+ * the plugin is loaded.
+ *
+ * @param entry_points
+ * Functions and data specific to the FreeRDP side of the virtual channel
+ * and plugin. This structure must be copied within implementation-specific
+ * storage such that the functions it references can be invoked when
+ * needed.
+ *
+ * @param init_handle
+ * The handle which identifies the client connection, typically referred to
+ * within the FreeRDP source as pInitHandle. This handle is also provided
+ * to the channel init event handler. The handle must eventually be used
+ * within the channel open event handler to obtain a handle to the channel
+ * itself.
+ *
+ * @return
+ * TRUE if the plugin has initialized successfully, FALSE otherwise.
+ */
+BOOL VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX entry_points,
+ PVOID init_handle) {
+
+ CHANNEL_ENTRY_POINTS_FREERDP_EX* entry_points_ex =
+ (CHANNEL_ENTRY_POINTS_FREERDP_EX*) entry_points;
+
+ /* Get structure representing the Guacamole side of the SVC from plugin
+ * parameters */
+ guac_rdp_common_svc* svc = (guac_rdp_common_svc*) entry_points_ex->pExtendedData;
+
+ /* Copy FreeRDP data into SVC structure for future reference */
+ svc->_entry_points = *entry_points_ex;
+ svc->_init_handle = init_handle;
+
+ /* Complete initialization */
+ if (svc->_entry_points.pVirtualChannelInitEx(svc, svc, init_handle,
+ &svc->_channel_def, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
+ guac_rdp_common_svc_handle_init_event) != CHANNEL_RC_OK) {
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
diff --git a/src/protocols/rdp/guac_ai/ai_messages.c b/src/protocols/rdp/plugins/guacai/guacai-messages.c
similarity index 95%
rename from src/protocols/rdp/guac_ai/ai_messages.c
rename to src/protocols/rdp/plugins/guacai/guacai-messages.c
index c346587..38f7a7c 100644
--- a/src/protocols/rdp/guac_ai/ai_messages.c
+++ b/src/protocols/rdp/plugins/guacai/guacai-messages.c
@@ -17,24 +17,15 @@
* under the License.
*/
-#include "config.h"
-
-#include "ai_messages.h"
-#include "audio_input.h"
+#include "channels/audio-input/audio-buffer.h"
+#include "plugins/guacai/guacai-messages.h"
#include "rdp.h"
-#include <stdlib.h>
-
-#include <freerdp/freerdp.h>
-#include <freerdp/constants.h>
#include <freerdp/dvc.h>
#include <guacamole/client.h>
-
-#ifdef ENABLE_WINPR
#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
+
+#include <stdlib.h>
/**
* Reads AUDIO_FORMAT data from the given stream into the given struct.
@@ -113,6 +104,19 @@
}
+/**
+ * Sends a Data PDU along the given channel. A Data PDU is used by the client
+ * to send actual audio data following a Data Incoming PDU.
+ *
+ * @param channel
+ * The channel along which the PDU should be sent.
+ *
+ * @param buffer
+ * The audio data to send.
+ *
+ * @param length
+ * The number of bytes of audio data to send.
+ */
static void guac_rdp_ai_send_data(IWTSVirtualChannel* channel,
char* buffer, int length) {
@@ -286,7 +290,7 @@
}
-static void guac_rdp_ai_flush_packet(char* buffer, int length, void* data) {
+void guac_rdp_ai_flush_packet(char* buffer, int length, void* data) {
IWTSVirtualChannel* channel = (IWTSVirtualChannel*) data;
diff --git a/src/protocols/rdp/guac_ai/ai_messages.h b/src/protocols/rdp/plugins/guacai/guacai-messages.h
similarity index 91%
rename from src/protocols/rdp/guac_ai/ai_messages.h
rename to src/protocols/rdp/plugins/guacai/guacai-messages.h
index 55cf6e9..6a2333f 100644
--- a/src/protocols/rdp/guac_ai/ai_messages.h
+++ b/src/protocols/rdp/plugins/guacai/guacai-messages.h
@@ -17,19 +17,15 @@
* under the License.
*/
-#ifndef GUAC_RDP_AI_MESSAGES_H
-#define GUAC_RDP_AI_MESSAGES_H
+#ifndef GUAC_RDP_PLUGINS_GUACAI_MESSAGES_H
+#define GUAC_RDP_PLUGINS_GUACAI_MESSAGES_H
-#include "config.h"
+#include "channels/audio-input/audio-buffer.h"
#include <freerdp/dvc.h>
#include <guacamole/client.h>
-
-#ifdef ENABLE_WINPR
#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
+#include <winpr/wtypes.h>
/**
* The format tag associated with raw wave audio (WAVE_FORMAT_PCM). This format
@@ -212,5 +208,14 @@
void guac_rdp_ai_process_formatchange(guac_client* client,
IWTSVirtualChannel* channel, wStream* stream);
+/**
+ * Audio buffer flush handler which sends audio data along the active audio
+ * input channel using a Data Incoming PDU and Data PDU. The arbitrary data
+ * provided to the handler by the audio buffer implementation is in this case
+ * the IWTSVirtualChannel structure representing the active audio input
+ * channel.
+ */
+guac_rdp_audio_buffer_flush_handler guac_rdp_ai_flush_packet;
+
#endif
diff --git a/src/protocols/rdp/guac_ai/ai_service.c b/src/protocols/rdp/plugins/guacai/guacai.c
similarity index 81%
rename from src/protocols/rdp/guac_ai/ai_service.c
rename to src/protocols/rdp/plugins/guacai/guacai.c
index 5058ea0..15de865 100644
--- a/src/protocols/rdp/guac_ai/ai_service.c
+++ b/src/protocols/rdp/plugins/guacai/guacai.c
@@ -17,27 +17,20 @@
* under the License.
*/
-#include "config.h"
-
-#include "ai_messages.h"
-#include "ai_service.h"
-#include "audio_input.h"
-#include "ptr_string.h"
+#include "channels/audio-input/audio-buffer.h"
+#include "plugins/guacai/guacai.h"
+#include "plugins/guacai/guacai-messages.h"
+#include "plugins/ptr-string.h"
#include "rdp.h"
-#include <stdlib.h>
-#include <string.h>
-
-#include <freerdp/freerdp.h>
-#include <freerdp/constants.h>
#include <freerdp/dvc.h>
+#include <freerdp/settings.h>
#include <guacamole/client.h>
-
-#ifdef ENABLE_WINPR
#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
+#include <winpr/wtsapi.h>
+#include <winpr/wtypes.h>
+
+#include <stdlib.h>
/**
* Handles the given data received along the AUDIO_INPUT channel of the RDP
@@ -95,43 +88,9 @@
}
-#ifdef LEGACY_IWTSVIRTUALCHANNELCALLBACK
/**
* Callback which is invoked when data is received along a connection to the
- * AUDIO_INPUT plugin. This callback is specific to FreeRDP 1.1 and older.
- *
- * @param channel_callback
- * The IWTSVirtualChannelCallback structure to which this callback was
- * originally assigned.
- *
- * @param size
- * The number of bytes received.
- *
- * @param buffer
- * A buffer containing all bytes received.
- *
- * @return
- * Always zero.
- */
-static int guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
- UINT32 size, BYTE* buffer) {
-
- guac_rdp_ai_channel_callback* ai_channel_callback =
- (guac_rdp_ai_channel_callback*) channel_callback;
- IWTSVirtualChannel* channel = ai_channel_callback->channel;
-
- /* Invoke generalized (API-independent) data handler */
- wStream* stream = Stream_New(buffer, size);
- guac_rdp_ai_handle_data(ai_channel_callback->client, channel, stream);
- Stream_Free(stream, FALSE);
-
- return 0;
-
-}
-#else
-/**
- * Callback which is invoked when data is received along a connection to the
- * AUDIO_INPUT plugin. This callback is specific to FreeRDP 1.2 and newer.
+ * AUDIO_INPUT plugin.
*
* @param channel_callback
* The IWTSVirtualChannelCallback structure to which this callback was
@@ -143,7 +102,7 @@
* @return
* Always zero.
*/
-static int guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
+static UINT guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
wStream* stream) {
guac_rdp_ai_channel_callback* ai_channel_callback =
@@ -153,10 +112,9 @@
/* Invoke generalized (API-independent) data handler */
guac_rdp_ai_handle_data(ai_channel_callback->client, channel, stream);
- return 0;
+ return CHANNEL_RC_OK;
}
-#endif
/**
* Callback which is invoked when a connection to the AUDIO_INPUT plugin is
@@ -169,7 +127,7 @@
* @return
* Always zero.
*/
-static int guac_rdp_ai_close(IWTSVirtualChannelCallback* channel_callback) {
+static UINT guac_rdp_ai_close(IWTSVirtualChannelCallback* channel_callback) {
guac_rdp_ai_channel_callback* ai_channel_callback =
(guac_rdp_ai_channel_callback*) channel_callback;
@@ -184,7 +142,7 @@
guac_rdp_audio_buffer_end(audio_buffer);
free(ai_channel_callback);
- return 0;
+ return CHANNEL_RC_OK;
}
@@ -219,7 +177,7 @@
* @return
* Always zero.
*/
-static int guac_rdp_ai_new_connection(
+static UINT guac_rdp_ai_new_connection(
IWTSListenerCallback* listener_callback, IWTSVirtualChannel* channel,
BYTE* data, int* accept,
IWTSVirtualChannelCallback** channel_callback) {
@@ -243,7 +201,8 @@
/* Return callback through pointer */
*channel_callback = (IWTSVirtualChannelCallback*) ai_channel_callback;
- return 0;
+
+ return CHANNEL_RC_OK;
}
@@ -261,7 +220,7 @@
* @return
* Always zero.
*/
-static int guac_rdp_ai_initialize(IWTSPlugin* plugin,
+static UINT guac_rdp_ai_initialize(IWTSPlugin* plugin,
IWTSVirtualChannelManager* manager) {
/* Allocate new listener callback */
@@ -281,7 +240,7 @@
manager->CreateListener(manager, "AUDIO_INPUT", 0,
(IWTSListenerCallback*) ai_listener_callback, NULL);
- return 0;
+ return CHANNEL_RC_OK;
}
@@ -295,7 +254,7 @@
* @return
* Always zero.
*/
-static int guac_rdp_ai_terminated(IWTSPlugin* plugin) {
+static UINT guac_rdp_ai_terminated(IWTSPlugin* plugin) {
guac_rdp_ai_plugin* ai_plugin = (guac_rdp_ai_plugin*) plugin;
guac_client* client = ai_plugin->client;
@@ -305,7 +264,7 @@
free(ai_plugin);
guac_client_log(client, GUAC_LOG_DEBUG, "AUDIO_INPUT plugin unloaded.");
- return 0;
+ return CHANNEL_RC_OK;
}
@@ -315,13 +274,8 @@
int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) {
/* Pull guac_client from arguments */
-#ifdef HAVE_ADDIN_ARGV
ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);
guac_client* client = (guac_client*) guac_rdp_string_to_ptr(args->argv[1]);
-#else
- RDP_PLUGIN_DATA* data = pEntryPoints->GetPluginData(pEntryPoints);
- guac_client* client = (guac_client*) guac_rdp_string_to_ptr(data->data[1]);
-#endif
/* Pull previously-allocated plugin */
guac_rdp_ai_plugin* ai_plugin = (guac_rdp_ai_plugin*)
@@ -343,7 +297,7 @@
guac_client_log(client, GUAC_LOG_DEBUG, "AUDIO_INPUT plugin loaded.");
}
- return 1;
+ return CHANNEL_RC_OK;
}
diff --git a/src/protocols/rdp/guac_ai/ai_service.h b/src/protocols/rdp/plugins/guacai/guacai.h
similarity index 96%
rename from src/protocols/rdp/guac_ai/ai_service.h
rename to src/protocols/rdp/plugins/guacai/guacai.h
index 28f2227..d26563c 100644
--- a/src/protocols/rdp/guac_ai/ai_service.h
+++ b/src/protocols/rdp/plugins/guacai/guacai.h
@@ -17,14 +17,12 @@
* under the License.
*/
-#ifndef GUAC_RDP_AI_SERVICE_H
-#define GUAC_RDP_AI_SERVICE_H
+#ifndef GUAC_RDP_PLUGINS_GUACAI_H
+#define GUAC_RDP_PLUGINS_GUACAI_H
-#include "config.h"
-
-#include <freerdp/freerdp.h>
#include <freerdp/constants.h>
#include <freerdp/dvc.h>
+#include <freerdp/freerdp.h>
#include <guacamole/client.h>
/**
diff --git a/src/protocols/rdp/ptr_string.c b/src/protocols/rdp/plugins/ptr-string.c
similarity index 91%
rename from src/protocols/rdp/ptr_string.c
rename to src/protocols/rdp/plugins/ptr-string.c
index 5ec1b62..324a519 100644
--- a/src/protocols/rdp/ptr_string.c
+++ b/src/protocols/rdp/plugins/ptr-string.c
@@ -17,13 +17,9 @@
* under the License.
*/
-#include "config.h"
-#include "ptr_string.h"
-
-#include <guacamole/client.h>
+#include "plugins/ptr-string.h"
#include <stdio.h>
-#include <stdlib.h>
void guac_rdp_ptr_to_string(void* data, char* str) {
diff --git a/src/protocols/rdp/ptr_string.h b/src/protocols/rdp/plugins/ptr-string.h
similarity index 95%
rename from src/protocols/rdp/ptr_string.h
rename to src/protocols/rdp/plugins/ptr-string.h
index 9a9156b..ef682b1 100644
--- a/src/protocols/rdp/ptr_string.h
+++ b/src/protocols/rdp/plugins/ptr-string.h
@@ -17,10 +17,8 @@
* under the License.
*/
-#ifndef GUAC_RDP_PTR_STRING_H
-#define GUAC_RDP_PTR_STRING_H
-
-#include "config.h"
+#ifndef GUAC_RDP_PLUGINS_PTR_STRING_H
+#define GUAC_RDP_PLUGINS_PTR_STRING_H
#include <guacamole/client.h>
diff --git a/src/protocols/rdp/rdp_pointer.c b/src/protocols/rdp/pointer.c
similarity index 62%
rename from src/protocols/rdp/rdp_pointer.c
rename to src/protocols/rdp/pointer.c
index f510b57..5a72c78 100644
--- a/src/protocols/rdp/rdp_pointer.c
+++ b/src/protocols/rdp/pointer.c
@@ -17,21 +17,21 @@
* under the License.
*/
-#include "config.h"
-
-#include "client.h"
+#include "color.h"
#include "common/cursor.h"
#include "common/display.h"
+#include "common/surface.h"
+#include "pointer.h"
#include "rdp.h"
-#include "rdp_pointer.h"
#include <cairo/cairo.h>
+#include <freerdp/codec/color.h>
#include <freerdp/freerdp.h>
+#include <freerdp/gdi/gdi.h>
#include <guacamole/client.h>
+#include <winpr/crt.h>
-#include <stdlib.h>
-
-void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
+BOOL guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
@@ -41,17 +41,18 @@
rdp_client->display, pointer->width, pointer->height);
/* Allocate data for image */
- unsigned char* data =
- (unsigned char*) malloc(pointer->width * pointer->height * 4);
+ unsigned char* data = _aligned_malloc(pointer->width * pointer->height * 4, 16);
cairo_surface_t* surface;
/* Convert to alpha cursor if mask data present */
if (pointer->andMaskData && pointer->xorMaskData)
- freerdp_alpha_cursor_convert(data,
- pointer->xorMaskData, pointer->andMaskData,
- pointer->width, pointer->height, pointer->xorBpp,
- ((rdp_freerdp_context*) context)->clrconv);
+ freerdp_image_copy_from_pointer_data(data,
+ guac_rdp_get_native_pixel_format(TRUE), 0, 0, 0,
+ pointer->width, pointer->height, pointer->xorMaskData,
+ pointer->lengthXorMask, pointer->andMaskData,
+ pointer->lengthAndMask, pointer->xorBpp,
+ &context->gdi->palette);
/* Create surface from image data */
surface = cairo_image_surface_create_for_data(
@@ -63,14 +64,16 @@
/* Free surface */
cairo_surface_destroy(surface);
- free(data);
+ _aligned_free(data);
/* Remember buffer */
((guac_rdp_pointer*) pointer)->layer = buffer;
+ return TRUE;
+
}
-void guac_rdp_pointer_set(rdpContext* context, rdpPointer* pointer) {
+BOOL guac_rdp_pointer_set(rdpContext* context, const rdpPointer* pointer) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
@@ -80,6 +83,8 @@
pointer->xPos, pointer->yPos,
((guac_rdp_pointer*) pointer)->layer->surface);
+ return TRUE;
+
}
void guac_rdp_pointer_free(rdpContext* context, rdpPointer* pointer) {
@@ -91,13 +96,31 @@
/* Free buffer */
guac_common_display_free_buffer(rdp_client->display, buffer);
+ /* NOTE: FreeRDP-allocated memory for the rdpPointer will be automatically
+ * released after this free handler is invoked */
+
}
-void guac_rdp_pointer_set_null(rdpContext* context) {
- /* STUB */
+BOOL guac_rdp_pointer_set_null(rdpContext* context) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ /* Set cursor to empty/blank graphic */
+ guac_common_cursor_set_blank(rdp_client->display->cursor);
+
+ return TRUE;
+
}
-void guac_rdp_pointer_set_default(rdpContext* context) {
- /* STUB */
+BOOL guac_rdp_pointer_set_default(rdpContext* context) {
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ /* Set cursor to embedded pointer */
+ guac_common_cursor_set_pointer(rdp_client->display->cursor);
+
+ return TRUE;
}
diff --git a/src/protocols/rdp/rdp_pointer.h b/src/protocols/rdp/pointer.h
similarity index 80%
rename from src/protocols/rdp/rdp_pointer.h
rename to src/protocols/rdp/pointer.h
index 995c8d6..b5fa6a2 100644
--- a/src/protocols/rdp/rdp_pointer.h
+++ b/src/protocols/rdp/pointer.h
@@ -17,14 +17,14 @@
* under the License.
*/
+#ifndef GUAC_RDP_POINTER_H
+#define GUAC_RDP_POINTER_H
-#ifndef _GUAC_RDP_RDP_POINTER_H
-#define _GUAC_RDP_RDP_POINTER_H
-
-#include "config.h"
#include "common/display.h"
#include <freerdp/freerdp.h>
+#include <freerdp/graphics.h>
+#include <winpr/wtypes.h>
/**
* Guacamole-specific rdpPointer data.
@@ -52,8 +52,11 @@
*
* @param pointer
* The pointer to cache.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer);
+BOOL guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer);
/**
* Sets the given cached pointer as the current pointer. The given pointer must
@@ -64,8 +67,11 @@
*
* @param pointer
* The pointer to set as the current mouse pointer.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_pointer_set(rdpContext* context, rdpPointer* pointer);
+BOOL guac_rdp_pointer_set(rdpContext* context, const rdpPointer* pointer);
/**
* Frees all Guacamole-related data associated with the given pointer, allowing
@@ -84,8 +90,11 @@
*
* @param context
* The rdpContext associated with the current RDP session.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_pointer_set_null(rdpContext* context);
+BOOL guac_rdp_pointer_set_null(rdpContext* context);
/**
* Sets the system-dependent (as in dependent on the client system) default
@@ -93,7 +102,10 @@
*
* @param context
* The rdpContext associated with the current RDP session.
+ *
+ * @return
+ * TRUE if successful, FALSE otherwise.
*/
-void guac_rdp_pointer_set_default(rdpContext* context);
+BOOL guac_rdp_pointer_set_default(rdpContext* context);
#endif
diff --git a/src/protocols/rdp/rdp_print_job.c b/src/protocols/rdp/print-job.c
similarity index 99%
rename from src/protocols/rdp/rdp_print_job.c
rename to src/protocols/rdp/print-job.c
index a3b51bc..5a75c6e 100644
--- a/src/protocols/rdp/rdp_print_job.c
+++ b/src/protocols/rdp/print-job.c
@@ -17,9 +17,7 @@
* under the License.
*/
-
-#include "config.h"
-#include "rdp_print_job.h"
+#include "print-job.h"
#include <guacamole/client.h>
#include <guacamole/protocol.h>
diff --git a/src/protocols/rdp/rdp_print_job.h b/src/protocols/rdp/print-job.h
similarity index 98%
rename from src/protocols/rdp/rdp_print_job.h
rename to src/protocols/rdp/print-job.h
index 5682f18..fb990a8 100644
--- a/src/protocols/rdp/rdp_print_job.h
+++ b/src/protocols/rdp/print-job.h
@@ -20,8 +20,6 @@
#ifndef GUAC_RDP_PRINT_JOB_H
#define GUAC_RDP_PRINT_JOB_H
-#include "config.h"
-
#include <guacamole/client.h>
#include <guacamole/stream.h>
#include <guacamole/user.h>
@@ -79,6 +77,9 @@
*/
typedef struct guac_rdp_print_job {
+ /**
+ * The Guacamole client associated with the RDP session.
+ */
guac_client* client;
/**
diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c
index d0df053..6d8381d 100644
--- a/src/protocols/rdp/rdp.c
+++ b/src/protocols/rdp/rdp.c
@@ -17,28 +17,30 @@
* under the License.
*/
-#include "config.h"
-
-#include "audio_input.h"
+#include "bitmap.h"
+#include "channels/audio-input/audio-buffer.h"
+#include "channels/audio-input/audio-input.h"
+#include "channels/cliprdr.h"
+#include "channels/disp.h"
+#include "channels/pipe-svc.h"
+#include "channels/rail.h"
+#include "channels/rdpdr/rdpdr.h"
+#include "channels/rdpsnd/rdpsnd.h"
#include "client.h"
+#include "color.h"
#include "common/cursor.h"
#include "common/display.h"
#include "common/recording.h"
-#include "dvc.h"
+#include "config.h"
#include "error.h"
+#include "fs.h"
+#include "gdi.h"
+#include "glyph.h"
#include "keyboard.h"
+#include "plugins/channels.h"
+#include "pointer.h"
+#include "print-job.h"
#include "rdp.h"
-#include "rdp_bitmap.h"
-#include "rdp_cliprdr.h"
-#include "rdp_disp.h"
-#include "rdp_fs.h"
-#include "rdp_print_job.h"
-#include "rdp_gdi.h"
-#include "rdp_glyph.h"
-#include "rdp_pointer.h"
-#include "rdp_rail.h"
-#include "rdp_stream.h"
-#include "rdp_svc.h"
#ifdef ENABLE_COMMON_SSH
#include "common-ssh/sftp.h"
@@ -46,6 +48,7 @@
#include "common-ssh/user.h"
#endif
+#include <freerdp/addin.h>
#include <freerdp/cache/bitmap.h>
#include <freerdp/cache/brush.h>
#include <freerdp/cache/glyph.h>
@@ -53,349 +56,130 @@
#include <freerdp/cache/palette.h>
#include <freerdp/cache/pointer.h>
#include <freerdp/channels/channels.h>
+#include <freerdp/client/channels.h>
#include <freerdp/freerdp.h>
+#include <freerdp/gdi/gdi.h>
+#include <freerdp/graphics.h>
+#include <freerdp/primary.h>
+#include <freerdp/settings.h>
+#include <freerdp/update.h>
#include <guacamole/audio.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/timestamp.h>
-
-#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
-#include <freerdp/client/cliprdr.h>
-#else
-#include "compat/client-cliprdr.h"
-#endif
-
-#ifdef HAVE_FREERDP_CLIENT_DISP_H
-#include <freerdp/client/disp.h>
-#endif
-
-#ifdef HAVE_FREERDP_EVENT_PUBSUB
-#include <freerdp/event.h>
-#endif
-
-#ifdef LEGACY_FREERDP
-#include "compat/rail.h"
-#else
-#include <freerdp/rail.h>
-#endif
-
-#ifdef ENABLE_WINPR
+#include <winpr/error.h>
+#include <winpr/synch.h>
#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-wtypes.h"
-#endif
-#ifdef HAVE_FREERDP_ADDIN_H
-#include <freerdp/addin.h>
-#endif
-
-#ifdef HAVE_FREERDP_CLIENT_CHANNELS_H
-#include <freerdp/client/channels.h>
-#endif
-
-#ifdef HAVE_FREERDP_VERSION_H
-#include <freerdp/version.h>
-#endif
-
-#include <errno.h>
-#include <poll.h>
-#include <pthread.h>
#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
#include <time.h>
-/**
- * Callback invoked by FreeRDP for data received along a channel. This is the
- * most recent version of the callback and uses a 16-bit unsigned integer for
- * the channel ID, as well as different type naming for the datatype of the
- * data itself. This function does nothing more than invoke
- * freerdp_channels_data() with the given arguments. The prototypes of these
- * functions are compatible in 1.2 and later, but not necessarily prior to
- * that, hence the conditional compilation of differing prototypes.
- *
- * Beware that the official purpose of these parameters is an undocumented
- * mystery. The meanings below are derived from looking at how the function is
- * used within FreeRDP.
- *
- * @param rdp_inst
- * The RDP client instance associated with the channel receiving the data.
- *
- * @param channelId
- * The integer ID of the channel that received the data.
- *
- * @param data
- * A buffer containing the received data.
- *
- * @param size
- * The number of bytes received and contained in the given buffer (the
- * number of bytes received within the PDU that resulted in this function
- * being inboked).
- *
- * @param flags
- * Channel control flags, as defined by the CHANNEL_PDU_HEADER in the RDP
- * specification.
- *
- * @param total_size
- * The total length of the chanel data being received, which may span
- * multiple PDUs (see the "length" field of CHANNEL_PDU_HEADER).
- *
- * @return
- * Zero if the received channel data was successfully handled, non-zero
- * otherwise. Note that this return value is discarded in practice.
- */
-#if defined(FREERDP_VERSION_MAJOR) \
- && (FREERDP_VERSION_MAJOR > 1 || FREERDP_VERSION_MINOR >= 2)
-static int __guac_receive_channel_data(freerdp* rdp_inst, UINT16 channelId,
- BYTE* data, int size, int flags, int total_size) {
-#else
-static int __guac_receive_channel_data(freerdp* rdp_inst, int channelId,
- UINT8* data, int size, int flags, int total_size) {
-#endif
- return freerdp_channels_data(rdp_inst, channelId,
- data, size, flags, total_size);
-}
-
-#ifdef HAVE_FREERDP_EVENT_PUBSUB
-/**
- * Called whenever a channel connects via the PubSub event system within
- * FreeRDP.
- *
- * @param context
- * The rdpContext associated with the active RDP session.
- *
- * @param e
- * Event-specific arguments, mainly the name of the channel, and a
- * reference to the associated plugin loaded for that channel by FreeRDP.
- */
-static void guac_rdp_channel_connected(rdpContext* context,
- ChannelConnectedEventArgs* e) {
-
- 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;
-
- if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) {
-#ifdef HAVE_RDPSETTINGS_SUPPORTDISPLAYCONTROL
- /* Store reference to the display update plugin once it's connected */
- if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0) {
-
- DispClientContext* disp = (DispClientContext*) e->pInterface;
-
- /* Init module with current display size */
- guac_rdp_disp_set_size(rdp_client->disp, rdp_client->settings,
- context->instance, guac_rdp_get_width(context->instance),
- guac_rdp_get_height(context->instance));
-
- /* Store connected channel */
- guac_rdp_disp_connect(rdp_client->disp, disp);
- guac_client_log(client, GUAC_LOG_DEBUG,
- "Display update channel connected.");
-
- }
-#endif
- }
-
-}
-#endif
-
BOOL rdp_freerdp_pre_connect(freerdp* instance) {
rdpContext* context = instance->context;
- rdpChannels* channels = context->channels;
+ 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;
- rdpBitmap* bitmap;
- rdpGlyph* glyph;
- rdpPointer* pointer;
- rdpPrimaryUpdate* primary;
- CLRCONV* clrconv;
+ /* Push desired settings to FreeRDP */
+ guac_rdp_push_settings(client, settings, instance);
- guac_rdp_dvc_list* dvc_list = guac_rdp_dvc_list_alloc();
-
-#ifdef HAVE_FREERDP_REGISTER_ADDIN_PROVIDER
/* Init FreeRDP add-in provider */
freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0);
-#endif
-#ifdef HAVE_FREERDP_EVENT_PUBSUB
- /* Subscribe to and handle channel connected events */
- PubSub_SubscribeChannelConnected(context->pubSub,
- (pChannelConnectedEventHandler) guac_rdp_channel_connected);
-#endif
-
-#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
/* Load "disp" plugin for display update */
if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE)
- guac_rdp_disp_load_plugin(instance->context, dvc_list);
-#endif
+ guac_rdp_disp_load_plugin(context);
/* Load "AUDIO_INPUT" plugin for audio input*/
if (settings->enable_audio_input) {
rdp_client->audio_input = guac_rdp_audio_buffer_alloc();
- guac_rdp_audio_load_plugin(instance->context, dvc_list);
+ guac_rdp_audio_load_plugin(instance->context);
}
- /* Load clipboard plugin if not disabled */
- if (!(settings->disable_copy && settings->disable_paste)
- && freerdp_channels_load_plugin(channels, instance->settings,
- "cliprdr", NULL)) {
+ /* Load "cliprdr" service if not disabled */
+ if (!(settings->disable_copy && settings->disable_paste))
+ guac_rdp_clipboard_load_plugin(rdp_client->clipboard, context);
+ else
guac_client_log(client, GUAC_LOG_WARNING,
- "Failed to load cliprdr plugin. Clipboard will not work.");
- }
+ "Copy and paste are both disabled. Clipboard plugin will not be loaded.");
/* If RDPSND/RDPDR required, load them */
if (settings->printing_enabled
|| settings->drive_enabled
|| settings->audio_enabled) {
-
- /* Load RDPDR plugin */
- if (freerdp_channels_load_plugin(channels, instance->settings,
- "guacdr", client))
- guac_client_log(client, GUAC_LOG_WARNING,
- "Failed to load guacdr plugin. Drive redirection and "
- "printing will not work. Sound MAY not work.");
-
- /* Load RDPSND plugin */
- if (freerdp_channels_load_plugin(channels, instance->settings,
- "guacsnd", client))
- guac_client_log(client, GUAC_LOG_WARNING,
- "Failed to load guacsnd alongside guacdr plugin. Sound "
- "will not work. Drive redirection and printing MAY not "
- "work.");
-
+ guac_rdpdr_load_plugin(context);
+ guac_rdpsnd_load_plugin(context);
}
/* Load RAIL plugin if RemoteApp in use */
- if (settings->remote_app != NULL) {
-
-#ifdef LEGACY_FREERDP
- RDP_PLUGIN_DATA* plugin_data = malloc(sizeof(RDP_PLUGIN_DATA) * 2);
-
- plugin_data[0].size = sizeof(RDP_PLUGIN_DATA);
- plugin_data[0].data[0] = settings->remote_app;
- plugin_data[0].data[1] = settings->remote_app_dir;
- plugin_data[0].data[2] = settings->remote_app_args;
- plugin_data[0].data[3] = NULL;
-
- plugin_data[1].size = 0;
-
- /* Attempt to load rail */
- if (freerdp_channels_load_plugin(channels, instance->settings,
- "rail", plugin_data))
- guac_client_log(client, GUAC_LOG_WARNING,
- "Failed to load rail plugin. RemoteApp will not work.");
-#else
- /* Attempt to load rail */
- if (freerdp_channels_load_plugin(channels, instance->settings,
- "rail", instance->settings))
- guac_client_log(client, GUAC_LOG_WARNING,
- "Failed to load rail plugin. RemoteApp will not work.");
-#endif
-
- }
+ if (settings->remote_app != NULL)
+ guac_rdp_rail_load_plugin(context);
/* Load SVC plugin instances for all static channels */
if (settings->svc_names != NULL) {
char** current = settings->svc_names;
do {
-
- guac_rdp_svc* svc = guac_rdp_alloc_svc(client, *current);
-
- /* Attempt to load guacsvc plugin for new static channel */
- if (freerdp_channels_load_plugin(channels, instance->settings,
- "guacsvc", svc)) {
- guac_client_log(client, GUAC_LOG_WARNING,
- "Cannot create static channel \"%s\": failed to load guacsvc plugin.",
- svc->name);
- guac_rdp_free_svc(svc);
- }
-
- /* Store and log on success */
- else {
- guac_rdp_add_svc(client, svc);
- guac_client_log(client, GUAC_LOG_INFO, "Created static channel \"%s\"...",
- svc->name);
- }
-
+ guac_rdp_pipe_svc_load_plugin(context, *current);
} while (*(++current) != NULL);
}
- /* Load DRDYNVC plugin if required */
- if (guac_rdp_load_drdynvc(instance->context, dvc_list))
+ /* Load plugin providing Dynamic Virtual Channel support, if required */
+ if (instance->settings->SupportDynamicChannels &&
+ guac_freerdp_channels_load_plugin(context, "drdynvc",
+ instance->settings)) {
guac_client_log(client, GUAC_LOG_WARNING,
"Failed to load drdynvc plugin. Display update and audio "
"input support will be disabled.");
+ }
- /* Dynamic virtual channel list is no longer needed */
- guac_rdp_dvc_list_free(dvc_list);
-
- /* Init color conversion structure */
- clrconv = calloc(1, sizeof(CLRCONV));
- clrconv->alpha = 1;
- clrconv->invert = 0;
- clrconv->rgb555 = 0;
- clrconv->palette = calloc(1, sizeof(rdpPalette));
- ((rdp_freerdp_context*) context)->clrconv = clrconv;
-
- /* Init FreeRDP cache */
- instance->context->cache = cache_new(instance->settings);
+ /* Init FreeRDP internal GDI implementation */
+ if (!gdi_init(instance, guac_rdp_get_native_pixel_format(FALSE)))
+ return FALSE;
/* Set up bitmap handling */
- bitmap = calloc(1, sizeof(rdpBitmap));
- bitmap->size = sizeof(guac_rdp_bitmap);
- bitmap->New = guac_rdp_bitmap_new;
- bitmap->Free = guac_rdp_bitmap_free;
- bitmap->Paint = guac_rdp_bitmap_paint;
- bitmap->Decompress = guac_rdp_bitmap_decompress;
- bitmap->SetSurface = guac_rdp_bitmap_setsurface;
- graphics_register_bitmap(context->graphics, bitmap);
- free(bitmap);
+ rdpBitmap bitmap = *graphics->Bitmap_Prototype;
+ bitmap.size = sizeof(guac_rdp_bitmap);
+ bitmap.New = guac_rdp_bitmap_new;
+ bitmap.Free = guac_rdp_bitmap_free;
+ bitmap.Paint = guac_rdp_bitmap_paint;
+ bitmap.SetSurface = guac_rdp_bitmap_setsurface;
+ graphics_register_bitmap(graphics, &bitmap);
/* Set up glyph handling */
- glyph = calloc(1, sizeof(rdpGlyph));
- glyph->size = sizeof(guac_rdp_glyph);
- glyph->New = guac_rdp_glyph_new;
- glyph->Free = guac_rdp_glyph_free;
- glyph->Draw = guac_rdp_glyph_draw;
- glyph->BeginDraw = guac_rdp_glyph_begindraw;
- glyph->EndDraw = guac_rdp_glyph_enddraw;
- graphics_register_glyph(context->graphics, glyph);
- free(glyph);
+ rdpGlyph glyph = *graphics->Glyph_Prototype;
+ glyph.size = sizeof(guac_rdp_glyph);
+ glyph.New = guac_rdp_glyph_new;
+ glyph.Free = guac_rdp_glyph_free;
+ glyph.Draw = guac_rdp_glyph_draw;
+ glyph.BeginDraw = guac_rdp_glyph_begindraw;
+ glyph.EndDraw = guac_rdp_glyph_enddraw;
+ graphics_register_glyph(graphics, &glyph);
/* Set up pointer handling */
- pointer = calloc(1, sizeof(rdpPointer));
- pointer->size = sizeof(guac_rdp_pointer);
- pointer->New = guac_rdp_pointer_new;
- pointer->Free = guac_rdp_pointer_free;
- pointer->Set = guac_rdp_pointer_set;
-#ifdef HAVE_RDPPOINTER_SETNULL
- pointer->SetNull = guac_rdp_pointer_set_null;
-#endif
-#ifdef HAVE_RDPPOINTER_SETDEFAULT
- pointer->SetDefault = guac_rdp_pointer_set_default;
-#endif
- graphics_register_pointer(context->graphics, pointer);
- free(pointer);
+ rdpPointer pointer = *graphics->Pointer_Prototype;
+ pointer.size = sizeof(guac_rdp_pointer);
+ pointer.New = guac_rdp_pointer_new;
+ pointer.Free = guac_rdp_pointer_free;
+ pointer.Set = guac_rdp_pointer_set;
+ pointer.SetNull = guac_rdp_pointer_set_null;
+ pointer.SetDefault = guac_rdp_pointer_set_default;
+ graphics_register_pointer(graphics, &pointer);
/* Set up GDI */
instance->update->DesktopResize = guac_rdp_gdi_desktop_resize;
instance->update->EndPaint = guac_rdp_gdi_end_paint;
- instance->update->Palette = guac_rdp_gdi_palette_update;
instance->update->SetBounds = guac_rdp_gdi_set_bounds;
- primary = instance->update->primary;
+ rdpPrimaryUpdate* primary = 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);
@@ -404,39 +188,6 @@
offscreen_cache_register_callbacks(instance->update);
palette_cache_register_callbacks(instance->update);
- /* Init channels (pre-connect) */
- if (freerdp_channels_pre_connect(channels, instance)) {
- guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error initializing RDP client channel manager");
- return FALSE;
- }
-
- return TRUE;
-
-}
-
-/**
- * Callback invoked by FreeRDP just after the connection is established with
- * the RDP server. Implementations are required to manually invoke
- * freerdp_channels_post_connect().
- *
- * @param instance
- * The FreeRDP instance that has just connected.
- *
- * @return
- * TRUE if successful, FALSE if an error occurs.
- */
-static BOOL rdp_freerdp_post_connect(freerdp* instance) {
-
- rdpContext* context = instance->context;
- guac_client* client = ((rdp_freerdp_context*) context)->client;
- rdpChannels* channels = instance->context->channels;
-
- /* Init channels (post-connect) */
- if (freerdp_channels_post_connect(channels, instance)) {
- guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error initializing RDP client channel manager");
- return FALSE;
- }
-
return TRUE;
}
@@ -499,8 +250,9 @@
* @return
* TRUE if the certificate passes verification, FALSE otherwise.
*/
-static BOOL rdp_freerdp_verify_certificate(freerdp* instance, char* subject,
- char* issuer, char* fingerprint) {
+static DWORD rdp_freerdp_verify_certificate(freerdp* instance,
+ const char* common_name, const char* subject, const char* issuer,
+ const char* fingerprint, BOOL host_mismatch) {
rdpContext* context = instance->context;
guac_client* client = ((rdp_freerdp_context*) context)->client;
@@ -510,45 +262,15 @@
/* Bypass validation if ignore_certificate given */
if (rdp_client->settings->ignore_certificate) {
guac_client_log(client, GUAC_LOG_INFO, "Certificate validation bypassed");
- return TRUE;
+ return 2; /* Accept only for this session */
}
guac_client_log(client, GUAC_LOG_INFO, "Certificate validation failed");
- return FALSE;
+ return 0; /* Reject certificate */
}
/**
- * Callback invoked by FreeRDP after a new rdpContext has been allocated and
- * associated with the current FreeRDP instance. Implementations are required
- * to manually invoke freerdp_channels_new() at this point.
- *
- * @param instance
- * The FreeRDP instance whose context has just been allocated.
- *
- * @param context
- * The newly-allocated FreeRDP context.
- */
-static void rdp_freerdp_context_new(freerdp* instance, rdpContext* context) {
- context->channels = freerdp_channels_new();
-}
-
-/**
- * Callback invoked by FreeRDP when the rdpContext is being freed. This must be
- * provided, but there is no Guacamole-specific data associated with the
- * FreeRDP context, so nothing is done here.
- *
- * @param instance
- * The FreeRDP instance whose context is being freed.
- *
- * @param context
- * The FreeRDP context being freed.
- */
-static void rdp_freerdp_context_free(freerdp* instance, rdpContext* context) {
- /* EMPTY */
-}
-
-/**
* Waits for messages from the RDP server for the given number of milliseconds.
*
* @param client
@@ -566,78 +288,30 @@
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
freerdp* rdp_inst = rdp_client->rdp_inst;
- rdpChannels* channels = rdp_inst->context->channels;
- int result;
- int index;
+ HANDLE handles[GUAC_RDP_MAX_FILE_DESCRIPTORS];
+ int num_handles = freerdp_get_event_handles(rdp_inst->context, handles,
+ GUAC_RDP_MAX_FILE_DESCRIPTORS);
- /* List of all file descriptors which we may read data from */
- void* read_fds[GUAC_RDP_MAX_FILE_DESCRIPTORS];
- int read_count = 0;
+ /* Wait for data and construct a reasonable frame */
+ int result = WaitForMultipleObjects(num_handles, handles, FALSE,
+ timeout_msecs);
- /* List of all file descriptors which data may be written to. These will
- * ultimately be ignored, but FreeRDP requires that both read and write
- * file descriptors be retrieved simultaneously. */
- void* write_fds[GUAC_RDP_MAX_FILE_DESCRIPTORS];
- int write_count = 0;
+ /* Translate WaitForMultipleObjects() return values */
+ switch (result) {
- struct pollfd fds[GUAC_RDP_MAX_FILE_DESCRIPTORS];
-
- /* Get RDP file descriptors */
- if (!freerdp_get_fds(rdp_inst, read_fds, &read_count,
- write_fds, &write_count)) {
- guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
- "Unable to read RDP file descriptors.");
- return -1;
- }
-
- /* Get RDP channel file descriptors */
- if (!freerdp_channels_get_fds(channels, rdp_inst, read_fds, &read_count,
- write_fds, &write_count)) {
- guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
- "Unable to read RDP channel file descriptors.");
- return -1;
- }
-
- /* If no file descriptors, error */
- if (read_count == 0) {
- guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
- "No file descriptors associated with RDP connection.");
- return -1;
- }
-
- /* Populate poll() array of read file descriptors */
- for (index = 0; index < read_count; index++) {
-
- struct pollfd* current = &fds[index];
-
- /* Init poll() array element with RDP file descriptor */
- current->fd = (int)(long) (read_fds[index]);
- current->events = POLLIN;
- current->revents = 0;
-
- }
-
- /* Wait until data can be read from RDP file descriptors */
- result = poll(fds, read_count, timeout_msecs);
- if (result < 0) {
-
- /* If error ignorable, pretend timout occurred */
- if (errno == EAGAIN
- || errno == EWOULDBLOCK
- || errno == EINPROGRESS
- || errno == EINTR)
+ /* Timeout elapsed before wait could complete */
+ case WAIT_TIMEOUT:
return 0;
- /* Otherwise, return as error */
- guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
- "Error waiting for file descriptor.");
- return -1;
+ /* Attempt to wait failed due to an error */
+ case WAIT_FAILED:
+ return -1;
}
- /* Return wait result */
- return result;
+ /* Wait was successful */
+ return 1;
}
@@ -686,29 +360,16 @@
rdp_client->current_surface = rdp_client->display->default_surface;
- rdp_client->requested_clipboard_format = CB_FORMAT_TEXT;
rdp_client->available_svc = guac_common_list_alloc();
-#ifdef HAVE_FREERDP_CHANNELS_GLOBAL_INIT
- freerdp_channels_global_init();
-#endif
-
/* Init client */
freerdp* rdp_inst = freerdp_new();
rdp_inst->PreConnect = rdp_freerdp_pre_connect;
- rdp_inst->PostConnect = rdp_freerdp_post_connect;
rdp_inst->Authenticate = rdp_freerdp_authenticate;
rdp_inst->VerifyCertificate = rdp_freerdp_verify_certificate;
- rdp_inst->ReceiveChannelData = __guac_receive_channel_data;
/* Allocate FreeRDP context */
-#ifdef LEGACY_FREERDP
- rdp_inst->context_size = sizeof(rdp_freerdp_context);
-#else
rdp_inst->ContextSize = sizeof(rdp_freerdp_context);
-#endif
- rdp_inst->ContextNew = (pContextNew) rdp_freerdp_context_new;
- rdp_inst->ContextFree = (pContextFree) rdp_freerdp_context_free;
freerdp_context_new(rdp_inst);
((rdp_freerdp_context*) rdp_inst->context)->client = client;
@@ -720,9 +381,6 @@
/* Set default pointer */
guac_common_cursor_set_pointer(rdp_client->display->cursor);
- /* Push desired settings to FreeRDP */
- guac_rdp_push_settings(client, settings, rdp_inst);
-
/* Connect to RDP server */
if (!freerdp_connect(rdp_inst)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND,
@@ -732,7 +390,6 @@
/* Connection complete */
rdp_client->rdp_inst = rdp_inst;
- rdpChannels* channels = rdp_inst->context->channels;
guac_timestamp last_frame_end = guac_timestamp_current();
@@ -744,9 +401,7 @@
&& !guac_rdp_disp_reconnect_needed(rdp_client->disp)) {
/* Update remote display size */
- pthread_mutex_lock(&(rdp_client->rdp_lock));
guac_rdp_disp_update_size(rdp_client->disp, settings, rdp_inst);
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
/* Wait for data and construct a reasonable frame */
int wait_result = rdp_guac_client_wait_for_messages(client,
@@ -762,42 +417,15 @@
guac_timestamp frame_end;
int frame_remaining;
- pthread_mutex_lock(&(rdp_client->rdp_lock));
-
/* Check the libfreerdp fds */
- if (!freerdp_check_fds(rdp_inst)
- || !freerdp_channels_check_fds(channels, rdp_inst)) {
+ if (!freerdp_check_event_handles(rdp_inst->context)) {
/* Flag connection failure */
wait_result = -1;
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
break;
}
- /* Check for channel events */
- wMessage* event = freerdp_channels_pop_event(channels);
- if (event) {
-
- /* Handle channel events (clipboard and RAIL) */
-#ifdef LEGACY_EVENT
- if (event->event_class == CliprdrChannel_Class)
- guac_rdp_process_cliprdr_event(client, event);
- else if (event->event_class == RailChannel_Class)
- guac_rdp_process_rail_event(client, event);
-#else
- if (GetMessageClass(event->id) == CliprdrChannel_Class)
- guac_rdp_process_cliprdr_event(client, event);
- else if (GetMessageClass(event->id) == RailChannel_Class)
- guac_rdp_process_rail_event(client, event);
-#endif
-
- freerdp_event_free(event);
-
- }
-
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
-
/* Calculate time remaining in frame */
frame_end = guac_timestamp_current();
frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION
@@ -830,9 +458,7 @@
}
/* Test whether the RDP server is closing the connection */
- pthread_mutex_lock(&(rdp_client->rdp_lock));
int connection_closing = freerdp_shall_disconnect(rdp_inst);
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
/* Close connection cleanly if server is disconnecting */
if (connection_closing)
@@ -858,16 +484,13 @@
guac_rdp_print_job_free(rdp_client->active_job);
}
- pthread_mutex_lock(&(rdp_client->rdp_lock));
-
/* Disconnect client and channels */
- freerdp_channels_close(channels, rdp_inst);
- freerdp_channels_free(channels);
freerdp_disconnect(rdp_inst);
+ /* Clean up FreeRDP internal GDI implementation */
+ gdi_free(rdp_inst);
+
/* Clean up RDP client context */
- freerdp_clrconv_free(((rdp_freerdp_context*) rdp_inst->context)->clrconv);
- cache_free(rdp_inst->context->cache);
freerdp_context_free(rdp_inst);
/* Clean up RDP client */
@@ -883,8 +506,6 @@
/* Free display */
guac_common_display_free(rdp_client->display);
- pthread_mutex_unlock(&(rdp_client->rdp_lock));
-
/* Client is now disconnected */
guac_client_log(client, GUAC_LOG_INFO, "Internal RDP client disconnected");
diff --git a/src/protocols/rdp/rdp.h b/src/protocols/rdp/rdp.h
index 1d2eb11..32f72df 100644
--- a/src/protocols/rdp/rdp.h
+++ b/src/protocols/rdp/rdp.h
@@ -20,24 +20,19 @@
#ifndef GUAC_RDP_H
#define GUAC_RDP_H
-#include "config.h"
-
-#include "audio_input.h"
+#include "channels/audio-input/audio-buffer.h"
+#include "channels/cliprdr.h"
+#include "channels/disp.h"
#include "common/clipboard.h"
#include "common/display.h"
#include "common/list.h"
#include "common/recording.h"
#include "common/surface.h"
+#include "config.h"
+#include "fs.h"
#include "keyboard.h"
-#include "rdp_disp.h"
-#include "rdp_fs.h"
-#include "rdp_print_job.h"
-#include "rdp_settings.h"
-
-#include <freerdp/freerdp.h>
-#include <freerdp/codec/color.h>
-#include <guacamole/audio.h>
-#include <guacamole/client.h>
+#include "print-job.h"
+#include "settings.h"
#ifdef ENABLE_COMMON_SSH
#include "common-ssh/sftp.h"
@@ -45,6 +40,12 @@
#include "common-ssh/user.h"
#endif
+#include <freerdp/codec/color.h>
+#include <freerdp/freerdp.h>
+#include <guacamole/audio.h>
+#include <guacamole/client.h>
+#include <winpr/wtypes.h>
+
#include <pthread.h>
#include <stdint.h>
@@ -95,17 +96,9 @@
guac_rdp_keyboard* keyboard;
/**
- * The current clipboard contents.
+ * The current state of the clipboard and the CLIPRDR channel.
*/
- guac_common_clipboard* clipboard;
-
- /**
- * The format of the clipboard which was requested. Data received from
- * the RDP server should conform to this format. This will be one of
- * several legal clipboard format values defined within FreeRDP, such as
- * CB_FORMAT_TEXT.
- */
- int requested_clipboard_format;
+ guac_rdp_clipboard* clipboard;
/**
* Audio output, if any.
@@ -161,13 +154,6 @@
guac_common_list* available_svc;
/**
- * Lock which is locked and unlocked for each RDP message, and for each
- * part of the RDP client instance which may be dynamically freed and
- * reallocated during reconnection.
- */
- pthread_mutex_t rdp_lock;
-
- /**
* Common attributes for locks.
*/
pthread_mutexattr_t attributes;
@@ -192,11 +178,6 @@
guac_client* client;
/**
- * Color conversion structure to be used to convert RDP images to PNGs.
- */
- CLRCONV* clrconv;
-
- /**
* The current color palette, as received from the RDP server.
*/
UINT32 palette[256];
diff --git a/src/protocols/rdp/rdp_bitmap.c b/src/protocols/rdp/rdp_bitmap.c
deleted file mode 100644
index f77bbae..0000000
--- a/src/protocols/rdp/rdp_bitmap.c
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "config.h"
-
-#include "client.h"
-#include "common/display.h"
-#include "common/surface.h"
-#include "rdp.h"
-#include "rdp_bitmap.h"
-#include "rdp_settings.h"
-
-#include <cairo/cairo.h>
-#include <freerdp/codec/bitmap.h>
-#include <freerdp/codec/color.h>
-#include <freerdp/freerdp.h>
-#include <guacamole/client.h>
-#include <guacamole/socket.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-wtypes.h"
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-
-void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap) {
-
- guac_client* client = ((rdp_freerdp_context*) context)->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
-
- /* Allocate buffer */
- guac_common_display_layer* buffer = guac_common_display_alloc_buffer(
- rdp_client->display, bitmap->width, bitmap->height);
-
- /* Cache image data if present */
- if (bitmap->data != NULL) {
-
- /* Create surface from image data */
- cairo_surface_t* image = cairo_image_surface_create_for_data(
- bitmap->data, CAIRO_FORMAT_RGB24,
- bitmap->width, bitmap->height, 4*bitmap->width);
-
- /* Send surface to buffer */
- guac_common_surface_draw(buffer->surface, 0, 0, image);
-
- /* Free surface */
- cairo_surface_destroy(image);
-
- }
-
- /* Store buffer reference in bitmap */
- ((guac_rdp_bitmap*) bitmap)->layer = buffer;
-
-}
-
-void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
-
- /* Convert image data if present */
- if (bitmap->data != NULL && bitmap->bpp != 32) {
-
- /* Convert image data to 32-bit RGB */
- unsigned char* image_buffer = freerdp_image_convert(bitmap->data, NULL,
- bitmap->width, bitmap->height,
- guac_rdp_get_depth(context->instance),
- 32, ((rdp_freerdp_context*) context)->clrconv);
-
- /* Free existing image, if any */
- if (image_buffer != bitmap->data) {
-#ifdef FREERDP_BITMAP_REQUIRES_ALIGNED_MALLOC
- _aligned_free(bitmap->data);
-#else
- free(bitmap->data);
-#endif
- }
-
- /* Store converted image in bitmap */
- bitmap->data = image_buffer;
-
- }
-
- /* No corresponding surface yet - caching is deferred. */
- ((guac_rdp_bitmap*) bitmap)->layer = NULL;
-
- /* Start at zero usage */
- ((guac_rdp_bitmap*) bitmap)->used = 0;
-
-}
-
-void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap) {
-
- guac_client* client = ((rdp_freerdp_context*) context)->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
-
- guac_common_display_layer* buffer = ((guac_rdp_bitmap*) bitmap)->layer;
-
- int width = bitmap->right - bitmap->left + 1;
- int height = bitmap->bottom - bitmap->top + 1;
-
- /* If not cached, cache if necessary */
- if (buffer == NULL && ((guac_rdp_bitmap*) bitmap)->used >= 1)
- guac_rdp_cache_bitmap(context, bitmap);
-
- /* If cached, retrieve from cache */
- if (buffer != NULL)
- guac_common_surface_copy(buffer->surface, 0, 0, width, height,
- rdp_client->display->default_surface,
- bitmap->left, bitmap->top);
-
- /* Otherwise, draw with stored image data */
- else if (bitmap->data != NULL) {
-
- /* Create surface from image data */
- cairo_surface_t* image = cairo_image_surface_create_for_data(
- bitmap->data, CAIRO_FORMAT_RGB24,
- width, height, 4*bitmap->width);
-
- /* Draw image on default surface */
- guac_common_surface_draw(rdp_client->display->default_surface,
- bitmap->left, bitmap->top, image);
-
- /* Free surface */
- cairo_surface_destroy(image);
-
- }
-
- /* Increment usage counter */
- ((guac_rdp_bitmap*) bitmap)->used++;
-
-}
-
-void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap) {
-
- guac_client* client = ((rdp_freerdp_context*) context)->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- guac_common_display_layer* buffer = ((guac_rdp_bitmap*) bitmap)->layer;
-
- /* If cached, free buffer */
- if (buffer != NULL)
- guac_common_display_free_buffer(rdp_client->display, buffer);
-
-}
-
-void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary) {
-
- guac_client* client = ((rdp_freerdp_context*) context)->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
-
- if (primary)
- rdp_client->current_surface = rdp_client->display->default_surface;
-
- else {
-
- /* Make sure that the recieved bitmap is not NULL before processing */
- if (bitmap == NULL) {
- guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in bitmap_setsurface instruction.");
- return;
- }
-
- /* If not available as a surface, make available. */
- if (((guac_rdp_bitmap*) bitmap)->layer == NULL)
- guac_rdp_cache_bitmap(context, bitmap);
-
- rdp_client->current_surface =
- ((guac_rdp_bitmap*) bitmap)->layer->surface;
-
- }
-
-}
-
-#ifdef LEGACY_RDPBITMAP
-void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap, UINT8* data,
- int width, int height, int bpp, int length, BOOL compressed) {
-#else
-void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap, UINT8* data,
- int width, int height, int bpp, int length, BOOL compressed, int codec_id) {
-#endif
-
- int size = width * height * 4;
-
-#ifdef FREERDP_BITMAP_REQUIRES_ALIGNED_MALLOC
- /* Free pre-existing data, if any (might be reused) */
- if (bitmap->data != NULL)
- _aligned_free(bitmap->data);
-
- /* Allocate new data */
- bitmap->data = (UINT8*) _aligned_malloc(size, 16);
-#else
- /* Free pre-existing data, if any (might be reused) */
- free(bitmap->data);
-
- /* Allocate new data */
- bitmap->data = (UINT8*) malloc(size);
-#endif
-
- if (compressed) {
-
-#ifdef HAVE_RDPCONTEXT_CODECS
- rdpCodecs* codecs = context->codecs;
-
- /* Decode as interleaved if less than 32 bits per pixel */
- if (bpp < 32) {
- freerdp_client_codecs_prepare(codecs, FREERDP_CODEC_INTERLEAVED);
-#ifdef INTERLEAVED_DECOMPRESS_TAKES_PALETTE
- interleaved_decompress(codecs->interleaved, data, length, bpp,
- &(bitmap->data), PIXEL_FORMAT_XRGB32, -1, 0, 0, width, height,
- (BYTE*) ((rdp_freerdp_context*) context)->palette);
- bitmap->bpp = 32;
-#else
- interleaved_decompress(codecs->interleaved, data, length, bpp,
- &(bitmap->data), PIXEL_FORMAT_XRGB32, -1, 0, 0, width, height);
- bitmap->bpp = bpp;
-#endif
- }
-
- /* Otherwise, decode as planar */
- else {
- freerdp_client_codecs_prepare(codecs, FREERDP_CODEC_PLANAR);
-#ifdef PLANAR_DECOMPRESS_CAN_FLIP
- planar_decompress(codecs->planar, data, length,
- &(bitmap->data), PIXEL_FORMAT_XRGB32, -1, 0, 0, width, height,
- TRUE);
- bitmap->bpp = 32;
-#else
- planar_decompress(codecs->planar, data, length,
- &(bitmap->data), PIXEL_FORMAT_XRGB32, -1, 0, 0, width, height);
- bitmap->bpp = bpp;
-#endif
- }
-#else
- bitmap_decompress(data, bitmap->data, width, height, length, bpp, bpp);
- bitmap->bpp = bpp;
-#endif
-
- }
- else {
- freerdp_image_flip(data, bitmap->data, width, height, bpp);
- bitmap->bpp = bpp;
- }
-
- bitmap->compressed = FALSE;
- bitmap->length = size;
-
-}
-
diff --git a/src/protocols/rdp/rdp_bitmap.h b/src/protocols/rdp/rdp_bitmap.h
deleted file mode 100644
index dda3f76..0000000
--- a/src/protocols/rdp/rdp_bitmap.h
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef _GUAC_RDP_RDP_BITMAP_H
-#define _GUAC_RDP_RDP_BITMAP_H
-
-#include "config.h"
-#include "common/display.h"
-
-#include <freerdp/freerdp.h>
-#include <guacamole/layer.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-wtypes.h"
-#endif
-
-/**
- * Guacamole-specific rdpBitmap data.
- */
-typedef struct guac_rdp_bitmap {
-
- /**
- * FreeRDP bitmap data - MUST GO FIRST.
- */
- rdpBitmap bitmap;
-
- /**
- * Layer containing cached image data.
- */
- guac_common_display_layer* layer;
-
- /**
- * The number of times a bitmap has been used.
- */
- int used;
-
-} guac_rdp_bitmap;
-
-/**
- * Caches the given bitmap immediately, storing its data in a remote Guacamole
- * buffer. As RDP bitmaps are frequently created, used once, and immediately
- * destroyed, we defer actual remote-side caching of RDP bitmaps until they are
- * used at least once.
- *
- * @param context
- * The rdpContext associated with the current RDP session.
- *
- * @param bitmap
- * The bitmap to cache.
- */
-void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap);
-
-/**
- * Initializes the given newly-created rdpBitmap.
- *
- * @param context
- * The rdpContext associated with the current RDP session.
- *
- * @param bitmap
- * The bitmap to initialize.
- */
-void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap);
-
-/**
- * Paints the given rdpBitmap on the primary display surface. Note that this
- * operation does NOT draw to the "current" surface set by calls to
- * guac_rdp_bitmap_setsurface().
- *
- * @param context
- * The rdpContext associated with the current RDP session.
- *
- * @param bitmap
- * The bitmap to paint. This structure will also contain the specifics of
- * the paint operation to perform, including the destination X/Y
- * coordinates.
- */
-void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap);
-
-/**
- * Frees any Guacamole-specific data associated with the given rdpBitmap.
- *
- * @param context
- * The rdpContext associated with the current RDP session.
- *
- * @param bitmap
- * The bitmap whose Guacamole-specific data is to be freed.
- */
-void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap);
-
-/**
- * Sets the given rdpBitmap as the drawing surface for future operations or,
- * if the primary flag is set, resets the current drawing surface to the
- * primary drawing surface of the remote display.
- *
- * @param context
- * The rdpContext associated with the current RDP session.
- *
- * @param bitmap
- * The rdpBitmap to set as the current drawing surface. This parameter is
- * only valid if the primary flag is FALSE.
- *
- * @param primary
- * TRUE if the bitmap parameter should be ignored, and the current drawing
- * surface should be reset to the primary drawing surface of the remote
- * display, FALSE otherwise.
- */
-void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap,
- BOOL primary);
-
-#ifdef LEGACY_RDPBITMAP
-/**
- * Decompresses or copies the given image data, storing the result within the
- * given bitmap, depending on the compressed flag. Note that even if the
- * received data is not compressed, it is the duty of this function to also
- * flip received data, if the row order is backwards.
- *
- * @param context
- * The rdpContext associated with the current RDP session.
- *
- * @param bitmap
- * The bitmap in which the decompressed/copied data should be stored.
- *
- * @param data
- * Possibly-compressed image data.
- *
- * @param width
- * The width of the image data, in pixels.
- *
- * @param height
- * The height of the image data, in pixels.
- *
- * @param bpp
- * The number of bits per pixel in the image data.
- *
- * @param length
- * The length of the image data, in bytes.
- *
- * @param compressed
- * TRUE if the image data is compressed, FALSE otherwise.
- */
-void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap,
- UINT8* data, int width, int height, int bpp, int length,
- BOOL compressed);
-#else
-/**
- * Decompresses or copies the given image data, storing the result within the
- * given bitmap, depending on the compressed flag. Note that even if the
- * received data is not compressed, it is the duty of this function to also
- * flip received data, if the row order is backwards.
- *
- * @param context
- * The rdpContext associated with the current RDP session.
- *
- * @param bitmap
- * The bitmap in which the decompressed/copied data should be stored.
- *
- * @param data
- * Possibly-compressed image data.
- *
- * @param width
- * The width of the image data, in pixels.
- *
- * @param height
- * The height of the image data, in pixels.
- *
- * @param bpp
- * The number of bits per pixel in the image data.
- *
- * @param length
- * The length of the image data, in bytes.
- *
- * @param compressed
- * TRUE if the image data is compressed, FALSE otherwise.
- *
- * @param codec_id
- * The ID of the codec used to compress the image data. This parameter is
- * currently ignored.
- */
-void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap,
- UINT8* data, int width, int height, int bpp, int length,
- BOOL compressed, int codec_id);
-#endif
-
-#endif
diff --git a/src/protocols/rdp/rdp_cliprdr.c b/src/protocols/rdp/rdp_cliprdr.c
deleted file mode 100644
index 903752c..0000000
--- a/src/protocols/rdp/rdp_cliprdr.c
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "config.h"
-
-#include "client.h"
-#include "common/clipboard.h"
-#include "common/iconv.h"
-#include "rdp.h"
-#include "rdp_cliprdr.h"
-
-#include <freerdp/channels/channels.h>
-#include <freerdp/freerdp.h>
-#include <freerdp/utils/event.h>
-#include <guacamole/client.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-wtypes.h"
-#endif
-
-#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
-#include <freerdp/client/cliprdr.h>
-#else
-#include "compat/client-cliprdr.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-void guac_rdp_process_cliprdr_event(guac_client* client, wMessage* event) {
-
-#ifdef LEGACY_EVENT
- switch (event->event_type) {
-#else
- switch (GetMessageType(event->id)) {
-#endif
-
- case CliprdrChannel_MonitorReady:
- guac_rdp_process_cb_monitor_ready(client, event);
- break;
-
- case CliprdrChannel_FormatList:
- guac_rdp_process_cb_format_list(client,
- (RDP_CB_FORMAT_LIST_EVENT*) event);
- break;
-
- case CliprdrChannel_DataRequest:
- guac_rdp_process_cb_data_request(client,
- (RDP_CB_DATA_REQUEST_EVENT*) event);
- break;
-
- case CliprdrChannel_DataResponse:
- guac_rdp_process_cb_data_response(client,
- (RDP_CB_DATA_RESPONSE_EVENT*) event);
- break;
-
- default:
-#ifdef LEGACY_EVENT
- guac_client_log(client, GUAC_LOG_INFO,
- "Unknown cliprdr event type: 0x%x",
- event->event_type);
-#else
- guac_client_log(client, GUAC_LOG_INFO,
- "Unknown cliprdr event type: 0x%x",
- GetMessageType(event->id));
-#endif
-
- }
-
-}
-
-void guac_rdp_process_cb_monitor_ready(guac_client* client, wMessage* event) {
-
- rdpChannels* channels =
- ((guac_rdp_client*) client->data)->rdp_inst->context->channels;
-
- RDP_CB_FORMAT_LIST_EVENT* format_list =
- (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(
- CliprdrChannel_Class,
- CliprdrChannel_FormatList,
- NULL, NULL);
-
- /* Received notification of clipboard support. */
-
- /* Respond with supported format list */
- format_list->formats = (UINT32*) malloc(sizeof(UINT32)*2);
- format_list->formats[0] = CB_FORMAT_TEXT;
- format_list->formats[1] = CB_FORMAT_UNICODETEXT;
- format_list->num_formats = 2;
-
- freerdp_channels_send_event(channels, (wMessage*) format_list);
-
-}
-
-/**
- * Sends a clipboard data request for the given format.
- *
- * @param client
- * The guac_client associated with the current RDP session.
- *
- * @param format
- * The clipboard format to request. This format must be one of the
- * documented values used by the CLIPRDR channel for clipboard format IDs.
- */
-static void __guac_rdp_cb_request_format(guac_client* client, int format) {
-
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- rdpChannels* channels = rdp_client->rdp_inst->context->channels;
-
- /* Create new data request */
- RDP_CB_DATA_REQUEST_EVENT* data_request =
- (RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new(
- CliprdrChannel_Class,
- CliprdrChannel_DataRequest,
- NULL, NULL);
-
- /* Set to requested format */
- rdp_client->requested_clipboard_format = format;
- data_request->format = format;
-
- /* Send request */
- freerdp_channels_send_event(channels, (wMessage*) data_request);
-
-}
-
-void guac_rdp_process_cb_format_list(guac_client* client,
- RDP_CB_FORMAT_LIST_EVENT* event) {
-
- int formats = 0;
-
- /* Received notification of available data */
-
- int i;
- for (i=0; i<event->num_formats; i++) {
-
- /* If plain text available, request it */
- if (event->formats[i] == CB_FORMAT_TEXT)
- formats |= GUAC_RDP_CLIPBOARD_FORMAT_CP1252;
- else if (event->formats[i] == CB_FORMAT_UNICODETEXT)
- formats |= GUAC_RDP_CLIPBOARD_FORMAT_UTF16;
-
- }
-
- /* Prefer Unicode to plain text */
- if (formats & GUAC_RDP_CLIPBOARD_FORMAT_UTF16) {
- __guac_rdp_cb_request_format(client, CB_FORMAT_UNICODETEXT);
- return;
- }
-
- /* Use plain text if Unicode unavailable */
- if (formats & GUAC_RDP_CLIPBOARD_FORMAT_CP1252) {
- __guac_rdp_cb_request_format(client, CB_FORMAT_TEXT);
- return;
- }
-
- /* Ignore if no supported format available */
- guac_client_log(client, GUAC_LOG_INFO, "Ignoring unsupported clipboard data");
-
-}
-
-void guac_rdp_process_cb_data_request(guac_client* client,
- RDP_CB_DATA_REQUEST_EVENT* event) {
-
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- rdpChannels* channels = rdp_client->rdp_inst->context->channels;
-
- guac_iconv_write* writer;
- const char* input = rdp_client->clipboard->buffer;
- char* output = malloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
-
- RDP_CB_DATA_RESPONSE_EVENT* data_response;
-
- /* Determine output encoding */
- switch (event->format) {
-
- case CB_FORMAT_TEXT:
- writer = GUAC_WRITE_CP1252;
- break;
-
- case CB_FORMAT_UNICODETEXT:
- writer = GUAC_WRITE_UTF16;
- break;
-
- default:
- guac_client_log(client, GUAC_LOG_ERROR,
- "Server requested unsupported clipboard data type");
- free(output);
- return;
-
- }
-
- /* Create new data response */
- data_response = (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(
- CliprdrChannel_Class,
- CliprdrChannel_DataResponse,
- NULL, NULL);
-
- /* Set data and size */
- data_response->data = (BYTE*) output;
- guac_iconv(GUAC_READ_UTF8, &input, rdp_client->clipboard->length,
- writer, &output, GUAC_RDP_CLIPBOARD_MAX_LENGTH);
- data_response->size = ((BYTE*) output) - data_response->data;
-
- /* Send response */
- freerdp_channels_send_event(channels, (wMessage*) data_response);
-
-}
-
-void guac_rdp_process_cb_data_response(guac_client* client,
- RDP_CB_DATA_RESPONSE_EVENT* event) {
-
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- char received_data[GUAC_RDP_CLIPBOARD_MAX_LENGTH];
-
- /* Ignore received text if outbound clipboard transfer is disabled */
- if (rdp_client->settings->disable_copy)
- return;
-
- guac_iconv_read* reader;
- const char* input = (char*) event->data;
- char* output = received_data;
-
- /* Find correct source encoding */
- switch (rdp_client->requested_clipboard_format) {
-
- /* Non-Unicode */
- case CB_FORMAT_TEXT:
- reader = GUAC_READ_CP1252;
- break;
-
- /* Unicode (UTF-16) */
- case CB_FORMAT_UNICODETEXT:
- reader = GUAC_READ_UTF16;
- break;
-
- default:
- guac_client_log(client, GUAC_LOG_ERROR, "Requested clipboard data in "
- "unsupported format %i",
- rdp_client->requested_clipboard_format);
- return;
-
- }
-
- /* Convert send clipboard data */
- if (guac_iconv(reader, &input, event->size,
- GUAC_WRITE_UTF8, &output, sizeof(received_data))) {
-
- int length = strnlen(received_data, sizeof(received_data));
- guac_common_clipboard_reset(rdp_client->clipboard, "text/plain");
- guac_common_clipboard_append(rdp_client->clipboard, received_data, length);
- guac_common_clipboard_send(rdp_client->clipboard, client);
-
- }
-
-}
-
diff --git a/src/protocols/rdp/rdp_cliprdr.h b/src/protocols/rdp/rdp_cliprdr.h
deleted file mode 100644
index b66e1b7..0000000
--- a/src/protocols/rdp/rdp_cliprdr.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef __GUAC_RDP_RDP_CLIPRDR_H
-#define __GUAC_RDP_RDP_CLIPRDR_H
-
-#include "config.h"
-
-#include <guacamole/client.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
-#include <freerdp/client/cliprdr.h>
-#else
-#include "compat/client-cliprdr.h"
-#endif
-
-/**
- * Clipboard format for text encoded in Windows CP1252.
- */
-#define GUAC_RDP_CLIPBOARD_FORMAT_CP1252 1
-
-/**
- * Clipboard format for text encoded in UTF-16.
- */
-#define GUAC_RDP_CLIPBOARD_FORMAT_UTF16 2
-
-/**
- * Called within the main RDP connection thread whenever a CLIPRDR message is
- * received. This function will dispatch that message to an appropriate
- * function, specific to that message type.
- *
- * @param client
- * The guac_client associated with the current RDP session.
- *
- * @param event
- * The received CLIPRDR message.
- */
-void guac_rdp_process_cliprdr_event(guac_client* client, wMessage* event);
-
-/**
- * Handles the given CLIPRDR event, which MUST be a Monitor Ready event. It
- * is the responsibility of this function to respond to the Monitor Ready
- * event with a list of supported clipboard formats.
- *
- * @param client
- * The guac_client associated with the current RDP session.
- *
- * @param event
- * The received CLIPRDR message, which must be a Monitor Ready event.
- */
-void guac_rdp_process_cb_monitor_ready(guac_client* client, wMessage* event);
-
-/**
- * Handles the given CLIPRDR event, which MUST be a Format List event. It
- * is the responsibility of this function to respond to the Format List
- * event with a request for clipboard data in one of the enumerated formats.
- * This event is fired whenever remote clipboard data is available.
- *
- * @param client
- * The guac_client associated with the current RDP session.
- *
- * @param event
- * The received CLIPRDR message, which must be a Format List event.
- */
-void guac_rdp_process_cb_format_list(guac_client* client,
- RDP_CB_FORMAT_LIST_EVENT* event);
-
-/**
- * Handles the given CLIPRDR event, which MUST be a Data Request event. It
- * is the responsibility of this function to respond to the Data Request
- * event with a data response containing the current clipoard contents.
- *
- * @param client
- * The guac_client associated with the current RDP session.
- *
- * @param event
- * The received CLIPRDR message, which must be a Data Request event.
- */
-void guac_rdp_process_cb_data_request(guac_client* client,
- RDP_CB_DATA_REQUEST_EVENT* event);
-
-/**
- * Handles the given CLIPRDR event, which MUST be a Data Response event. It
- * is the responsibility of this function to read and forward the received
- * clipboard data to connected clients.
- *
- * @param client
- * The guac_client associated with the current RDP session.
- *
- * @param event
- * The received CLIPRDR message, which must be a Data Response event.
- */
-void guac_rdp_process_cb_data_response(guac_client* client,
- RDP_CB_DATA_RESPONSE_EVENT* event);
-
-#endif
-
diff --git a/src/protocols/rdp/rdp_color.c b/src/protocols/rdp/rdp_color.c
deleted file mode 100644
index 0b7de7f..0000000
--- a/src/protocols/rdp/rdp_color.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "config.h"
-
-#include "client.h"
-#include "rdp.h"
-#include "rdp_settings.h"
-
-#include <freerdp/codec/color.h>
-#include <freerdp/freerdp.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-wtypes.h"
-#endif
-
-UINT32 guac_rdp_convert_color(rdpContext* context, UINT32 color) {
-
-#ifdef HAVE_FREERDP_CONVERT_GDI_ORDER_COLOR
- UINT32* palette = ((rdp_freerdp_context*) context)->palette;
-
- /* Convert given color to ARGB32 */
- return freerdp_convert_gdi_order_color(color,
- guac_rdp_get_depth(context->instance), PIXEL_FORMAT_ARGB32,
- (BYTE*) palette);
-
-#elif defined(HAVE_FREERDP_COLOR_CONVERT_DRAWING_ORDER_COLOR_TO_GDI_COLOR)
- CLRCONV* clrconv = ((rdp_freerdp_context*) context)->clrconv;
-
- /* Convert given color to ARGB32 */
- return freerdp_color_convert_drawing_order_color_to_gdi_color(color,
- guac_rdp_get_depth(context->instance), clrconv);
-
-#else
- CLRCONV* clrconv = ((rdp_freerdp_context*) context)->clrconv;
-
- /* Convert given color to ARGB32 */
- return freerdp_color_convert_var(color,
- guac_rdp_get_depth(context->instance), 32,
- clrconv);
-#endif
-
-}
-
diff --git a/src/protocols/rdp/rdp_rail.c b/src/protocols/rdp/rdp_rail.c
deleted file mode 100644
index 671be8d..0000000
--- a/src/protocols/rdp/rdp_rail.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "config.h"
-
-#include "client.h"
-#include "rdp.h"
-#include "rdp_rail.h"
-#include "rdp_settings.h"
-
-#include <freerdp/channels/channels.h>
-#include <freerdp/freerdp.h>
-#include <freerdp/utils/event.h>
-#include <guacamole/client.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-wtypes.h"
-#endif
-
-#ifdef LEGACY_FREERDP
-#include "compat/rail.h"
-#else
-#include <freerdp/rail.h>
-#endif
-
-#include <stddef.h>
-
-void guac_rdp_process_rail_event(guac_client* client, wMessage* event) {
-
-#ifdef LEGACY_EVENT
- switch (event->event_type) {
-#else
- switch (GetMessageType(event->id)) {
-#endif
-
- /* Get system parameters */
- case RailChannel_GetSystemParam:
- guac_rdp_process_rail_get_sysparam(client, event);
- break;
-
- /* Currently ignored events */
- case RailChannel_ServerSystemParam:
- case RailChannel_ServerExecuteResult:
- case RailChannel_ServerMinMaxInfo:
- case RailChannel_ServerLocalMoveSize:
- case RailChannel_ServerGetAppIdResponse:
- case RailChannel_ServerLanguageBarInfo:
- break;
-
- default:
-#ifdef LEGACY_EVENT
- guac_client_log(client, GUAC_LOG_INFO,
- "Unknown rail event type: 0x%x",
- event->event_type);
-#else
- guac_client_log(client, GUAC_LOG_INFO,
- "Unknown rail event type: 0x%x",
- GetMessageType(event->id));
-#endif
-
- }
-
-}
-
-void guac_rdp_process_rail_get_sysparam(guac_client* client, wMessage* event) {
-
- wMessage* response;
- RAIL_SYSPARAM_ORDER* sysparam;
-
- /* Get channels */
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- rdpChannels* channels = rdp_client->rdp_inst->context->channels;
-
- /* Get sysparam structure */
-#ifdef LEGACY_EVENT
- sysparam = (RAIL_SYSPARAM_ORDER*) event->user_data;
-#else
- sysparam = (RAIL_SYSPARAM_ORDER*) event->wParam;
-#endif
-
- response = freerdp_event_new(RailChannel_Class,
- RailChannel_ClientSystemParam,
- NULL,
- sysparam);
-
- /* Work area */
- sysparam->workArea.left = 0;
- sysparam->workArea.top = 0;
- sysparam->workArea.right = rdp_client->settings->width;
- sysparam->workArea.bottom = rdp_client->settings->height;
-
- /* Taskbar */
- sysparam->taskbarPos.left = 0;
- sysparam->taskbarPos.top = 0;
- sysparam->taskbarPos.right = 0;
- sysparam->taskbarPos.bottom = 0;
-
- sysparam->dragFullWindows = FALSE;
-
- /* Send response */
- freerdp_channels_send_event(channels, response);
-
-}
-
diff --git a/src/protocols/rdp/rdp_rail.h b/src/protocols/rdp/rdp_rail.h
deleted file mode 100644
index 949eb3e..0000000
--- a/src/protocols/rdp/rdp_rail.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef __GUAC_RDP_RDP_RAIL_H
-#define __GUAC_RDP_RDP_RAIL_H
-
-#include "config.h"
-
-#include <guacamole/client.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-/**
- * Dispatches a given RAIL event to the appropriate handler.
- *
- * @param client
- * The guac_client associated with the current RDP session.
- *
- * @param event
- * The RAIL event to process.
- */
-void guac_rdp_process_rail_event(guac_client* client, wMessage* event);
-
-/**
- * Handles the event sent when updating system parameters. The event given
- * MUST be a SYSPARAM event.
- *
- * @param client
- * The guac_client associated with the current RDP session.
- *
- * @param event
- * The system parameter event to process.
- */
-void guac_rdp_process_rail_get_sysparam(guac_client* client, wMessage* event);
-
-#endif
-
diff --git a/src/protocols/rdp/rdp_status.h b/src/protocols/rdp/rdp_status.h
deleted file mode 100644
index 6a2d5b6..0000000
--- a/src/protocols/rdp/rdp_status.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef __GUAC_RDP_STATUS_H
-#define __GUAC_RDP_STATUS_H
-
-/**
- * RDP-specific status constants.
- *
- * @file rdp_status.h
- */
-
-#include "config.h"
-
-/* Include any constants from winpr/file.h, if available */
-
-#ifdef ENABLE_WINPR
-#include <winpr/file.h>
-#endif
-
-/* Constants which MAY be defined within FreeRDP */
-
-#ifndef STATUS_SUCCESS
-#define STATUS_SUCCESS 0x00000000
-#define STATUS_NO_MORE_FILES 0x80000006
-#define STATUS_DEVICE_OFF_LINE 0x80000010
-#define STATUS_NOT_IMPLEMENTED 0xC0000002
-#define STATUS_INVALID_PARAMETER 0xC000000D
-#define STATUS_NO_SUCH_FILE 0xC000000F
-#define STATUS_END_OF_FILE 0xC0000011
-#define STATUS_ACCESS_DENIED 0xC0000022
-#define STATUS_OBJECT_NAME_COLLISION 0xC0000035
-#define STATUS_DISK_FULL 0xC000007F
-#define STATUS_FILE_INVALID 0xC0000098
-#define STATUS_FILE_IS_A_DIRECTORY 0xC00000BA
-#define STATUS_NOT_SUPPORTED 0xC00000BB
-#define STATUS_NOT_A_DIRECTORY 0xC0000103
-#define STATUS_TOO_MANY_OPENED_FILES 0xC000011F
-#define STATUS_CANNOT_DELETE 0xC0000121
-#define STATUS_FILE_DELETED 0xC0000123
-#define STATUS_FILE_CLOSED 0xC0000128
-#endif
-
-/* Constants which are NEVER defined within FreeRDP */
-
-#define STATUS_FILE_SYSTEM_LIMITATION 0xC0000427
-#define STATUS_FILE_TOO_LARGE 0xC0000904
-
-#endif
diff --git a/src/protocols/rdp/rdp_stream.c b/src/protocols/rdp/rdp_stream.c
deleted file mode 100644
index 533ff07..0000000
--- a/src/protocols/rdp/rdp_stream.c
+++ /dev/null
@@ -1,594 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#include "config.h"
-#include "client.h"
-#include "common/clipboard.h"
-#include "rdp.h"
-#include "rdp_fs.h"
-#include "rdp_svc.h"
-#include "rdp_stream.h"
-
-#include <freerdp/freerdp.h>
-#include <freerdp/channels/channels.h>
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-#include <guacamole/socket.h>
-#include <guacamole/stream.h>
-#include <guacamole/string.h>
-
-#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
-#include <freerdp/client/cliprdr.h>
-#else
-#include "compat/client-cliprdr.h"
-#endif
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-stream.h"
-#include "compat/winpr-wtypes.h"
-#endif
-
-#include <stdlib.h>
-
-/**
- * Writes the given filename to the given upload path, sanitizing the filename
- * and translating the filename to the root directory.
- */
-static void __generate_upload_path(const char* filename, char* path) {
-
- int i;
-
- /* Add initial backslash */
- *(path++) = '\\';
-
- for (i=1; i<GUAC_RDP_FS_MAX_PATH; i++) {
-
- /* Get current, stop at end */
- char c = *(filename++);
- if (c == '\0')
- break;
-
- /* Replace special characters with underscores */
- if (c == '/' || c == '\\')
- c = '_';
-
- *(path++) = c;
-
- }
-
- /* Terminate path */
- *path = '\0';
-
-}
-
-int guac_rdp_upload_file_handler(guac_user* user, guac_stream* stream,
- char* mimetype, char* filename) {
-
- guac_client* client = user->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
-
- int file_id;
- guac_rdp_stream* rdp_stream;
- char file_path[GUAC_RDP_FS_MAX_PATH];
-
- /* Get filesystem, return error if no filesystem */
- guac_rdp_fs* fs = rdp_client->filesystem;
- if (fs == NULL) {
- guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
- GUAC_PROTOCOL_STATUS_SERVER_ERROR);
- guac_socket_flush(user->socket);
- return 0;
- }
-
- /* Translate name */
- __generate_upload_path(filename, file_path);
-
- /* Open file */
- file_id = guac_rdp_fs_open(fs, file_path, ACCESS_GENERIC_WRITE, 0,
- DISP_FILE_OVERWRITE_IF, 0);
- if (file_id < 0) {
- guac_protocol_send_ack(user->socket, stream, "FAIL (CANNOT OPEN)",
- GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
- guac_socket_flush(user->socket);
- return 0;
- }
-
- /* Init upload status */
- rdp_stream = malloc(sizeof(guac_rdp_stream));
- rdp_stream->type = GUAC_RDP_UPLOAD_STREAM;
- rdp_stream->upload_status.offset = 0;
- rdp_stream->upload_status.file_id = file_id;
- stream->data = rdp_stream;
- stream->blob_handler = guac_rdp_upload_blob_handler;
- stream->end_handler = guac_rdp_upload_end_handler;
-
- guac_protocol_send_ack(user->socket, stream, "OK (STREAM BEGIN)",
- GUAC_PROTOCOL_STATUS_SUCCESS);
- guac_socket_flush(user->socket);
- return 0;
-
-}
-
-int guac_rdp_svc_pipe_handler(guac_user* user, guac_stream* stream,
- char* mimetype, char* name) {
-
- guac_rdp_stream* rdp_stream;
- guac_rdp_svc* svc = guac_rdp_get_svc(user->client, name);
-
- /* Fail if no such SVC */
- if (svc == NULL) {
- guac_user_log(user, GUAC_LOG_ERROR,
- "Requested non-existent pipe: \"%s\".",
- name);
- guac_protocol_send_ack(user->socket, stream, "FAIL (NO SUCH PIPE)",
- GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
- guac_socket_flush(user->socket);
- return 0;
- }
- else
- guac_user_log(user, GUAC_LOG_ERROR,
- "Inbound half of channel \"%s\" connected.",
- name);
-
- /* Init stream data */
- stream->data = rdp_stream = malloc(sizeof(guac_rdp_stream));
- stream->blob_handler = guac_rdp_svc_blob_handler;
- rdp_stream->type = GUAC_RDP_INBOUND_SVC_STREAM;
- rdp_stream->svc = svc;
-
- return 0;
-
-}
-
-int guac_rdp_clipboard_handler(guac_user* user, guac_stream* stream,
- char* mimetype) {
-
- guac_client* client = user->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- guac_rdp_stream* rdp_stream;
-
- /* Init stream data */
- stream->data = rdp_stream = malloc(sizeof(guac_rdp_stream));
- stream->blob_handler = guac_rdp_clipboard_blob_handler;
- stream->end_handler = guac_rdp_clipboard_end_handler;
- rdp_stream->type = GUAC_RDP_INBOUND_CLIPBOARD_STREAM;
-
- guac_common_clipboard_reset(rdp_client->clipboard, mimetype);
- return 0;
-
-}
-
-int guac_rdp_upload_blob_handler(guac_user* user, guac_stream* stream,
- void* data, int length) {
-
- int bytes_written;
- guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
-
- /* Get filesystem, return error if no filesystem 0*/
- guac_client* client = user->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- guac_rdp_fs* fs = rdp_client->filesystem;
- if (fs == NULL) {
- guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
- GUAC_PROTOCOL_STATUS_SERVER_ERROR);
- guac_socket_flush(user->socket);
- return 0;
- }
-
- /* Write entire block */
- while (length > 0) {
-
- /* Attempt write */
- bytes_written = guac_rdp_fs_write(fs,
- rdp_stream->upload_status.file_id,
- rdp_stream->upload_status.offset,
- data, length);
-
- /* On error, abort */
- if (bytes_written < 0) {
- guac_protocol_send_ack(user->socket, stream,
- "FAIL (BAD WRITE)",
- GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
- guac_socket_flush(user->socket);
- return 0;
- }
-
- /* Update counters */
- rdp_stream->upload_status.offset += bytes_written;
- data += bytes_written;
- length -= bytes_written;
-
- }
-
- guac_protocol_send_ack(user->socket, stream, "OK (DATA RECEIVED)",
- GUAC_PROTOCOL_STATUS_SUCCESS);
- guac_socket_flush(user->socket);
- return 0;
-
-}
-
-int guac_rdp_svc_blob_handler(guac_user* user, guac_stream* stream,
- void* data, int length) {
-
- guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
-
- /* Write blob data to SVC directly */
- guac_rdp_svc_write(rdp_stream->svc, data, length);
-
- guac_protocol_send_ack(user->socket, stream, "OK (DATA RECEIVED)",
- GUAC_PROTOCOL_STATUS_SUCCESS);
- guac_socket_flush(user->socket);
- return 0;
-
-}
-
-int guac_rdp_clipboard_blob_handler(guac_user* user, guac_stream* stream,
- void* data, int length) {
-
- guac_client* client = user->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- guac_common_clipboard_append(rdp_client->clipboard, (char*) data, length);
-
- return 0;
-}
-
-int guac_rdp_upload_end_handler(guac_user* user, guac_stream* stream) {
-
- guac_client* client = user->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
-
- /* Get filesystem, return error if no filesystem */
- guac_rdp_fs* fs = rdp_client->filesystem;
- if (fs == NULL) {
- guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
- GUAC_PROTOCOL_STATUS_SERVER_ERROR);
- guac_socket_flush(user->socket);
- return 0;
- }
-
- /* Close file */
- guac_rdp_fs_close(fs, rdp_stream->upload_status.file_id);
-
- /* Acknowledge stream end */
- guac_protocol_send_ack(user->socket, stream, "OK (STREAM END)",
- GUAC_PROTOCOL_STATUS_SUCCESS);
- guac_socket_flush(user->socket);
-
- free(rdp_stream);
- return 0;
-
-}
-
-int guac_rdp_clipboard_end_handler(guac_user* user, guac_stream* stream) {
-
- guac_client* client = user->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
-
- /* Terminate clipboard data with NULL */
- guac_common_clipboard_append(rdp_client->clipboard, "", 1);
-
- /* Notify RDP server of new data, if connected */
- freerdp* rdp_inst = rdp_client->rdp_inst;
- if (rdp_inst != NULL) {
-
- rdpChannels* channels = rdp_inst->context->channels;
-
- RDP_CB_FORMAT_LIST_EVENT* format_list =
- (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(
- CliprdrChannel_Class,
- CliprdrChannel_FormatList,
- NULL, NULL);
-
- /* Notify server that text data is now available */
- format_list->formats = (UINT32*) malloc(sizeof(UINT32) * 2);
- format_list->formats[0] = CB_FORMAT_TEXT;
- format_list->formats[1] = CB_FORMAT_UNICODETEXT;
- format_list->num_formats = 2;
-
- freerdp_channels_send_event(channels, (wMessage*) format_list);
-
- }
-
- return 0;
-}
-
-int guac_rdp_download_ack_handler(guac_user* user, guac_stream* stream,
- char* message, guac_protocol_status status) {
-
- guac_client* client = user->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
-
- /* Get filesystem, return error if no filesystem */
- guac_rdp_fs* fs = rdp_client->filesystem;
- if (fs == NULL) {
- guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
- GUAC_PROTOCOL_STATUS_SERVER_ERROR);
- guac_socket_flush(user->socket);
- return 0;
- }
-
- /* If successful, read data */
- if (status == GUAC_PROTOCOL_STATUS_SUCCESS) {
-
- /* Attempt read into buffer */
- char buffer[4096];
- int bytes_read = guac_rdp_fs_read(fs,
- rdp_stream->download_status.file_id,
- rdp_stream->download_status.offset, buffer, sizeof(buffer));
-
- /* If bytes read, send as blob */
- if (bytes_read > 0) {
- rdp_stream->download_status.offset += bytes_read;
- guac_protocol_send_blob(user->socket, stream,
- buffer, bytes_read);
- }
-
- /* If EOF, send end */
- else if (bytes_read == 0) {
- guac_protocol_send_end(user->socket, stream);
- guac_user_free_stream(user, stream);
- free(rdp_stream);
- }
-
- /* Otherwise, fail stream */
- else {
- guac_user_log(user, GUAC_LOG_ERROR,
- "Error reading file for download");
- guac_protocol_send_end(user->socket, stream);
- guac_user_free_stream(user, stream);
- free(rdp_stream);
- }
-
- guac_socket_flush(user->socket);
-
- }
-
- /* Otherwise, return stream to user */
- else
- guac_user_free_stream(user, stream);
-
- return 0;
-
-}
-
-int guac_rdp_ls_ack_handler(guac_user* user, guac_stream* stream,
- char* message, guac_protocol_status status) {
-
- int blob_written = 0;
- const char* filename;
-
- guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
-
- /* If unsuccessful, free stream and abort */
- if (status != GUAC_PROTOCOL_STATUS_SUCCESS) {
- guac_rdp_fs_close(rdp_stream->ls_status.fs,
- rdp_stream->ls_status.file_id);
- guac_user_free_stream(user, stream);
- free(rdp_stream);
- return 0;
- }
-
- /* While directory entries remain */
- while ((filename = guac_rdp_fs_read_dir(rdp_stream->ls_status.fs,
- rdp_stream->ls_status.file_id)) != NULL
- && !blob_written) {
-
- char absolute_path[GUAC_RDP_FS_MAX_PATH];
-
- /* Skip current and parent directory entries */
- if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
- continue;
-
- /* Concatenate into absolute path - skip if invalid */
- if (!guac_rdp_fs_append_filename(absolute_path,
- rdp_stream->ls_status.directory_name, filename)) {
-
- guac_user_log(user, GUAC_LOG_DEBUG,
- "Skipping filename \"%s\" - filename is invalid or "
- "resulting path is too long", filename);
-
- continue;
- }
-
- /* Attempt to open file to determine type */
- int file_id = guac_rdp_fs_open(rdp_stream->ls_status.fs, absolute_path,
- ACCESS_GENERIC_READ, 0, DISP_FILE_OPEN, 0);
- if (file_id < 0)
- continue;
-
- /* Get opened file */
- guac_rdp_fs_file* file = guac_rdp_fs_get_file(rdp_stream->ls_status.fs,
- file_id);
- if (file == NULL) {
- guac_client_log(rdp_stream->ls_status.fs->client, GUAC_LOG_DEBUG,
- "%s: Successful open produced bad file_id: %i",
- __func__, file_id);
- return 0;
- }
-
- /* Determine mimetype */
- const char* mimetype;
- if (file->attributes & FILE_ATTRIBUTE_DIRECTORY)
- mimetype = GUAC_USER_STREAM_INDEX_MIMETYPE;
- else
- mimetype = "application/octet-stream";
-
- /* Write entry */
- blob_written |= guac_common_json_write_property(user, stream,
- &rdp_stream->ls_status.json_state, absolute_path, mimetype);
-
- guac_rdp_fs_close(rdp_stream->ls_status.fs, file_id);
-
- }
-
- /* Complete JSON and cleanup at end of directory */
- if (filename == NULL) {
-
- /* Complete JSON object */
- guac_common_json_end_object(user, stream,
- &rdp_stream->ls_status.json_state);
- guac_common_json_flush(user, stream,
- &rdp_stream->ls_status.json_state);
-
- /* Clean up resources */
- guac_rdp_fs_close(rdp_stream->ls_status.fs,
- rdp_stream->ls_status.file_id);
- free(rdp_stream);
-
- /* Signal of stream */
- guac_protocol_send_end(user->socket, stream);
- guac_user_free_stream(user, stream);
-
- }
-
- guac_socket_flush(user->socket);
- return 0;
-
-}
-
-int guac_rdp_download_get_handler(guac_user* user, guac_object* object,
- char* name) {
-
- guac_client* client = user->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
-
- /* Get filesystem, ignore request if no filesystem */
- guac_rdp_fs* fs = rdp_client->filesystem;
- if (fs == NULL)
- return 0;
-
- /* Attempt to open file for reading */
- int file_id = guac_rdp_fs_open(fs, name, ACCESS_GENERIC_READ, 0,
- DISP_FILE_OPEN, 0);
- if (file_id < 0) {
- guac_user_log(user, GUAC_LOG_INFO, "Unable to read file \"%s\"",
- name);
- return 0;
- }
-
- /* Get opened file */
- guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
- if (file == NULL) {
- guac_client_log(fs->client, GUAC_LOG_DEBUG,
- "%s: Successful open produced bad file_id: %i",
- __func__, file_id);
- return 0;
- }
-
- /* If directory, send contents of directory */
- if (file->attributes & FILE_ATTRIBUTE_DIRECTORY) {
-
- /* Create stream data */
- guac_rdp_stream* rdp_stream = malloc(sizeof(guac_rdp_stream));
- rdp_stream->type = GUAC_RDP_LS_STREAM;
- rdp_stream->ls_status.fs = fs;
- rdp_stream->ls_status.file_id = file_id;
- guac_strlcpy(rdp_stream->ls_status.directory_name, name,
- sizeof(rdp_stream->ls_status.directory_name));
-
- /* Allocate stream for body */
- guac_stream* stream = guac_user_alloc_stream(user);
- stream->ack_handler = guac_rdp_ls_ack_handler;
- stream->data = rdp_stream;
-
- /* Init JSON object state */
- guac_common_json_begin_object(user, stream,
- &rdp_stream->ls_status.json_state);
-
- /* Associate new stream with get request */
- guac_protocol_send_body(user->socket, object, stream,
- GUAC_USER_STREAM_INDEX_MIMETYPE, name);
-
- }
-
- /* Otherwise, send file contents */
- else {
-
- /* Create stream data */
- guac_rdp_stream* rdp_stream = malloc(sizeof(guac_rdp_stream));
- rdp_stream->type = GUAC_RDP_DOWNLOAD_STREAM;
- rdp_stream->download_status.file_id = file_id;
- rdp_stream->download_status.offset = 0;
-
- /* Allocate stream for body */
- guac_stream* stream = guac_user_alloc_stream(user);
- stream->data = rdp_stream;
- stream->ack_handler = guac_rdp_download_ack_handler;
-
- /* Associate new stream with get request */
- guac_protocol_send_body(user->socket, object, stream,
- "application/octet-stream", name);
-
- }
-
- guac_socket_flush(user->socket);
- return 0;
-}
-
-int guac_rdp_upload_put_handler(guac_user* user, guac_object* object,
- guac_stream* stream, char* mimetype, char* name) {
-
- guac_client* client = user->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
-
- /* Get filesystem, return error if no filesystem */
- guac_rdp_fs* fs = rdp_client->filesystem;
- if (fs == NULL) {
- guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
- GUAC_PROTOCOL_STATUS_SERVER_ERROR);
- guac_socket_flush(user->socket);
- return 0;
- }
-
- /* Open file */
- int file_id = guac_rdp_fs_open(fs, name, ACCESS_GENERIC_WRITE, 0,
- DISP_FILE_OVERWRITE_IF, 0);
-
- /* Abort on failure */
- if (file_id < 0) {
- guac_protocol_send_ack(user->socket, stream, "FAIL (CANNOT OPEN)",
- GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
- guac_socket_flush(user->socket);
- return 0;
- }
-
- /* Init upload stream data */
- guac_rdp_stream* rdp_stream = malloc(sizeof(guac_rdp_stream));
- rdp_stream->type = GUAC_RDP_UPLOAD_STREAM;
- rdp_stream->upload_status.offset = 0;
- rdp_stream->upload_status.file_id = file_id;
-
- /* Allocate stream, init for file upload */
- stream->data = rdp_stream;
- stream->blob_handler = guac_rdp_upload_blob_handler;
- stream->end_handler = guac_rdp_upload_end_handler;
-
- /* Acknowledge stream creation */
- guac_protocol_send_ack(user->socket, stream, "OK (STREAM BEGIN)",
- GUAC_PROTOCOL_STATUS_SUCCESS);
- guac_socket_flush(user->socket);
- return 0;
-}
-
diff --git a/src/protocols/rdp/rdp_stream.h b/src/protocols/rdp/rdp_stream.h
deleted file mode 100644
index deec4f9..0000000
--- a/src/protocols/rdp/rdp_stream.h
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#ifndef _GUAC_RDP_STREAM_H
-#define _GUAC_RDP_STREAM_H
-
-#include "config.h"
-#include "common/json.h"
-#include "rdp_svc.h"
-
-#include <guacamole/user.h>
-#include <guacamole/protocol.h>
-#include <guacamole/stream.h>
-
-#include <stdint.h>
-
-/**
- * The transfer status of a file being downloaded.
- */
-typedef struct guac_rdp_download_status {
-
- /**
- * The file ID of the file being downloaded.
- */
- int file_id;
-
- /**
- * The current position within the file.
- */
- uint64_t offset;
-
-} guac_rdp_download_status;
-
-/**
- * Structure which represents the current state of an upload.
- */
-typedef struct guac_rdp_upload_status {
-
- /**
- * The overall offset within the file that the next write should
- * occur at.
- */
- uint64_t offset;
-
- /**
- * The ID of the file being written to.
- */
- int file_id;
-
-} guac_rdp_upload_status;
-
-/**
- * The current state of a directory listing operation.
- */
-typedef struct guac_rdp_ls_status {
-
- /**
- * The filesystem associated with the directory being listed.
- */
- guac_rdp_fs* fs;
-
- /**
- * The file ID of the directory being listed.
- */
- int file_id;
-
- /**
- * The absolute path of the directory being listed.
- */
- char directory_name[GUAC_RDP_FS_MAX_PATH];
-
- /**
- * The current state of the JSON directory object being written.
- */
- guac_common_json_state json_state;
-
-} guac_rdp_ls_status;
-
-/**
- * All available stream types.
- */
-typedef enum guac_rdp_stream_type {
-
- /**
- * An in-progress file upload.
- */
- GUAC_RDP_UPLOAD_STREAM,
-
- /**
- * An in-progress file download.
- */
- GUAC_RDP_DOWNLOAD_STREAM,
-
- /**
- * An in-progress stream of a directory listing.
- */
- GUAC_RDP_LS_STREAM,
-
- /**
- * The inbound half of a static virtual channel.
- */
- GUAC_RDP_INBOUND_SVC_STREAM,
-
- /**
- * An inbound stream of clipboard data.
- */
- GUAC_RDP_INBOUND_CLIPBOARD_STREAM
-
-} guac_rdp_stream_type;
-
-/**
- * Variable-typed stream data.
- */
-typedef struct guac_rdp_stream {
-
- /**
- * The type of this stream.
- */
- guac_rdp_stream_type type;
-
- /**
- * The file upload status. Only valid for GUAC_RDP_UPLOAD_STREAM.
- */
- guac_rdp_upload_status upload_status;
-
- /**
- * The file upload status. Only valid for GUAC_RDP_DOWNLOAD_STREAM.
- */
- guac_rdp_download_status download_status;
-
- /**
- * The directory list status. Only valid for GUAC_RDP_LS_STREAM.
- */
- guac_rdp_ls_status ls_status;
-
- /**
- * Associated SVC instance. Only valid for GUAC_RDP_INBOUND_SVC_STREAM.
- */
- guac_rdp_svc* svc;
-
-} guac_rdp_stream;
-
-/**
- * Handler for inbound files related to file uploads.
- */
-guac_user_file_handler guac_rdp_upload_file_handler;
-
-/**
- * Handler for inbound pipes related to static virtual channels.
- */
-guac_user_pipe_handler guac_rdp_svc_pipe_handler;
-
-/**
- * Handler for inbound clipboard data.
- */
-guac_user_clipboard_handler guac_rdp_clipboard_handler;
-
-/**
- * Handler for stream data related to file uploads.
- */
-guac_user_blob_handler guac_rdp_upload_blob_handler;
-
-/**
- * Handler for stream data related to static virtual channels.
- */
-guac_user_blob_handler guac_rdp_svc_blob_handler;
-
-/**
- * Handler for stream data related to clipboard.
- */
-guac_user_blob_handler guac_rdp_clipboard_blob_handler;
-
-/**
- * Handler for end-of-stream related to file uploads.
- */
-guac_user_end_handler guac_rdp_upload_end_handler;
-
-/**
- * Handler for end-of-stream related to clipboard.
- */
-guac_user_end_handler guac_rdp_clipboard_end_handler;
-
-/**
- * Handler for acknowledgements of receipt of data related to file downloads.
- */
-guac_user_ack_handler guac_rdp_download_ack_handler;
-
-/**
- * Handler for ack messages received due to receipt of a "body" or "blob"
- * instruction associated with a directory list operation.
- */
-guac_user_ack_handler guac_rdp_ls_ack_handler;
-
-/**
- * Handler for get messages. In context of downloads and the filesystem exposed
- * via the Guacamole protocol, get messages request the body of a file within
- * the filesystem.
- */
-guac_user_get_handler guac_rdp_download_get_handler;
-
-/**
- * Handler for put messages. In context of uploads and the filesystem exposed
- * via the Guacamole protocol, put messages request write access to a file
- * within the filesystem.
- */
-guac_user_put_handler guac_rdp_upload_put_handler;
-
-#endif
-
diff --git a/src/protocols/rdp/rdp_svc.c b/src/protocols/rdp/rdp_svc.c
deleted file mode 100644
index 0a6eb24..0000000
--- a/src/protocols/rdp/rdp_svc.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "config.h"
-#include "client.h"
-#include "common/list.h"
-#include "rdp.h"
-#include "rdp_svc.h"
-
-#include <freerdp/utils/svc_plugin.h>
-#include <guacamole/client.h>
-#include <guacamole/string.h>
-
-#ifdef ENABLE_WINPR
-#include <winpr/stream.h>
-#else
-#include "compat/winpr-stream.h"
-#endif
-
-#include <stdlib.h>
-
-guac_rdp_svc* guac_rdp_alloc_svc(guac_client* client, char* name) {
-
- guac_rdp_svc* svc = malloc(sizeof(guac_rdp_svc));
-
- /* Init SVC */
- svc->client = client;
- svc->plugin = NULL;
- svc->output_pipe = NULL;
-
- /* Init name */
- int name_length = guac_strlcpy(svc->name, name, GUAC_RDP_SVC_MAX_LENGTH);
-
- /* Warn about name length */
- if (name_length >= GUAC_RDP_SVC_MAX_LENGTH)
- guac_client_log(client, GUAC_LOG_INFO,
- "Static channel name \"%s\" exceeds maximum of %i characters "
- "and will be truncated", name, GUAC_RDP_SVC_MAX_LENGTH - 1);
-
- return svc;
-}
-
-void guac_rdp_free_svc(guac_rdp_svc* svc) {
- free(svc);
-}
-
-void guac_rdp_svc_send_pipe(guac_socket* socket, guac_rdp_svc* svc) {
-
- /* Send pipe instruction for the SVC's output stream */
- guac_protocol_send_pipe(socket, svc->output_pipe,
- "application/octet-stream", svc->name);
-
-}
-
-void guac_rdp_svc_send_pipes(guac_user* user) {
-
- guac_client* client = user->client;
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
-
- guac_common_list_lock(rdp_client->available_svc);
-
- /* Send pipe for each allocated SVC's output stream */
- guac_common_list_element* current = rdp_client->available_svc->head;
- while (current != NULL) {
- guac_rdp_svc_send_pipe(user->socket, (guac_rdp_svc*) current->data);
- current = current->next;
- }
-
- guac_common_list_unlock(rdp_client->available_svc);
-
-}
-
-void guac_rdp_add_svc(guac_client* client, guac_rdp_svc* svc) {
-
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
-
- /* Add to list of available SVC */
- guac_common_list_lock(rdp_client->available_svc);
- guac_common_list_add(rdp_client->available_svc, svc);
- guac_common_list_unlock(rdp_client->available_svc);
-
-}
-
-guac_rdp_svc* guac_rdp_get_svc(guac_client* client, const char* name) {
-
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- guac_common_list_element* current;
- guac_rdp_svc* found = NULL;
-
- /* For each available SVC */
- guac_common_list_lock(rdp_client->available_svc);
- current = rdp_client->available_svc->head;
- while (current != NULL) {
-
- /* If name matches, found */
- guac_rdp_svc* current_svc = (guac_rdp_svc*) current->data;
- if (strcmp(current_svc->name, name) == 0) {
- found = current_svc;
- break;
- }
-
- current = current->next;
-
- }
- guac_common_list_unlock(rdp_client->available_svc);
-
- return found;
-
-}
-
-guac_rdp_svc* guac_rdp_remove_svc(guac_client* client, const char* name) {
-
- guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
- guac_common_list_element* current;
- guac_rdp_svc* found = NULL;
-
- /* For each available SVC */
- guac_common_list_lock(rdp_client->available_svc);
- current = rdp_client->available_svc->head;
- while (current != NULL) {
-
- /* If name matches, remove entry */
- guac_rdp_svc* current_svc = (guac_rdp_svc*) current->data;
- if (strcmp(current_svc->name, name) == 0) {
- guac_common_list_remove(rdp_client->available_svc, current);
- found = current_svc;
- break;
- }
-
- current = current->next;
-
- }
- guac_common_list_unlock(rdp_client->available_svc);
-
- /* Return removed entry, if any */
- return found;
-
-}
-
-void guac_rdp_svc_write(guac_rdp_svc* svc, void* data, int length) {
-
- wStream* output_stream;
-
- /* Do not write of plugin not associated */
- if (svc->plugin == NULL) {
- guac_client_log(svc->client, GUAC_LOG_ERROR,
- "Channel \"%s\" output dropped.",
- svc->name);
- return;
- }
-
- /* Build packet */
- output_stream = Stream_New(NULL, length);
- Stream_Write(output_stream, data, length);
-
- /* Send packet */
- svc_plugin_send(svc->plugin, output_stream);
-
-}
-
diff --git a/src/protocols/rdp/rdp_svc.h b/src/protocols/rdp/rdp_svc.h
deleted file mode 100644
index ebb3a13..0000000
--- a/src/protocols/rdp/rdp_svc.h
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#ifndef __GUAC_RDP_RDP_SVC_H
-#define __GUAC_RDP_RDP_SVC_H
-
-#include "config.h"
-
-#include <freerdp/utils/svc_plugin.h>
-#include <guacamole/client.h>
-#include <guacamole/stream.h>
-
-/**
- * The maximum number of bytes to allow within each channel name, including
- * null terminator.
- */
-#define GUAC_RDP_SVC_MAX_LENGTH 8
-
-/**
- * Structure describing a static virtual channel, and the corresponding
- * Guacamole pipes.
- */
-typedef struct guac_rdp_svc {
-
- /**
- * Reference to the client owning this static channel.
- */
- guac_client* client;
-
- /**
- * Reference to associated SVC plugin.
- */
- rdpSvcPlugin* plugin;
-
- /**
- * The name of the RDP channel in use, and the name to use for each pipe.
- */
- char name[GUAC_RDP_SVC_MAX_LENGTH];
-
- /**
- * The output pipe, opened when the RDP server receives a connection to
- * the static channel.
- */
- guac_stream* output_pipe;
-
-} guac_rdp_svc;
-
-/**
- * Allocate a new SVC with the given name.
- *
- * @param client
- * The guac_client associated with the current RDP session.
- *
- * @param name
- * The name of the virtual channel to allocate.
- *
- * @return
- * A newly-allocated static virtual channel.
- */
-guac_rdp_svc* guac_rdp_alloc_svc(guac_client* client, char* name);
-
-/**
- * Free the given SVC.
- *
- * @param svc
- * The static virtual channel to free.
- */
-void guac_rdp_free_svc(guac_rdp_svc* svc);
-
-/**
- * Sends the "pipe" instruction describing the given static virtual channel
- * along the given socket. This pipe instruction will relate the SVC's
- * underlying output stream with the SVC's name and the mimetype
- * "application/octet-stream".
- *
- * @param socket
- * The socket along which the "pipe" instruction should be sent.
- *
- * @param svc
- * The static virtual channel that the "pipe" instruction should describe.
- */
-void guac_rdp_svc_send_pipe(guac_socket* socket, guac_rdp_svc* svc);
-
-/**
- * Sends the "pipe" instructions describing all static virtual channels
- * available to the given user along that user's socket. Each pipe instruction
- * will relate the associated SVC's underlying output stream with the SVC's
- * name and the mimetype "application/octet-stream".
- *
- * @param user
- * The user to send the "pipe" instructions to.
- */
-void guac_rdp_svc_send_pipes(guac_user* user);
-
-/**
- * Add the given SVC to the list of all available SVCs.
- *
- * @param client
- * The guac_client associated with the current RDP session.
- *
- * @param svc
- * The static virtual channel to add to the list of all such channels
- * available.
- */
-void guac_rdp_add_svc(guac_client* client, guac_rdp_svc* svc);
-
-/**
- * Retrieve the SVC with the given name from the list stored in the client.
- *
- * @param client
- * The guac_client associated with the current RDP session.
- *
- * @param name
- * The name of the static virtual channel to retrieve.
- *
- * @return
- * The static virtual channel with the given name, or NULL if no such
- * virtual channel exists.
- */
-guac_rdp_svc* guac_rdp_get_svc(guac_client* client, const char* name);
-
-/**
- * Remove the SVC with the given name from the list stored in the client.
- *
- * @param client
- * The guac_client associated with the current RDP session.
- *
- * @param name
- * The name of the static virtual channel to remove.
- *
- * @return
- * The static virtual channel that was removed, or NULL if no such virtual
- * channel exists.
- */
-guac_rdp_svc* guac_rdp_remove_svc(guac_client* client, const char* name);
-
-/**
- * Write the given blob of data to the virtual channel.
- *
- * @param svc
- * The static virtual channel to write data to.
- *
- * @param data
- * The data to write.
- *
- * @param length
- * The number of bytes to write.
- */
-void guac_rdp_svc_write(guac_rdp_svc* svc, void* data, int length);
-
-#endif
-
diff --git a/src/protocols/rdp/rdp_settings.c b/src/protocols/rdp/settings.c
similarity index 82%
rename from src/protocols/rdp/rdp_settings.c
rename to src/protocols/rdp/settings.c
index 11cc861..fe2cf67 100644
--- a/src/protocols/rdp/rdp_settings.c
+++ b/src/protocols/rdp/settings.c
@@ -17,27 +17,23 @@
* under the License.
*/
-#include "config.h"
-
-#include "client.h"
#include "common/string.h"
-#include "rdp.h"
-#include "rdp_settings.h"
+#include "config.h"
#include "resolution.h"
+#include "settings.h"
#include <freerdp/constants.h>
#include <freerdp/settings.h>
+#include <freerdp/freerdp.h>
+#include <guacamole/client.h>
#include <guacamole/string.h>
#include <guacamole/user.h>
-
-#ifdef ENABLE_WINPR
+#include <winpr/crt.h>
#include <winpr/wtypes.h>
-#else
-#include "compat/winpr-wtypes.h"
-#endif
#include <errno.h>
#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
/* Client plugin arguments */
@@ -107,17 +103,13 @@
"enable-audio-input",
"read-only",
-#ifdef HAVE_FREERDP_GATEWAY_SUPPORT
"gateway-hostname",
"gateway-port",
"gateway-domain",
"gateway-username",
"gateway-password",
-#endif
-#ifdef HAVE_FREERDP_LOAD_BALANCER_SUPPORT
"load-balance-info",
-#endif
"disable-copy",
"disable-paste",
@@ -243,7 +235,8 @@
/**
* The type of security to use for the connection. Valid values are "rdp",
- * "tls", "nla", or "any". By default, "rdp" security is used.
+ * "tls", "nla", "nla-ext", or "any". By default, the security mode is
+ * negotiated ("any").
*/
IDX_SECURITY,
@@ -318,7 +311,7 @@
/**
* "true" if desktop composition (Aero) should be enabled during the
- * session, "false" or blank otherwise. As desktop composition provides
+ * session, "false" or blank otherwise. As desktop composition provides
* alpha blending and other special effects, this increases the amount of
* bandwidth used.
*/
@@ -363,7 +356,7 @@
/**
* The timezone to pass through to the RDP connection, in IANA format, which
- * will be translated into Windows formats. See the following page for
+ * will be translated into Windows formats. See the following page for
* information and list of valid values:
* https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
*/
@@ -383,7 +376,7 @@
IDX_SFTP_HOSTNAME,
/**
- * The public SSH host key of the SFTP server. Optional.
+ * The public SSH host key of the SFTP server. Optional.
*/
IDX_SFTP_HOST_KEY,
@@ -432,7 +425,7 @@
/**
* The interval at which SSH keepalive messages are sent to the server for
- * SFTP connections. The default is 0 (disabling keepalives), and a value
+ * SFTP connections. The default is 0 (disabling keepalives), and a value
* of 1 is automatically increased to 2 by libssh2 to avoid busy loop corner
* cases.
*/
@@ -470,7 +463,7 @@
/**
* Whether keys pressed and released should be included in the session
* recording. Key events are NOT included by default within the recording,
- * as doing so has privacy and security implications. Including key events
+ * as doing so has privacy and security implications. Including key events
* may be necessary in certain auditing contexts, but should only be done
* with caution. Key events can easily contain sensitive information, such
* as passwords, credit card numbers, etc.
@@ -501,7 +494,6 @@
*/
IDX_READ_ONLY,
-#ifdef HAVE_FREERDP_GATEWAY_SUPPORT
/**
* The hostname of the remote desktop gateway that should be used as an
* intermediary for the remote desktop connection. If omitted, a gateway
@@ -538,15 +530,12 @@
* gateway, if a gateway is being used.
*/
IDX_GATEWAY_PASSWORD,
-#endif
-#ifdef HAVE_FREERDP_LOAD_BALANCER_SUPPORT
/**
* The load balancing information/cookie which should be provided to
* the connection broker, if a connection broker is being used.
*/
IDX_LOAD_BALANCE_INFO,
-#endif
/**
* Whether outbound clipboard access should be blocked. If set to "true",
@@ -604,6 +593,12 @@
settings->security_mode = GUAC_SECURITY_NLA;
}
+ /* Extended NLA security */
+ else if (strcmp(argv[IDX_SECURITY], "nla-ext") == 0) {
+ guac_user_log(user, GUAC_LOG_INFO, "Security mode: Extended NLA");
+ settings->security_mode = GUAC_SECURITY_EXTENDED_NLA;
+ }
+
/* TLS security */
else if (strcmp(argv[IDX_SECURITY], "tls") == 0) {
guac_user_log(user, GUAC_LOG_INFO, "Security mode: TLS");
@@ -616,16 +611,16 @@
settings->security_mode = GUAC_SECURITY_RDP;
}
- /* ANY security (allow server to choose) */
+ /* Negotiate security (allow server to choose) */
else if (strcmp(argv[IDX_SECURITY], "any") == 0) {
- guac_user_log(user, GUAC_LOG_INFO, "Security mode: ANY");
+ guac_user_log(user, GUAC_LOG_INFO, "Security mode: Negotiate (ANY)");
settings->security_mode = GUAC_SECURITY_ANY;
}
/* If nothing given, default to RDP */
else {
- guac_user_log(user, GUAC_LOG_INFO, "No security mode specified. Defaulting to RDP.");
- settings->security_mode = GUAC_SECURITY_RDP;
+ guac_user_log(user, GUAC_LOG_INFO, "No security mode specified. Defaulting to security mode negotiation with server.");
+ settings->security_mode = GUAC_SECURITY_ANY;
}
/* Set hostname */
@@ -813,7 +808,6 @@
"Preconnection BLOB: \"%s\"", settings->preconnection_blob);
}
-#ifndef HAVE_RDPSETTINGS_SENDPRECONNECTIONPDU
/* Warn if support for the preconnection BLOB / ID is absent */
if (settings->preconnection_blob != NULL
|| settings->preconnection_id != -1) {
@@ -822,7 +816,6 @@
"preconnection PDU. The specified preconnection BLOB and/or "
"ID will be ignored.");
}
-#endif
/* Audio enable/disable */
settings->audio_enabled =
@@ -990,7 +983,6 @@
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_ENABLE_AUDIO_INPUT, 0);
-#ifdef HAVE_FREERDP_GATEWAY_SUPPORT
/* Set gateway hostname */
settings->gateway_hostname =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
@@ -1015,14 +1007,11 @@
settings->gateway_password =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_GATEWAY_PASSWORD, NULL);
-#endif
-#ifdef HAVE_FREERDP_LOAD_BALANCER_SUPPORT
/* Set load balance info */
settings->load_balance_info =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_LOAD_BALANCE_INFO, NULL);
-#endif
/* Parse clipboard copy disable flag */
settings->disable_copy =
@@ -1087,18 +1076,14 @@
free(settings->sftp_username);
#endif
-#ifdef HAVE_FREERDP_GATEWAY_SUPPORT
/* Free RD gateway information */
free(settings->gateway_hostname);
free(settings->gateway_domain);
free(settings->gateway_username);
free(settings->gateway_password);
-#endif
-#ifdef HAVE_FREERDP_LOAD_BALANCER_SUPPORT
/* Free load balancer information string */
free(settings->load_balance_info);
-#endif
/* Free settings structure */
free(settings);
@@ -1106,27 +1091,15 @@
}
int guac_rdp_get_width(freerdp* rdp) {
-#ifdef LEGACY_RDPSETTINGS
- return rdp->settings->width;
-#else
return rdp->settings->DesktopWidth;
-#endif
}
int guac_rdp_get_height(freerdp* rdp) {
-#ifdef LEGACY_RDPSETTINGS
- return rdp->settings->height;
-#else
return rdp->settings->DesktopHeight;
-#endif
}
int guac_rdp_get_depth(freerdp* rdp) {
-#ifdef LEGACY_RDPSETTINGS
- return rdp->settings->color_depth;
-#else
return rdp->settings->ColorDepth;
-#endif
}
/**
@@ -1199,60 +1172,25 @@
void guac_rdp_push_settings(guac_client* client,
guac_rdp_settings* guac_settings, freerdp* rdp) {
- BOOL bitmap_cache = !guac_settings->disable_bitmap_caching;
rdpSettings* rdp_settings = rdp->settings;
/* Authentication */
-#ifdef LEGACY_RDPSETTINGS
- rdp_settings->domain = guac_rdp_strdup(guac_settings->domain);
- rdp_settings->username = guac_rdp_strdup(guac_settings->username);
- rdp_settings->password = guac_rdp_strdup(guac_settings->password);
-#else
rdp_settings->Domain = guac_rdp_strdup(guac_settings->domain);
rdp_settings->Username = guac_rdp_strdup(guac_settings->username);
rdp_settings->Password = guac_rdp_strdup(guac_settings->password);
-#endif
/* Connection */
-#ifdef LEGACY_RDPSETTINGS
- rdp_settings->hostname = guac_rdp_strdup(guac_settings->hostname);
- rdp_settings->port = guac_settings->port;
-#else
rdp_settings->ServerHostname = guac_rdp_strdup(guac_settings->hostname);
rdp_settings->ServerPort = guac_settings->port;
-#endif
/* Session */
-#ifdef LEGACY_RDPSETTINGS
- rdp_settings->color_depth = guac_settings->color_depth;
- rdp_settings->width = guac_settings->width;
- rdp_settings->height = guac_settings->height;
- rdp_settings->shell = guac_rdp_strdup(guac_settings->initial_program);
- rdp_settings->kbd_layout = guac_settings->server_layout->freerdp_keyboard_layout;
-#else
rdp_settings->ColorDepth = guac_settings->color_depth;
rdp_settings->DesktopWidth = guac_settings->width;
rdp_settings->DesktopHeight = guac_settings->height;
rdp_settings->AlternateShell = guac_rdp_strdup(guac_settings->initial_program);
rdp_settings->KeyboardLayout = guac_settings->server_layout->freerdp_keyboard_layout;
-#endif
/* Performance flags */
-#ifdef LEGACY_RDPSETTINGS
-
- /* Explicitly set flag value */
- rdp_settings->performance_flags = guac_rdp_get_performance_flags(guac_settings);
-
- /* Set individual flags - some FreeRDP versions overwrite the above */
- rdp_settings->smooth_fonts = guac_settings->font_smoothing_enabled;
- rdp_settings->disable_wallpaper = !guac_settings->wallpaper_enabled;
- rdp_settings->disable_full_window_drag = !guac_settings->full_window_drag_enabled;
- rdp_settings->disable_menu_animations = !guac_settings->menu_animations_enabled;
- rdp_settings->disable_theming = !guac_settings->theming_enabled;
- rdp_settings->desktop_composition = guac_settings->desktop_composition_enabled;
-
-#else
-
/* Explicitly set flag value */
rdp_settings->PerformanceFlags = guac_rdp_get_performance_flags(guac_settings);
@@ -1264,49 +1202,25 @@
rdp_settings->DisableThemes = !guac_settings->theming_enabled;
rdp_settings->AllowDesktopComposition = guac_settings->desktop_composition_enabled;
-#endif
-
/* Client name */
if (guac_settings->client_name != NULL) {
-#ifdef LEGACY_RDPSETTINGS
- guac_strlcpy(rdp_settings->client_hostname, guac_settings->client_name,
- RDP_CLIENT_HOSTNAME_SIZE);
-#else
guac_strlcpy(rdp_settings->ClientHostname, guac_settings->client_name,
RDP_CLIENT_HOSTNAME_SIZE);
-#endif
}
/* Console */
-#ifdef LEGACY_RDPSETTINGS
- rdp_settings->console_session = guac_settings->console;
- rdp_settings->console_audio = guac_settings->console_audio;
-#else
rdp_settings->ConsoleSession = guac_settings->console;
rdp_settings->RemoteConsoleAudio = guac_settings->console_audio;
-#endif
/* Audio */
-#ifdef LEGACY_RDPSETTINGS
-#ifdef HAVE_RDPSETTINGS_AUDIOPLAYBACK
- rdp_settings->audio_playback = guac_settings->audio_enabled;
-#endif
-#else
-#ifdef HAVE_RDPSETTINGS_AUDIOPLAYBACK
rdp_settings->AudioPlayback = guac_settings->audio_enabled;
-#endif
-#endif
/* Audio capture */
-#ifdef LEGACY_RDPSETTINGS
-#ifdef HAVE_RDPSETTINGS_AUDIOCAPTURE
- rdp_settings->audio_capture = guac_settings->enable_audio_input;
-#endif
-#else
-#ifdef HAVE_RDPSETTINGS_AUDIOCAPTURE
rdp_settings->AudioCapture = guac_settings->enable_audio_input;
-#endif
-#endif
+
+ /* Display Update channel */
+ rdp_settings->SupportDisplayControl =
+ (guac_settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE);
/* Timezone redirection */
if (guac_settings->timezone) {
@@ -1318,115 +1232,75 @@
}
/* Device redirection */
-#ifdef LEGACY_RDPSETTINGS
-#ifdef HAVE_RDPSETTINGS_DEVICEREDIRECTION
- rdp_settings->device_redirection = guac_settings->audio_enabled
- || guac_settings->drive_enabled
- || guac_settings->printing_enabled;
-#endif
-#else
-#ifdef HAVE_RDPSETTINGS_DEVICEREDIRECTION
rdp_settings->DeviceRedirection = guac_settings->audio_enabled
|| guac_settings->drive_enabled
|| guac_settings->printing_enabled;
-#endif
-#endif
/* Security */
switch (guac_settings->security_mode) {
- /* Standard RDP encryption */
+ /* Legacy RDP encryption */
case GUAC_SECURITY_RDP:
-#ifdef LEGACY_RDPSETTINGS
- rdp_settings->rdp_security = TRUE;
- rdp_settings->tls_security = FALSE;
- rdp_settings->nla_security = FALSE;
- rdp_settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE;
- rdp_settings->encryption_method =
- ENCRYPTION_METHOD_40BIT
- | ENCRYPTION_METHOD_128BIT
- | ENCRYPTION_METHOD_FIPS;
-#else
rdp_settings->RdpSecurity = TRUE;
rdp_settings->TlsSecurity = FALSE;
rdp_settings->NlaSecurity = FALSE;
+ rdp_settings->ExtSecurity = FALSE;
+ rdp_settings->UseRdpSecurityLayer = TRUE;
rdp_settings->EncryptionLevel = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE;
rdp_settings->EncryptionMethods =
ENCRYPTION_METHOD_40BIT
| ENCRYPTION_METHOD_128BIT
| ENCRYPTION_METHOD_FIPS;
-#endif
break;
/* TLS encryption */
case GUAC_SECURITY_TLS:
-#ifdef LEGACY_RDPSETTINGS
- rdp_settings->rdp_security = FALSE;
- rdp_settings->tls_security = TRUE;
- rdp_settings->nla_security = FALSE;
-#else
rdp_settings->RdpSecurity = FALSE;
rdp_settings->TlsSecurity = TRUE;
rdp_settings->NlaSecurity = FALSE;
-#endif
+ rdp_settings->ExtSecurity = FALSE;
break;
/* Network level authentication */
case GUAC_SECURITY_NLA:
-#ifdef LEGACY_RDPSETTINGS
- rdp_settings->rdp_security = FALSE;
- rdp_settings->tls_security = FALSE;
- rdp_settings->nla_security = TRUE;
-#else
rdp_settings->RdpSecurity = FALSE;
rdp_settings->TlsSecurity = FALSE;
rdp_settings->NlaSecurity = TRUE;
-#endif
+ rdp_settings->ExtSecurity = FALSE;
+ break;
+
+ /* Extended network level authentication */
+ case GUAC_SECURITY_EXTENDED_NLA:
+ rdp_settings->RdpSecurity = FALSE;
+ rdp_settings->TlsSecurity = FALSE;
+ rdp_settings->NlaSecurity = FALSE;
+ rdp_settings->ExtSecurity = TRUE;
break;
/* All security types */
case GUAC_SECURITY_ANY:
-#ifdef LEGACY_RDPSETTINGS
- rdp_settings->rdp_security = TRUE;
- rdp_settings->tls_security = TRUE;
- rdp_settings->nla_security = TRUE;
-#else
rdp_settings->RdpSecurity = TRUE;
rdp_settings->TlsSecurity = TRUE;
- rdp_settings->NlaSecurity = TRUE;
-#endif
+ rdp_settings->NlaSecurity = guac_settings->username && guac_settings->password;
+ rdp_settings->ExtSecurity = FALSE;
break;
}
/* Authentication */
-#ifdef LEGACY_RDPSETTINGS
- rdp_settings->authentication = !guac_settings->disable_authentication;
- rdp_settings->ignore_certificate = guac_settings->ignore_certificate;
- rdp_settings->encryption = TRUE;
-#else
rdp_settings->Authentication = !guac_settings->disable_authentication;
rdp_settings->IgnoreCertificate = guac_settings->ignore_certificate;
- rdp_settings->DisableEncryption = FALSE;
-#endif
/* RemoteApp */
if (guac_settings->remote_app != NULL) {
-#ifdef LEGACY_RDPSETTINGS
- rdp_settings->workarea = TRUE;
- rdp_settings->remote_app = TRUE;
- rdp_settings->rail_langbar_supported = TRUE;
-#else
rdp_settings->Workarea = TRUE;
rdp_settings->RemoteApplicationMode = TRUE;
rdp_settings->RemoteAppLanguageBarSupported = TRUE;
rdp_settings->RemoteApplicationProgram = guac_settings->remote_app;
rdp_settings->ShellWorkingDirectory = guac_rdp_strdup(guac_settings->remote_app_dir);
rdp_settings->RemoteApplicationCmdLine = guac_settings->remote_app_args;
-#endif
}
-#ifdef HAVE_RDPSETTINGS_SENDPRECONNECTIONPDU
/* Preconnection ID */
if (guac_settings->preconnection_id != -1) {
rdp_settings->NegotiateSecurityLayer = FALSE;
@@ -1440,9 +1314,7 @@
rdp_settings->SendPreconnectionPdu = TRUE;
rdp_settings->PreconnectionBlob = guac_settings->preconnection_blob;
}
-#endif
-#ifdef HAVE_FREERDP_GATEWAY_SUPPORT
/* Enable use of RD gateway if a gateway hostname is provided */
if (guac_settings->gateway_hostname != NULL) {
@@ -1460,80 +1332,29 @@
rdp_settings->GatewayPassword = guac_rdp_strdup(guac_settings->gateway_password);
}
-#endif
-#ifdef HAVE_FREERDP_LOAD_BALANCER_SUPPORT
/* Store load balance info (and calculate length) if provided */
if (guac_settings->load_balance_info != NULL) {
rdp_settings->LoadBalanceInfo = (BYTE*) guac_rdp_strdup(guac_settings->load_balance_info);
rdp_settings->LoadBalanceInfoLength = strlen(guac_settings->load_balance_info);
}
-#endif
- /* Order support */
-#ifdef LEGACY_RDPSETTINGS
- rdp_settings->bitmap_cache = bitmap_cache;
- rdp_settings->offscreen_bitmap_cache = !guac_settings->disable_offscreen_caching;
- rdp_settings->glyph_cache = !guac_settings->disable_glyph_caching;
- rdp_settings->os_major_type = OSMAJORTYPE_UNSPECIFIED;
- rdp_settings->os_minor_type = OSMINORTYPE_UNSPECIFIED;
- rdp_settings->desktop_resize = TRUE;
- rdp_settings->order_support[NEG_DSTBLT_INDEX] = TRUE;
- rdp_settings->order_support[NEG_PATBLT_INDEX] = FALSE; /* PATBLT not yet supported */
- rdp_settings->order_support[NEG_SCRBLT_INDEX] = TRUE;
- rdp_settings->order_support[NEG_OPAQUE_RECT_INDEX] = TRUE;
- rdp_settings->order_support[NEG_DRAWNINEGRID_INDEX] = FALSE;
- rdp_settings->order_support[NEG_MULTIDSTBLT_INDEX] = FALSE;
- rdp_settings->order_support[NEG_MULTIPATBLT_INDEX] = FALSE;
- rdp_settings->order_support[NEG_MULTISCRBLT_INDEX] = FALSE;
- rdp_settings->order_support[NEG_MULTIOPAQUERECT_INDEX] = FALSE;
- rdp_settings->order_support[NEG_MULTI_DRAWNINEGRID_INDEX] = FALSE;
- rdp_settings->order_support[NEG_LINETO_INDEX] = FALSE;
- rdp_settings->order_support[NEG_POLYLINE_INDEX] = FALSE;
- rdp_settings->order_support[NEG_MEMBLT_INDEX] = bitmap_cache;
- rdp_settings->order_support[NEG_MEM3BLT_INDEX] = FALSE;
- rdp_settings->order_support[NEG_MEMBLT_V2_INDEX] = bitmap_cache;
- rdp_settings->order_support[NEG_MEM3BLT_V2_INDEX] = FALSE;
- rdp_settings->order_support[NEG_SAVEBITMAP_INDEX] = FALSE;
- rdp_settings->order_support[NEG_GLYPH_INDEX_INDEX] = TRUE;
- rdp_settings->order_support[NEG_FAST_INDEX_INDEX] = TRUE;
- rdp_settings->order_support[NEG_FAST_GLYPH_INDEX] = TRUE;
- rdp_settings->order_support[NEG_POLYGON_SC_INDEX] = FALSE;
- rdp_settings->order_support[NEG_POLYGON_CB_INDEX] = FALSE;
- rdp_settings->order_support[NEG_ELLIPSE_SC_INDEX] = FALSE;
- rdp_settings->order_support[NEG_ELLIPSE_CB_INDEX] = FALSE;
-#else
- rdp_settings->BitmapCacheEnabled = bitmap_cache;
+ rdp_settings->BitmapCacheEnabled = !guac_settings->disable_bitmap_caching;
rdp_settings->OffscreenSupportLevel = !guac_settings->disable_offscreen_caching;
rdp_settings->GlyphSupportLevel = !guac_settings->disable_glyph_caching ? GLYPH_SUPPORT_FULL : GLYPH_SUPPORT_NONE;
rdp_settings->OsMajorType = OSMAJORTYPE_UNSPECIFIED;
rdp_settings->OsMinorType = OSMINORTYPE_UNSPECIFIED;
rdp_settings->DesktopResize = TRUE;
+
+ /* Claim support only for specific updates, independent of FreeRDP defaults */
+ ZeroMemory(rdp_settings->OrderSupport, GUAC_RDP_ORDER_SUPPORT_LENGTH);
rdp_settings->OrderSupport[NEG_DSTBLT_INDEX] = TRUE;
- rdp_settings->OrderSupport[NEG_PATBLT_INDEX] = FALSE; /* PATBLT not yet supported */
rdp_settings->OrderSupport[NEG_SCRBLT_INDEX] = TRUE;
- rdp_settings->OrderSupport[NEG_OPAQUE_RECT_INDEX] = TRUE;
- rdp_settings->OrderSupport[NEG_DRAWNINEGRID_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_MULTIDSTBLT_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_MULTIPATBLT_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_MULTISCRBLT_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_MULTIOPAQUERECT_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_MULTI_DRAWNINEGRID_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_LINETO_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_POLYLINE_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_MEMBLT_INDEX] = bitmap_cache;
- rdp_settings->OrderSupport[NEG_MEM3BLT_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_MEMBLT_V2_INDEX] = bitmap_cache;
- rdp_settings->OrderSupport[NEG_MEM3BLT_V2_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_SAVEBITMAP_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = TRUE;
- rdp_settings->OrderSupport[NEG_FAST_INDEX_INDEX] = TRUE;
- rdp_settings->OrderSupport[NEG_FAST_GLYPH_INDEX] = TRUE;
- rdp_settings->OrderSupport[NEG_POLYGON_SC_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_POLYGON_CB_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_ELLIPSE_SC_INDEX] = FALSE;
- rdp_settings->OrderSupport[NEG_ELLIPSE_CB_INDEX] = FALSE;
-#endif
+ rdp_settings->OrderSupport[NEG_MEMBLT_INDEX] = !guac_settings->disable_bitmap_caching;
+ rdp_settings->OrderSupport[NEG_MEMBLT_V2_INDEX] = !guac_settings->disable_bitmap_caching;
+ rdp_settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = !guac_settings->disable_glyph_caching;
+ rdp_settings->OrderSupport[NEG_FAST_INDEX_INDEX] = !guac_settings->disable_glyph_caching;
+ rdp_settings->OrderSupport[NEG_FAST_GLYPH_INDEX] = !guac_settings->disable_glyph_caching;
}
diff --git a/src/protocols/rdp/rdp_settings.h b/src/protocols/rdp/settings.h
similarity index 93%
rename from src/protocols/rdp/rdp_settings.h
rename to src/protocols/rdp/settings.h
index 9edbede..e4c579e 100644
--- a/src/protocols/rdp/rdp_settings.h
+++ b/src/protocols/rdp/settings.h
@@ -17,15 +17,15 @@
* under the License.
*/
-
-#ifndef __GUAC_RDP_SETTINGS_H
-#define __GUAC_RDP_SETTINGS_H
+#ifndef GUAC_RDP_SETTINGS_H
+#define GUAC_RDP_SETTINGS_H
#include "config.h"
-
-#include "rdp_keymap.h"
+#include "keymap.h"
#include <freerdp/freerdp.h>
+#include <guacamole/client.h>
+#include <guacamole/user.h>
/**
* The maximum number of bytes in the client hostname claimed during
@@ -59,12 +59,22 @@
#define GUAC_RDP_DEFAULT_RECORDING_NAME "recording"
/**
+ * The number of entries contained within the OrderSupport BYTE array
+ * referenced by the rdpSettings structure. This value is defined by the RDP
+ * negotiation process (there are 32 bytes available within the order
+ * negotiation field sent during the connection handshake) and is hard-coded
+ * within FreeRDP. There is no public constant for this value defined within
+ * the FreeRDP headers.
+ */
+#define GUAC_RDP_ORDER_SUPPORT_LENGTH 32
+
+/**
* All supported combinations of security types.
*/
typedef enum guac_rdp_security {
/**
- * Standard RDP encryption.
+ * Legacy RDP encryption.
*/
GUAC_SECURITY_RDP,
@@ -79,7 +89,12 @@
GUAC_SECURITY_NLA,
/**
- * Any method supported by the server.
+ * Extended network level authentication.
+ */
+ GUAC_SECURITY_EXTENDED_NLA,
+
+ /**
+ * Negotiate a security method supported by both server and client.
*/
GUAC_SECURITY_ANY
@@ -324,19 +339,19 @@
int menu_animations_enabled;
/**
- * Whether bitmap caching should be disabled. By default it is
+ * Whether bitmap caching should be disabled. By default it is
* enabled - this allows users to explicitly disable it.
*/
int disable_bitmap_caching;
/**
- * Whether offscreen caching should be disabled. By default it is
+ * Whether offscreen caching should be disabled. By default it is
* enabled - this allows users to explicitly disable it.
*/
int disable_offscreen_caching;
/**
- * Whether glyph caching should be disabled. By default it is enabled
+ * Whether glyph caching should be disabled. By default it is enabled
* - this allows users to explicitly disable it.
*/
int disable_glyph_caching;
@@ -420,7 +435,7 @@
/**
* The interval at which SSH keepalive messages are sent to the server for
- * SFTP connections. The default is 0 (disabling keepalives), and a value
+ * SFTP connections. The default is 0 (disabling keepalives), and a value
* of 1 is automatically increased to 2 by libssh2 to avoid busy loop corner
* cases.
*/
@@ -480,7 +495,6 @@
*/
int enable_audio_input;
-#ifdef HAVE_FREERDP_GATEWAY_SUPPORT
/**
* The hostname of the remote desktop gateway that should be used as an
* intermediary for the remote desktop connection. If no gateway should
@@ -515,15 +529,12 @@
* gateway, if a gateway is being used.
*/
char* gateway_password;
-#endif
-#ifdef HAVE_FREERDP_LOAD_BALANCER_SUPPORT
/**
* The load balancing information/cookie which should be provided to
* the connection broker, if a connection broker is being used.
*/
char* load_balance_info;
-#endif
} guac_rdp_settings;
diff --git a/src/protocols/rdp/sftp.c b/src/protocols/rdp/sftp.c
index ecfe35f..0fc2580 100644
--- a/src/protocols/rdp/sftp.c
+++ b/src/protocols/rdp/sftp.c
@@ -17,14 +17,11 @@
* under the License.
*/
-#include "config.h"
-
#include "common-ssh/sftp.h"
#include "rdp.h"
#include "sftp.h"
#include <guacamole/client.h>
-#include <guacamole/stream.h>
#include <guacamole/user.h>
int guac_rdp_sftp_file_handler(guac_user* user, guac_stream* stream,
diff --git a/src/protocols/rdp/sftp.h b/src/protocols/rdp/sftp.h
index d768324..3f7f9e2 100644
--- a/src/protocols/rdp/sftp.h
+++ b/src/protocols/rdp/sftp.h
@@ -20,8 +20,6 @@
#ifndef GUAC_RDP_SFTP_H
#define GUAC_RDP_SFTP_H
-#include "config.h"
-
#include <guacamole/stream.h>
#include <guacamole/user.h>
diff --git a/src/protocols/rdp/tests/Makefile.am b/src/protocols/rdp/tests/Makefile.am
index 3f57bbf..3313669 100644
--- a/src/protocols/rdp/tests/Makefile.am
+++ b/src/protocols/rdp/tests/Makefile.am
@@ -34,6 +34,7 @@
TESTS = $(check_PROGRAMS)
test_rdp_SOURCES = \
+ fs/basename.c \
fs/normalize_path.c
test_rdp_CFLAGS = \
diff --git a/src/protocols/rdp/tests/fs/basename.c b/src/protocols/rdp/tests/fs/basename.c
new file mode 100644
index 0000000..9ac6097
--- /dev/null
+++ b/src/protocols/rdp/tests/fs/basename.c
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "fs.h"
+
+#include <CUnit/CUnit.h>
+#include <stdlib.h>
+
+/**
+ * Test which verifies basenames are correctly extracted from Windows-style
+ * paths.
+ */
+void test_fs__basename_windows() {
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename("\\foo\\bar\\baz"), "baz")
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename("\\foo\\bar\\..\\baz\\"), "")
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename("bar\\..\\..\\baz\\a\\..\\b"), "b")
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename(".\\bar\\potato"), "potato")
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename("..\\..\\..\\..\\..\\..\\baz"), "baz")
+}
+
+/**
+ * Test which verifies basenames are correctly extracted from UNIX-style paths.
+ */
+void test_fs__basename_unix() {
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename("/foo/bar/baz"), "baz")
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename("/foo/bar/../baz/"), "")
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename("bar/../../baz/a/../b"), "b")
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename("./bar/potato"), "potato")
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename("../../../../../../baz"), "baz")
+}
+
+/**
+ * Test which verifies basenames are correctly extracted from paths consisting
+ * of mixed Windows and UNIX path separators.
+ */
+void test_fs__basename_mixed() {
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename("\\foo/bar\\baz"), "baz")
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename("/foo\\bar/..\\baz/"), "")
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename("bar\\../../baz\\a\\..\\b"), "b")
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename(".\\bar/potato"), "potato")
+ CU_ASSERT_STRING_EQUAL(guac_rdp_fs_basename("../..\\..\\..\\../..\\baz"), "baz")
+}
+
diff --git a/src/protocols/rdp/tests/fs/normalize_path.c b/src/protocols/rdp/tests/fs/normalize_path.c
index ccf23e0..22a2d80 100644
--- a/src/protocols/rdp/tests/fs/normalize_path.c
+++ b/src/protocols/rdp/tests/fs/normalize_path.c
@@ -17,7 +17,7 @@
* under the License.
*/
-#include "rdp_fs.h"
+#include "fs.h"
#include <CUnit/CUnit.h>
#include <stdlib.h>
diff --git a/src/protocols/rdp/unicode.c b/src/protocols/rdp/unicode.c
index 628615a..f055bea 100644
--- a/src/protocols/rdp/unicode.c
+++ b/src/protocols/rdp/unicode.c
@@ -17,12 +17,10 @@
* under the License.
*/
-#include "config.h"
+#include <guacamole/unicode.h>
#include <stdint.h>
-#include <guacamole/unicode.h>
-
void guac_rdp_utf16_to_utf8(const unsigned char* utf16, int length,
char* utf8, int size) {
diff --git a/src/protocols/rdp/upload.c b/src/protocols/rdp/upload.c
new file mode 100644
index 0000000..5317edb
--- /dev/null
+++ b/src/protocols/rdp/upload.c
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "fs.h"
+#include "rdp.h"
+#include "upload.h"
+
+#include <guacamole/client.h>
+#include <guacamole/object.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/stream.h>
+#include <guacamole/user.h>
+#include <winpr/nt.h>
+
+#include <stdlib.h>
+
+/**
+ * Writes the given filename to the given upload path, sanitizing the filename
+ * and translating the filename to the root directory.
+ *
+ * @param filename
+ * The filename to sanitize and move to the root directory.
+ *
+ * @param path
+ * A pointer to a buffer which should receive the sanitized path. The
+ * buffer must have at least GUAC_RDP_FS_MAX_PATH bytes available.
+ */
+static void __generate_upload_path(const char* filename, char* path) {
+
+ int i;
+
+ /* Add initial backslash */
+ *(path++) = '\\';
+
+ for (i=1; i<GUAC_RDP_FS_MAX_PATH; i++) {
+
+ /* Get current, stop at end */
+ char c = *(filename++);
+ if (c == '\0')
+ break;
+
+ /* Replace special characters with underscores */
+ if (c == '/' || c == '\\')
+ c = '_';
+
+ *(path++) = c;
+
+ }
+
+ /* Terminate path */
+ *path = '\0';
+
+}
+
+int guac_rdp_upload_file_handler(guac_user* user, guac_stream* stream,
+ char* mimetype, char* filename) {
+
+ guac_client* client = user->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ int file_id;
+ char file_path[GUAC_RDP_FS_MAX_PATH];
+
+ /* Get filesystem, return error if no filesystem */
+ guac_rdp_fs* fs = rdp_client->filesystem;
+ if (fs == NULL) {
+ guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
+ GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+ guac_socket_flush(user->socket);
+ return 0;
+ }
+
+ /* Translate name */
+ __generate_upload_path(filename, file_path);
+
+ /* Open file */
+ file_id = guac_rdp_fs_open(fs, file_path, GENERIC_WRITE, 0,
+ FILE_OVERWRITE_IF, 0);
+ if (file_id < 0) {
+ guac_protocol_send_ack(user->socket, stream, "FAIL (CANNOT OPEN)",
+ GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
+ guac_socket_flush(user->socket);
+ return 0;
+ }
+
+ /* Init upload status */
+ guac_rdp_upload_status* upload_status = malloc(sizeof(guac_rdp_upload_status));
+ upload_status->offset = 0;
+ upload_status->file_id = file_id;
+ stream->data = upload_status;
+ stream->blob_handler = guac_rdp_upload_blob_handler;
+ stream->end_handler = guac_rdp_upload_end_handler;
+
+ guac_protocol_send_ack(user->socket, stream, "OK (STREAM BEGIN)",
+ GUAC_PROTOCOL_STATUS_SUCCESS);
+ guac_socket_flush(user->socket);
+ return 0;
+
+}
+
+int guac_rdp_upload_blob_handler(guac_user* user, guac_stream* stream,
+ void* data, int length) {
+
+ int bytes_written;
+ guac_rdp_upload_status* upload_status = (guac_rdp_upload_status*) stream->data;
+
+ /* Get filesystem, return error if no filesystem */
+ guac_client* client = user->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_rdp_fs* fs = rdp_client->filesystem;
+ if (fs == NULL) {
+ guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
+ GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+ guac_socket_flush(user->socket);
+ return 0;
+ }
+
+ /* Write entire block */
+ while (length > 0) {
+
+ /* Attempt write */
+ bytes_written = guac_rdp_fs_write(fs, upload_status->file_id,
+ upload_status->offset, data, length);
+
+ /* On error, abort */
+ if (bytes_written < 0) {
+ guac_protocol_send_ack(user->socket, stream,
+ "FAIL (BAD WRITE)",
+ GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
+ guac_socket_flush(user->socket);
+ return 0;
+ }
+
+ /* Update counters */
+ upload_status->offset += bytes_written;
+ data += bytes_written;
+ length -= bytes_written;
+
+ }
+
+ guac_protocol_send_ack(user->socket, stream, "OK (DATA RECEIVED)",
+ GUAC_PROTOCOL_STATUS_SUCCESS);
+ guac_socket_flush(user->socket);
+ return 0;
+
+}
+
+int guac_rdp_upload_end_handler(guac_user* user, guac_stream* stream) {
+
+ guac_client* client = user->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_rdp_upload_status* upload_status = (guac_rdp_upload_status*) stream->data;
+
+ /* Get filesystem, return error if no filesystem */
+ guac_rdp_fs* fs = rdp_client->filesystem;
+ if (fs == NULL) {
+ guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
+ GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+ guac_socket_flush(user->socket);
+ return 0;
+ }
+
+ /* Close file */
+ guac_rdp_fs_close(fs, upload_status->file_id);
+
+ /* Acknowledge stream end */
+ guac_protocol_send_ack(user->socket, stream, "OK (STREAM END)",
+ GUAC_PROTOCOL_STATUS_SUCCESS);
+ guac_socket_flush(user->socket);
+
+ free(upload_status);
+ return 0;
+
+}
+
+int guac_rdp_upload_put_handler(guac_user* user, guac_object* object,
+ guac_stream* stream, char* mimetype, char* name) {
+
+ guac_client* client = user->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
+ /* Get filesystem, return error if no filesystem */
+ guac_rdp_fs* fs = rdp_client->filesystem;
+ if (fs == NULL) {
+ guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
+ GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+ guac_socket_flush(user->socket);
+ return 0;
+ }
+
+ /* Open file */
+ int file_id = guac_rdp_fs_open(fs, name, GENERIC_WRITE, 0,
+ FILE_OVERWRITE_IF, 0);
+
+ /* Abort on failure */
+ if (file_id < 0) {
+ guac_protocol_send_ack(user->socket, stream, "FAIL (CANNOT OPEN)",
+ GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
+ guac_socket_flush(user->socket);
+ return 0;
+ }
+
+ /* Init upload stream data */
+ guac_rdp_upload_status* upload_status = malloc(sizeof(guac_rdp_upload_status));
+ upload_status->offset = 0;
+ upload_status->file_id = file_id;
+
+ /* Allocate stream, init for file upload */
+ stream->data = upload_status;
+ stream->blob_handler = guac_rdp_upload_blob_handler;
+ stream->end_handler = guac_rdp_upload_end_handler;
+
+ /* Acknowledge stream creation */
+ guac_protocol_send_ack(user->socket, stream, "OK (STREAM BEGIN)",
+ GUAC_PROTOCOL_STATUS_SUCCESS);
+ guac_socket_flush(user->socket);
+ return 0;
+}
+
diff --git a/src/protocols/rdp/upload.h b/src/protocols/rdp/upload.h
new file mode 100644
index 0000000..254538f
--- /dev/null
+++ b/src/protocols/rdp/upload.h
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_UPLOAD_H
+#define GUAC_RDP_UPLOAD_H
+
+#include "common/json.h"
+
+#include <guacamole/protocol.h>
+#include <guacamole/stream.h>
+#include <guacamole/user.h>
+
+#include <stdint.h>
+
+/**
+ * Structure which represents the current state of an upload.
+ */
+typedef struct guac_rdp_upload_status {
+
+ /**
+ * The overall offset within the file that the next write should
+ * occur at.
+ */
+ int offset;
+
+ /**
+ * The ID of the file being written to.
+ */
+ int file_id;
+
+} guac_rdp_upload_status;
+
+/**
+ * Handler for inbound files related to file uploads.
+ */
+guac_user_file_handler guac_rdp_upload_file_handler;
+
+/**
+ * Handler for stream data related to file uploads.
+ */
+guac_user_blob_handler guac_rdp_upload_blob_handler;
+
+/**
+ * Handler for end-of-stream related to file uploads.
+ */
+guac_user_end_handler guac_rdp_upload_end_handler;
+
+/**
+ * Handler for put messages. In context of uploads and the filesystem exposed
+ * via the Guacamole protocol, put messages request write access to a file
+ * within the filesystem.
+ */
+guac_user_put_handler guac_rdp_upload_put_handler;
+
+#endif
+
diff --git a/src/protocols/rdp/user.c b/src/protocols/rdp/user.c
index 025848a..c2b487f 100644
--- a/src/protocols/rdp/user.c
+++ b/src/protocols/rdp/user.c
@@ -17,16 +17,17 @@
* under the License.
*/
-#include "config.h"
-
-#include "audio_input.h"
+#include "channels/audio-input/audio-input.h"
+#include "channels/cliprdr.h"
+#include "channels/pipe-svc.h"
+#include "common/cursor.h"
#include "common/display.h"
+#include "config.h"
#include "input.h"
-#include "user.h"
#include "rdp.h"
-#include "rdp_settings.h"
-#include "rdp_stream.h"
-#include "rdp_svc.h"
+#include "settings.h"
+#include "upload.h"
+#include "user.h"
#ifdef ENABLE_COMMON_SSH
#include "sftp.h"
@@ -36,9 +37,11 @@
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
+#include <guacamole/stream.h>
#include <guacamole/user.h>
#include <pthread.h>
+#include <stddef.h>
int guac_rdp_user_join_handler(guac_user* user, int argc, char** argv) {
@@ -86,7 +89,7 @@
guac_audio_stream_add_user(rdp_client->audio, user);
/* Bring user up to date with any registered static channels */
- guac_rdp_svc_send_pipes(user);
+ guac_rdp_pipe_svc_send_pipes(user);
/* Synchronize with current display */
guac_common_display_dup(rdp_client->display, user, user->socket);
@@ -112,7 +115,7 @@
user->file_handler = guac_rdp_user_file_handler;
/* Inbound arbitrary named pipes */
- user->pipe_handler = guac_rdp_svc_pipe_handler;
+ user->pipe_handler = guac_rdp_pipe_svc_pipe_handler;
}