Merge staging/1.1.0 changes back to master.
This commit is contained in:
commit
956c5f293e
@ -37,9 +37,9 @@ ARG BUILD_DIR=/tmp/guacd-docker-BUILD
|
|||||||
ARG BUILD_DEPENDENCIES=" \
|
ARG BUILD_DEPENDENCIES=" \
|
||||||
autoconf \
|
autoconf \
|
||||||
automake \
|
automake \
|
||||||
|
freerdp2-dev \
|
||||||
gcc \
|
gcc \
|
||||||
libcairo2-dev \
|
libcairo2-dev \
|
||||||
libfreerdp-dev \
|
|
||||||
libjpeg62-turbo-dev \
|
libjpeg62-turbo-dev \
|
||||||
libossp-uuid-dev \
|
libossp-uuid-dev \
|
||||||
libpango1.0-dev \
|
libpango1.0-dev \
|
||||||
@ -71,7 +71,7 @@ RUN ${PREFIX_DIR}/bin/build-guacd.sh "$BUILD_DIR" "$PREFIX_DIR"
|
|||||||
RUN ${PREFIX_DIR}/bin/list-dependencies.sh \
|
RUN ${PREFIX_DIR}/bin/list-dependencies.sh \
|
||||||
${PREFIX_DIR}/sbin/guacd \
|
${PREFIX_DIR}/sbin/guacd \
|
||||||
${PREFIX_DIR}/lib/libguac-client-*.so \
|
${PREFIX_DIR}/lib/libguac-client-*.so \
|
||||||
${PREFIX_DIR}/lib/freerdp/guac*.so \
|
${PREFIX_DIR}/lib/freerdp2/guac*.so \
|
||||||
> ${PREFIX_DIR}/DEPENDENCIES
|
> ${PREFIX_DIR}/DEPENDENCIES
|
||||||
|
|
||||||
# Use same Debian as the base for the runtime image
|
# Use same Debian as the base for the runtime image
|
||||||
@ -92,7 +92,6 @@ ENV GUACD_LOG_LEVEL=info
|
|||||||
ARG RUNTIME_DEPENDENCIES=" \
|
ARG RUNTIME_DEPENDENCIES=" \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
ghostscript \
|
ghostscript \
|
||||||
libfreerdp-plugins-standard \
|
|
||||||
fonts-liberation \
|
fonts-liberation \
|
||||||
fonts-dejavu \
|
fonts-dejavu \
|
||||||
xfonts-terminus"
|
xfonts-terminus"
|
||||||
@ -108,7 +107,7 @@ RUN apt-get update && \
|
|||||||
|
|
||||||
# Link FreeRDP plugins into proper path
|
# Link FreeRDP plugins into proper path
|
||||||
RUN ${PREFIX_DIR}/bin/link-freerdp-plugins.sh \
|
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 the default listener port
|
||||||
EXPOSE 4822
|
EXPOSE 4822
|
||||||
|
582
configure.ac
582
configure.ac
@ -555,537 +555,143 @@ then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
#
|
#
|
||||||
# FreeRDP
|
# FreeRDP 2 (libfreerdp2, libfreerdp-client2, and libwinpr2)
|
||||||
#
|
#
|
||||||
|
|
||||||
have_freerdp=disabled
|
have_freerdp2=disabled
|
||||||
RDP_LIBS=
|
|
||||||
WINPR_LIBS=
|
|
||||||
AC_ARG_WITH([rdp],
|
AC_ARG_WITH([rdp],
|
||||||
[AS_HELP_STRING([--with-rdp],
|
[AS_HELP_STRING([--with-rdp],
|
||||||
[support RDP @<:@default=check@:>@])],
|
[support RDP @<:@default=check@:>@])],
|
||||||
[],
|
[],
|
||||||
[with_rdp=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"
|
if test "x$with_rdp" != "xno"
|
||||||
then
|
then
|
||||||
have_winpr=yes
|
have_freerdp2=yes
|
||||||
have_freerdp=yes
|
PKG_CHECK_MODULES([RDP], [freerdp2 freerdp-client2 winpr2],
|
||||||
legacy_freerdp_extensions=no
|
[CPPFLAGS="${RDP_CFLAGS} -Werror $CPPFLAGS"],
|
||||||
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([
|
[AC_MSG_WARN([
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
Unable to find libfreerdp-core / libfreerdp
|
Unable to find FreeRDP (libfreerdp2 / libfreerdp-client2 / libwinpr2)
|
||||||
RDP will be disabled.
|
RDP will be disabled.
|
||||||
--------------------------------------------])
|
--------------------------------------------])
|
||||||
have_freerdp=no])])
|
have_freerdp2=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"])
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Available color conversion functions
|
# Available color conversion functions
|
||||||
if test "x${have_freerdp}" = "xyes"
|
if test "x$have_freerdp2" = "xyes"
|
||||||
then
|
then
|
||||||
AC_CHECK_DECL([freerdp_convert_gdi_order_color],
|
|
||||||
[AC_DEFINE([HAVE_FREERDP_CONVERT_GDI_ORDER_COLOR],,
|
# FreeRDP 2.0.0-rc3 and older referred to FreeRDPConvertColor() as
|
||||||
[Whether freerdp_convert_gdi_order_color() is defined])],,
|
# ConvertColor()
|
||||||
|
AC_CHECK_DECL([FreeRDPConvertColor],
|
||||||
|
[AC_DEFINE([HAVE_FREERDPCONVERTCOLOR],,
|
||||||
|
[Whether FreeRDPConvertColor() is defined])],,
|
||||||
[#include <freerdp/codec/color.h>])
|
[#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>])
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check for interval polling in plugins
|
# Glyph callback variants
|
||||||
if test "x${have_freerdp}" = "xyes"
|
if test "x${have_freerdp2}" = "xyes"
|
||||||
then
|
then
|
||||||
AC_CHECK_MEMBERS([rdpSvcPlugin.interval_ms],,,
|
|
||||||
[[#include <freerdp/utils/svc_plugin.h>]])
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Keyboard layout header
|
# FreeRDP 2.0.0-rc3 and older used UINT32 for integer parameters to all
|
||||||
if test "x${have_freerdp}" = "xyes"
|
# rdpGlyph callbacks
|
||||||
then
|
AC_MSG_CHECKING([whether rdpGlyph callbacks accept INT32 integer parameters])
|
||||||
AC_CHECK_HEADERS([freerdp/locale/keyboard.h],,
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
|
||||||
[AC_CHECK_HEADERS([freerdp/kbd/layouts.h],,
|
|
||||||
[AC_MSG_WARN([
|
|
||||||
--------------------------------------------
|
|
||||||
Unable to find keyboard layout headers
|
|
||||||
RDP will be disabled.
|
|
||||||
--------------------------------------------])
|
|
||||||
have_freerdp=no])])
|
|
||||||
fi
|
|
||||||
|
|
||||||
# New headers defining addins
|
|
||||||
if test "x${have_freerdp}" = "xyes"
|
|
||||||
then
|
|
||||||
AC_CHECK_HEADERS([freerdp/addin.h freerdp/client/channels.h])
|
|
||||||
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
|
|
||||||
|
|
||||||
# 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>
|
#include <freerdp/freerdp.h>
|
||||||
void __decompress(rdpContext* context,
|
#include <freerdp/graphics.h>
|
||||||
rdpBitmap* bitmap,
|
#include <winpr/wtypes.h>
|
||||||
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
|
|
||||||
|
|
||||||
#
|
BOOL test_begindraw(rdpContext* context, INT32 x, INT32 y,
|
||||||
# FreeRDP: IWTSVirtualChannelCallback
|
INT32 width, INT32 height, UINT32 fgcolor, UINT32 bgcolor,
|
||||||
#
|
BOOL redundant);
|
||||||
|
|
||||||
if test "x${have_freerdp}" = "xyes"
|
rdpGlyph glyph = {
|
||||||
then
|
.BeginDraw = test_begindraw
|
||||||
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() {
|
int main() {
|
||||||
BYTE* palette = NULL;
|
return (int) glyph.BeginDraw(NULL, 0, 0, 0, 0, 0, 0, FALSE);
|
||||||
interleaved_decompress(NULL, NULL, 0, 0, NULL, 0, 0, 0, 0, 0, 0, palette);
|
}
|
||||||
}]])],
|
|
||||||
[AC_MSG_RESULT([yes])
|
]])],
|
||||||
AC_DEFINE([INTERLEAVED_DECOMPRESS_TAKES_PALETTE],,
|
[AC_MSG_RESULT([yes])]
|
||||||
[Whether interleaved_decompress() accepts an additional palette parameter])],
|
[AC_DEFINE([FREERDP_GLYPH_CALLBACKS_ACCEPT_INT32],,
|
||||||
|
[Whether rdpGlyph callbacks accept INT32 integer parameters])],
|
||||||
[AC_MSG_RESULT([no])])
|
[AC_MSG_RESULT([no])])
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check whether planar_decompress() will handle flipping
|
# CLIPRDR callback variants
|
||||||
if test "x${have_freerdp}" = "xyes"
|
if test "x${have_freerdp2}" = "xyes"
|
||||||
then
|
then
|
||||||
AC_MSG_CHECKING([whether planar_decompress() can flip])
|
|
||||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <freerdp/codec/planar.h>
|
# 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() {
|
int main() {
|
||||||
BOOL* flip = TRUE;
|
return (int) context.MonitorReady(NULL, NULL);
|
||||||
planar_decompress(NULL, NULL, 0, NULL, 0, 0, 0, 0, 0, 0, flip);
|
}
|
||||||
}]])],
|
|
||||||
[AC_MSG_RESULT([yes])
|
]])],
|
||||||
AC_DEFINE([PLANAR_DECOMPRESS_CAN_FLIP],,
|
[AC_MSG_RESULT([yes])]
|
||||||
[Whether planar_decompress() can flip])],
|
[AC_DEFINE([FREERDP_CLIPRDR_CALLBACKS_REQUIRE_CONST],,
|
||||||
|
[Whether CLIPRDR callbacks require const for the final parameter])],
|
||||||
[AC_MSG_RESULT([no])])
|
[AC_MSG_RESULT([no])])
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#
|
# RAIL callback variants
|
||||||
# FreeRDP: rdpContext
|
if test "x${have_freerdp2}" = "xyes"
|
||||||
#
|
|
||||||
|
|
||||||
# Check for rdpContext.codecs
|
|
||||||
if test "x${have_freerdp}" = "xyes"
|
|
||||||
then
|
then
|
||||||
AC_CHECK_MEMBERS([rdpContext.codecs],
|
|
||||||
[AC_DEFINE([FREERDP_BITMAP_REQUIRES_ALIGNED_MALLOC],,
|
# FreeRDP 2.0.0-rc3 and older did not use const for RAIL callbacks
|
||||||
[Whether this version of FreeRDP requires _aligned_malloc() for bitmap data])],,
|
AC_MSG_CHECKING([whether RAIL callbacks require const for their final parameter])
|
||||||
[[#include <freerdp/freerdp.h>]])
|
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
|
fi
|
||||||
|
|
||||||
#
|
# Restore CPPFLAGS, removing FreeRDP-specific options needed for testing
|
||||||
# FreeRDP: rdpPalette
|
CPPFLAGS="$OLDCPPFLAGS"
|
||||||
#
|
|
||||||
|
|
||||||
if test "x${have_freerdp}" = "xyes"
|
AM_CONDITIONAL([ENABLE_RDP], [test "x${have_freerdp2}" = "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)
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# libssh2
|
# libssh2
|
||||||
@ -1426,7 +1032,7 @@ $PACKAGE_NAME version $PACKAGE_VERSION
|
|||||||
|
|
||||||
Library status:
|
Library status:
|
||||||
|
|
||||||
freerdp ............. ${have_freerdp}
|
freerdp2 ............ ${have_freerdp2}
|
||||||
pango ............... ${have_pango}
|
pango ............... ${have_pango}
|
||||||
libavcodec .......... ${have_libavcodec}
|
libavcodec .......... ${have_libavcodec}
|
||||||
libavutil ........... ${have_libavutil}
|
libavutil ........... ${have_libavutil}
|
||||||
|
@ -260,18 +260,31 @@ static int __guac_common_surface_is_opaque(guac_common_surface* surface,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the given rectangle should be combined into the existing
|
* 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 surface
|
||||||
* @param rect The update rectangle.
|
* The surface being updated.
|
||||||
* @param rect_only Non-zero if this update, by its nature, contains only
|
*
|
||||||
* metainformation about the update's rectangle, zero if
|
* @param rect
|
||||||
* the update also contains image data.
|
* The bounding rectangle of the update being made to the surface.
|
||||||
* @return Non-zero if the update should be combined with any existing update,
|
*
|
||||||
* zero otherwise.
|
* @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) {
|
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) {
|
if (surface->dirty) {
|
||||||
|
|
||||||
int combined_cost, dirty_cost, update_cost;
|
int combined_cost, dirty_cost, update_cost;
|
||||||
|
@ -70,7 +70,7 @@ while [ -n "$1" ]; do
|
|||||||
|
|
||||||
# Determine correct install location for FreeRDP plugins
|
# Determine correct install location for FreeRDP plugins
|
||||||
FREERDP_DIR="$(where_is_freerdp "$1")"
|
FREERDP_DIR="$(where_is_freerdp "$1")"
|
||||||
FREERDP_PLUGIN_DIR="${FREERDP_DIR}/freerdp"
|
FREERDP_PLUGIN_DIR="${FREERDP_DIR}/freerdp2"
|
||||||
|
|
||||||
# Add symbolic link if necessary
|
# Add symbolic link if necessary
|
||||||
if [ ! -e "$FREERDP_PLUGIN_DIR/$(basename "$1")" ]; then
|
if [ ! -e "$FREERDP_PLUGIN_DIR/$(basename "$1")" ]; then
|
||||||
|
1
src/protocols/rdp/.gitignore
vendored
1
src/protocols/rdp/.gitignore
vendored
@ -4,5 +4,6 @@ _generated_runner.c
|
|||||||
test_rdp
|
test_rdp
|
||||||
|
|
||||||
# Autogenerated sources
|
# Autogenerated sources
|
||||||
|
_generated_channel_entry_wrappers.c
|
||||||
_generated_keymaps.c
|
_generated_keymaps.c
|
||||||
|
|
||||||
|
@ -29,217 +29,171 @@ ACLOCAL_AMFLAGS = -I m4
|
|||||||
lib_LTLIBRARIES = libguac-client-rdp.la
|
lib_LTLIBRARIES = libguac-client-rdp.la
|
||||||
SUBDIRS = . tests
|
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
|
# 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 = \
|
libguac_client_rdp_la_CFLAGS = \
|
||||||
-Werror -Wall -Iinclude \
|
-Werror -Wall -Iinclude \
|
||||||
@COMMON_INCLUDE@ \
|
@COMMON_INCLUDE@ \
|
||||||
@COMMON_SSH_INCLUDE@ \
|
@COMMON_SSH_INCLUDE@ \
|
||||||
@LIBGUAC_INCLUDE@
|
@LIBGUAC_INCLUDE@ \
|
||||||
|
@RDP_CFLAGS@
|
||||||
|
|
||||||
libguac_client_rdp_la_LDFLAGS = \
|
libguac_client_rdp_la_LDFLAGS = \
|
||||||
-version-info 0:0:0 \
|
-version-info 0:0:0 \
|
||||||
@CAIRO_LIBS@ \
|
@CAIRO_LIBS@ \
|
||||||
@PTHREAD_LIBS@ \
|
@PTHREAD_LIBS@ \
|
||||||
@RDP_LIBS@ \
|
@RDP_LIBS@
|
||||||
@WINPR_LIBS@
|
|
||||||
|
|
||||||
libguac_client_rdp_la_LIBADD = \
|
libguac_client_rdp_la_LIBADD = \
|
||||||
@COMMON_LTLIB@ \
|
@COMMON_LTLIB@ \
|
||||||
@LIBGUAC_LTLIB@
|
@LIBGUAC_LTLIB@
|
||||||
|
|
||||||
#
|
#
|
||||||
# RDPDR
|
# Plugins for FreeRDP
|
||||||
#
|
#
|
||||||
|
|
||||||
guacdr_cflags = \
|
freerdp_LTLIBRARIES = \
|
||||||
|
libguac-common-svc-client.la \
|
||||||
|
libguacai-client.la
|
||||||
|
|
||||||
|
freerdpdir = ${libdir}/freerdp2
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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 \
|
-Werror -Wall -Iinclude \
|
||||||
@COMMON_INCLUDE@ \
|
@LIBGUAC_INCLUDE@ \
|
||||||
@COMMON_SSH_INCLUDE@ \
|
@RDP_CFLAGS@
|
||||||
@LIBGUAC_INCLUDE@
|
|
||||||
|
|
||||||
guacdr_ldflags = \
|
libguac_common_svc_client_la_LDFLAGS = \
|
||||||
-module -avoid-version -shared \
|
-module -avoid-version -shared \
|
||||||
@PTHREAD_LIBS@ \
|
@RDP_LIBS@
|
||||||
@RDP_LIBS@ \
|
|
||||||
@WINPR_LIBS@
|
|
||||||
|
|
||||||
guacdr_libadd = \
|
libguac_common_svc_client_la_LIBADD = \
|
||||||
@COMMON_LTLIB@ \
|
|
||||||
@LIBGUAC_LTLIB@
|
@LIBGUAC_LTLIB@
|
||||||
|
|
||||||
#
|
#
|
||||||
# Audio Input
|
# Audio Input
|
||||||
#
|
#
|
||||||
|
|
||||||
guacai_cflags = \
|
libguacai_client_la_SOURCES = \
|
||||||
|
channels/audio-input/audio-buffer.c \
|
||||||
|
plugins/guacai/guacai-messages.c \
|
||||||
|
plugins/guacai/guacai.c \
|
||||||
|
plugins/ptr-string.c
|
||||||
|
|
||||||
|
libguacai_client_la_CFLAGS = \
|
||||||
-Werror -Wall -Iinclude \
|
-Werror -Wall -Iinclude \
|
||||||
@COMMON_INCLUDE@ \
|
@COMMON_INCLUDE@ \
|
||||||
@COMMON_SSH_INCLUDE@ \
|
@COMMON_SSH_INCLUDE@ \
|
||||||
@LIBGUAC_INCLUDE@
|
@LIBGUAC_INCLUDE@ \
|
||||||
|
@RDP_CFLAGS@
|
||||||
|
|
||||||
guacai_ldflags = \
|
libguacai_client_la_LDFLAGS = \
|
||||||
-module -avoid-version -shared \
|
-module -avoid-version -shared \
|
||||||
@PTHREAD_LIBS@ \
|
@PTHREAD_LIBS@ \
|
||||||
@RDP_LIBS@ \
|
@RDP_LIBS@
|
||||||
@WINPR_LIBS@
|
|
||||||
|
|
||||||
guacai_libadd = \
|
libguacai_client_la_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@ \
|
@COMMON_LTLIB@ \
|
||||||
@LIBGUAC_LTLIB@
|
@LIBGUAC_LTLIB@
|
||||||
|
|
||||||
@ -254,11 +208,16 @@ libguac_client_rdp_la_LIBADD += @COMMON_SSH_LTLIB@
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
#
|
#
|
||||||
# Autogenerate keymaps
|
# Autogenerated keymaps and channel wrapper functions
|
||||||
#
|
#
|
||||||
|
|
||||||
CLEANFILES = _generated_keymaps.c
|
CLEANFILES = \
|
||||||
BUILT_SOURCES = _generated_keymaps.c
|
_generated_channel_entry_wrappers.c \
|
||||||
|
_generated_keymaps.c
|
||||||
|
|
||||||
|
BUILT_SOURCES = \
|
||||||
|
_generated_channel_entry_wrappers.c \
|
||||||
|
_generated_keymaps.c
|
||||||
|
|
||||||
rdp_keymaps = \
|
rdp_keymaps = \
|
||||||
$(srcdir)/keymaps/base.keymap \
|
$(srcdir)/keymaps/base.keymap \
|
||||||
@ -280,71 +239,13 @@ rdp_keymaps = \
|
|||||||
$(srcdir)/keymaps/tr_tr_qwerty.keymap
|
$(srcdir)/keymaps/tr_tr_qwerty.keymap
|
||||||
|
|
||||||
_generated_keymaps.c: $(rdp_keymaps)
|
_generated_keymaps.c: $(rdp_keymaps)
|
||||||
$(srcdir)/keymaps/generate.pl $(rdp_keymaps)
|
$(AM_V_GEN) $(srcdir)/keymaps/generate.pl $(rdp_keymaps)
|
||||||
|
|
||||||
|
_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
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
$(rdp_keymaps) \
|
$(rdp_keymaps) \
|
||||||
keymaps/generate.pl
|
keymaps/generate.pl \
|
||||||
|
plugins/generate-entry-wrappers.pl
|
||||||
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
|
|
||||||
|
|
||||||
|
@ -17,27 +17,16 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "bitmap.h"
|
||||||
|
|
||||||
#include "client.h"
|
|
||||||
#include "common/display.h"
|
#include "common/display.h"
|
||||||
#include "common/surface.h"
|
#include "common/surface.h"
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
#include "rdp_bitmap.h"
|
|
||||||
#include "rdp_settings.h"
|
|
||||||
|
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
#include <freerdp/codec/bitmap.h>
|
|
||||||
#include <freerdp/codec/color.h>
|
|
||||||
#include <freerdp/freerdp.h>
|
#include <freerdp/freerdp.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/socket.h>
|
#include <winpr/crt.h>
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/wtypes.h>
|
#include <winpr/wtypes.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-wtypes.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -72,30 +61,7 @@ void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
|
BOOL 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. */
|
/* No corresponding surface yet - caching is deferred. */
|
||||||
((guac_rdp_bitmap*) bitmap)->layer = NULL;
|
((guac_rdp_bitmap*) bitmap)->layer = NULL;
|
||||||
@ -103,9 +69,11 @@ void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
|
|||||||
/* Start at zero usage */
|
/* Start at zero usage */
|
||||||
((guac_rdp_bitmap*) bitmap)->used = 0;
|
((guac_rdp_bitmap*) bitmap)->used = 0;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap) {
|
BOOL guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap) {
|
||||||
|
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
@ -145,6 +113,8 @@ void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap) {
|
|||||||
/* Increment usage counter */
|
/* Increment usage counter */
|
||||||
((guac_rdp_bitmap*) bitmap)->used++;
|
((guac_rdp_bitmap*) bitmap)->used++;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap) {
|
void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap) {
|
||||||
@ -157,9 +127,16 @@ void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap) {
|
|||||||
if (buffer != NULL)
|
if (buffer != NULL)
|
||||||
guac_common_display_free_buffer(rdp_client->display, buffer);
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary) {
|
BOOL guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary) {
|
||||||
|
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
@ -172,7 +149,7 @@ void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL pri
|
|||||||
/* Make sure that the recieved bitmap is not NULL before processing */
|
/* Make sure that the recieved bitmap is not NULL before processing */
|
||||||
if (bitmap == NULL) {
|
if (bitmap == NULL) {
|
||||||
guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in bitmap_setsurface instruction.");
|
guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in bitmap_setsurface instruction.");
|
||||||
return;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If not available as a surface, make available. */
|
/* If not available as a surface, make available. */
|
||||||
@ -184,80 +161,7 @@ void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL pri
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
return TRUE;
|
||||||
|
|
||||||
#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;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -17,21 +17,16 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_BITMAP_H
|
||||||
#ifndef _GUAC_RDP_RDP_BITMAP_H
|
#define GUAC_RDP_BITMAP_H
|
||||||
#define _GUAC_RDP_RDP_BITMAP_H
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "common/display.h"
|
#include "common/display.h"
|
||||||
|
|
||||||
#include <freerdp/freerdp.h>
|
#include <freerdp/freerdp.h>
|
||||||
|
#include <freerdp/graphics.h>
|
||||||
#include <guacamole/layer.h>
|
#include <guacamole/layer.h>
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/wtypes.h>
|
#include <winpr/wtypes.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-wtypes.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Guacamole-specific rdpBitmap data.
|
* Guacamole-specific rdpBitmap data.
|
||||||
@ -77,8 +72,11 @@ void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap);
|
|||||||
*
|
*
|
||||||
* @param bitmap
|
* @param bitmap
|
||||||
* The bitmap to initialize.
|
* The bitmap to initialize.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* TRUE if successful, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap);
|
BOOL guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paints the given rdpBitmap on the primary display surface. Note that this
|
* Paints the given rdpBitmap on the primary display surface. Note that this
|
||||||
@ -92,8 +90,11 @@ void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap);
|
|||||||
* The bitmap to paint. This structure will also contain the specifics of
|
* The bitmap to paint. This structure will also contain the specifics of
|
||||||
* the paint operation to perform, including the destination X/Y
|
* the paint operation to perform, including the destination X/Y
|
||||||
* coordinates.
|
* coordinates.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* TRUE if successful, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap);
|
BOOL guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frees any Guacamole-specific data associated with the given rdpBitmap.
|
* Frees any Guacamole-specific data associated with the given rdpBitmap.
|
||||||
@ -122,82 +123,11 @@ void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap);
|
|||||||
* TRUE if the bitmap parameter should be ignored, and the current drawing
|
* TRUE if the bitmap parameter should be ignored, and the current drawing
|
||||||
* surface should be reset to the primary drawing surface of the remote
|
* surface should be reset to the primary drawing surface of the remote
|
||||||
* display, FALSE otherwise.
|
* display, FALSE otherwise.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* TRUE if successful, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap,
|
BOOL guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap,
|
||||||
BOOL primary);
|
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
|
#endif
|
@ -17,181 +17,19 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/audio-input/audio-buffer.h"
|
||||||
#include "audio_input.h"
|
|
||||||
#include "dvc.h"
|
|
||||||
#include "ptr_string.h"
|
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
|
|
||||||
#include <freerdp/freerdp.h>
|
#include <guacamole/client.h>
|
||||||
#include <freerdp/channels/channels.h>
|
|
||||||
#include <guacamole/protocol.h>
|
#include <guacamole/protocol.h>
|
||||||
#include <guacamole/socket.h>
|
#include <guacamole/socket.h>
|
||||||
#include <guacamole/stream.h>
|
#include <guacamole/stream.h>
|
||||||
#include <guacamole/user.h>
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <stdint.h>
|
||||||
/**
|
#include <stdlib.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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
guac_rdp_audio_buffer* guac_rdp_audio_buffer_alloc() {
|
guac_rdp_audio_buffer* guac_rdp_audio_buffer_alloc() {
|
||||||
guac_rdp_audio_buffer* buffer = calloc(1, sizeof(guac_rdp_audio_buffer));
|
guac_rdp_audio_buffer* buffer = calloc(1, sizeof(guac_rdp_audio_buffer));
|
@ -17,16 +17,11 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef GUAC_RDP_AUDIO_INPUT_H
|
#ifndef GUAC_RDP_CHANNELS_AUDIO_INPUT_AUDIO_BUFFER_H
|
||||||
#define GUAC_RDP_AUDIO_INPUT_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/stream.h>
|
||||||
#include <guacamole/user.h>
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -278,36 +273,5 @@ void guac_rdp_audio_buffer_end(guac_rdp_audio_buffer* audio_buffer);
|
|||||||
*/
|
*/
|
||||||
void guac_rdp_audio_buffer_free(guac_rdp_audio_buffer* audio_buffer);
|
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
|
#endif
|
||||||
|
|
195
src/protocols/rdp/channels/audio-input/audio-input.c
Normal file
195
src/protocols/rdp/channels/audio-input/audio-input.c
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
53
src/protocols/rdp/channels/audio-input/audio-input.h
Normal file
53
src/protocols/rdp/channels/audio-input/audio-input.h
Normal file
@ -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
|
||||||
|
|
620
src/protocols/rdp/channels/cliprdr.c
Normal file
620
src/protocols/rdp/channels/cliprdr.c
Normal file
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
146
src/protocols/rdp/channels/cliprdr.h
Normal file
146
src/protocols/rdp/channels/cliprdr.h
Normal file
@ -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
|
||||||
|
|
101
src/protocols/rdp/channels/common-svc.c
Normal file
101
src/protocols/rdp/channels/common-svc.c
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
229
src/protocols/rdp/channels/common-svc.h
Normal file
229
src/protocols/rdp/channels/common-svc.h
Normal file
@ -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
|
||||||
|
|
@ -17,29 +17,26 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/disp.h"
|
||||||
#include "client.h"
|
#include "plugins/channels.h"
|
||||||
#include "dvc.h"
|
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
#include "rdp_disp.h"
|
#include "settings.h"
|
||||||
#include "rdp_settings.h"
|
|
||||||
|
|
||||||
|
#include <freerdp/client/disp.h>
|
||||||
#include <freerdp/freerdp.h>
|
#include <freerdp/freerdp.h>
|
||||||
|
#include <freerdp/event.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/timestamp.h>
|
#include <guacamole/timestamp.h>
|
||||||
|
|
||||||
#ifdef HAVE_FREERDP_CLIENT_DISP_H
|
#include <stdlib.h>
|
||||||
#include <freerdp/client/disp.h>
|
#include <string.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
guac_rdp_disp* guac_rdp_disp_alloc() {
|
guac_rdp_disp* guac_rdp_disp_alloc() {
|
||||||
|
|
||||||
guac_rdp_disp* disp = malloc(sizeof(guac_rdp_disp));
|
guac_rdp_disp* disp = malloc(sizeof(guac_rdp_disp));
|
||||||
|
|
||||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
|
||||||
/* Not yet connected */
|
/* Not yet connected */
|
||||||
disp->disp = NULL;
|
disp->disp = NULL;
|
||||||
#endif
|
|
||||||
|
|
||||||
/* No requests have been made */
|
/* No requests have been made */
|
||||||
disp->last_request = guac_timestamp_current();
|
disp->last_request = guac_timestamp_current();
|
||||||
@ -55,23 +52,60 @@ void guac_rdp_disp_free(guac_rdp_disp* disp) {
|
|||||||
free(disp);
|
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
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
context->settings->SupportDisplayControl = TRUE;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
#endif
|
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 */
|
/* 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
|
* Fits a given dimension within the allowed bounds for Display Update
|
||||||
* messages, adjusting the other dimension such that aspect ratio is
|
* messages, adjusting the other dimension such that aspect ratio is
|
||||||
@ -172,7 +206,6 @@ void guac_rdp_disp_update_size(guac_rdp_disp* disp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
else if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) {
|
else if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) {
|
||||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
|
||||||
DISPLAY_CONTROL_MONITOR_LAYOUT monitors[1] = {{
|
DISPLAY_CONTROL_MONITOR_LAYOUT monitors[1] = {{
|
||||||
.Flags = 0x1, /* DISPLAYCONTROL_MONITOR_PRIMARY */
|
.Flags = 0x1, /* DISPLAYCONTROL_MONITOR_PRIMARY */
|
||||||
.Left = 0,
|
.Left = 0,
|
||||||
@ -189,7 +222,6 @@ void guac_rdp_disp_update_size(guac_rdp_disp* disp,
|
|||||||
/* Send display update notification if display channel is connected */
|
/* Send display update notification if display channel is connected */
|
||||||
if (disp->disp != NULL)
|
if (disp->disp != NULL)
|
||||||
disp->disp->SendMonitorLayout(disp->disp, 1, monitors);
|
disp->disp->SendMonitorLayout(disp->disp, 1, monitors);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -17,17 +17,15 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef GUAC_RDP_DISP_H
|
#ifndef GUAC_RDP_CHANNELS_DISP_H
|
||||||
#define GUAC_RDP_DISP_H
|
#define GUAC_RDP_CHANNELS_DISP_H
|
||||||
|
|
||||||
#include "dvc.h"
|
#include "settings.h"
|
||||||
#include "rdp_settings.h"
|
|
||||||
|
|
||||||
#include <freerdp/freerdp.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_FREERDP_CLIENT_DISP_H
|
|
||||||
#include <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.
|
* The minimum value for width or height, in pixels.
|
||||||
@ -50,12 +48,10 @@
|
|||||||
*/
|
*/
|
||||||
typedef struct guac_rdp_disp {
|
typedef struct guac_rdp_disp {
|
||||||
|
|
||||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
|
||||||
/**
|
/**
|
||||||
* Display control interface.
|
* Display control interface.
|
||||||
*/
|
*/
|
||||||
DispClientContext* disp;
|
DispClientContext* disp;
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The timestamp of the last display update request, or 0 if no request
|
* The timestamp of the last display update request, or 0 if no request
|
||||||
@ -83,53 +79,43 @@ typedef struct guac_rdp_disp {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocates a new display update module, which will ultimately control the
|
* 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();
|
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);
|
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
|
* 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
|
* to be loaded by FreeRDP's "drdynvc" plugin. The context of the plugin will
|
||||||
* once guac_rdp_load_drdynvc() is invoked with the guac_rdp_dvc_list passed to
|
* automatically be assicated with the guac_rdp_disp instance pointed to by the
|
||||||
* this function. The "disp" plugin ultimately adds support for the Display
|
* current guac_rdp_client. The plugin will only be loaded once the "drdynvc"
|
||||||
* Update channel. NOTE: It is still up to external code to detect when the
|
* plugin is loaded. The "disp" plugin ultimately adds support for the Display
|
||||||
* "disp" channel is connected, and update the guac_rdp_disp with a call to
|
* Update channel.
|
||||||
* guac_rdp_disp_connect().
|
*
|
||||||
|
* 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
|
* @param context
|
||||||
* The rdpContext associated with the active RDP session.
|
* 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);
|
void guac_rdp_disp_load_plugin(rdpContext* context);
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests a display size update, which may then be sent immediately to the
|
* Requests a display size update, which may then be sent immediately to the
|
||||||
@ -200,6 +186,10 @@ void guac_rdp_disp_reconnect_complete(guac_rdp_disp* disp);
|
|||||||
* Returns whether a full RDP reconnect is required for display update changes
|
* Returns whether a full RDP reconnect is required for display update changes
|
||||||
* to take effect.
|
* to take effect.
|
||||||
*
|
*
|
||||||
|
* @param disp
|
||||||
|
* The display update module that should be checked to determine whether a
|
||||||
|
* reconnect is required.
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
* Non-zero if a reconnect is needed, zero otherwise.
|
* Non-zero if a reconnect is needed, zero otherwise.
|
||||||
*/
|
*/
|
230
src/protocols/rdp/channels/pipe-svc.c
Normal file
230
src/protocols/rdp/channels/pipe-svc.c
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
188
src/protocols/rdp/channels/pipe-svc.h
Normal file
188
src/protocols/rdp/channels/pipe-svc.h
Normal file
@ -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
|
||||||
|
|
211
src/protocols/rdp/channels/rail.c
Normal file
211
src/protocols/rdp/channels/rail.c
Normal file
@ -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.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -17,19 +17,23 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_CHANNELS_RAIL_H
|
||||||
|
#define GUAC_RDP_CHANNELS_RAIL_H
|
||||||
|
|
||||||
#ifndef __GUAC_CLIENT_CLIPRDR_COMPAT_H
|
#include <freerdp/freerdp.h>
|
||||||
#define __GUAC_CLIENT_CLIPRDR_COMPAT_H
|
|
||||||
|
|
||||||
#include "config.h"
|
/**
|
||||||
|
* Initializes RemoteApp support for RDP and handling of the RAIL channel. If
|
||||||
#include <freerdp/plugins/cliprdr.h>
|
* failures occur, messages noting the specifics of those failures will be
|
||||||
|
* logged, and RemoteApp support will not be functional.
|
||||||
#define CliprdrChannel_Class RDP_EVENT_CLASS_CLIPRDR
|
*
|
||||||
#define CliprdrChannel_FormatList RDP_EVENT_TYPE_CB_FORMAT_LIST
|
* This MUST be called within the PreConnect callback of the freerdp instance
|
||||||
#define CliprdrChannel_MonitorReady RDP_EVENT_TYPE_CB_MONITOR_READY
|
* for RAIL support to be loaded.
|
||||||
#define CliprdrChannel_DataRequest RDP_EVENT_TYPE_CB_DATA_REQUEST
|
*
|
||||||
#define CliprdrChannel_DataResponse RDP_EVENT_TYPE_CB_DATA_RESPONSE
|
* @param context
|
||||||
|
* The rdpContext associated with the FreeRDP side of the RDP connection.
|
||||||
|
*/
|
||||||
|
void guac_rdp_rail_load_plugin(rdpContext* context);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -17,26 +17,22 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/rdpdr/rdpdr-fs-messages-dir-info.h"
|
||||||
|
#include "channels/rdpdr/rdpdr.h"
|
||||||
#include "rdpdr_service.h"
|
#include "fs.h"
|
||||||
#include "rdp_fs.h"
|
|
||||||
#include "rdp_status.h"
|
|
||||||
#include "unicode.h"
|
#include "unicode.h"
|
||||||
|
|
||||||
#include <freerdp/utils/svc_plugin.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/unicode.h>
|
#include <guacamole/unicode.h>
|
||||||
|
#include <winpr/nt.h>
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-stream.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_query_directory_info(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_query_directory_info(guac_rdp_common_svc* svc,
|
||||||
const char* entry_name, int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
const char* entry_name, int entry_file_id) {
|
||||||
|
|
||||||
guac_rdp_fs_file* file;
|
guac_rdp_fs_file* file;
|
||||||
|
|
||||||
@ -49,16 +45,17 @@ void guac_rdpdr_fs_process_query_directory_info(guac_rdpdr_device* device,
|
|||||||
(char*) utf16_entry_name, sizeof(utf16_entry_name));
|
(char*) utf16_entry_name, sizeof(utf16_entry_name));
|
||||||
|
|
||||||
/* Get file */
|
/* 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)
|
if (file == NULL)
|
||||||
return;
|
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\")]",
|
"%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,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
STATUS_SUCCESS, 4 + 64 + utf16_length + 2);
|
iorequest->completion_id, STATUS_SUCCESS,
|
||||||
|
4 + 64 + utf16_length + 2);
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream,
|
Stream_Write_UINT32(output_stream,
|
||||||
64 + utf16_length + 2); /* Length */
|
64 + utf16_length + 2); /* Length */
|
||||||
@ -77,12 +74,13 @@ void guac_rdpdr_fs_process_query_directory_info(guac_rdpdr_device* device,
|
|||||||
Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
|
Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
|
||||||
Stream_Write(output_stream, "\0\0", 2);
|
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,
|
void guac_rdpdr_fs_process_query_full_directory_info(guac_rdp_common_svc* svc,
|
||||||
const char* entry_name, int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
const char* entry_name, int entry_file_id) {
|
||||||
|
|
||||||
guac_rdp_fs_file* file;
|
guac_rdp_fs_file* file;
|
||||||
|
|
||||||
@ -95,16 +93,17 @@ void guac_rdpdr_fs_process_query_full_directory_info(guac_rdpdr_device* device,
|
|||||||
(char*) utf16_entry_name, sizeof(utf16_entry_name));
|
(char*) utf16_entry_name, sizeof(utf16_entry_name));
|
||||||
|
|
||||||
/* Get file */
|
/* 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)
|
if (file == NULL)
|
||||||
return;
|
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\")]",
|
"%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,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
STATUS_SUCCESS, 4 + 68 + utf16_length + 2);
|
iorequest->completion_id, STATUS_SUCCESS,
|
||||||
|
4 + 68 + utf16_length + 2);
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream,
|
Stream_Write_UINT32(output_stream,
|
||||||
68 + utf16_length + 2); /* Length */
|
68 + utf16_length + 2); /* Length */
|
||||||
@ -124,12 +123,13 @@ void guac_rdpdr_fs_process_query_full_directory_info(guac_rdpdr_device* device,
|
|||||||
Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
|
Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
|
||||||
Stream_Write(output_stream, "\0\0", 2);
|
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,
|
void guac_rdpdr_fs_process_query_both_directory_info(guac_rdp_common_svc* svc,
|
||||||
const char* entry_name, int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
const char* entry_name, int entry_file_id) {
|
||||||
|
|
||||||
guac_rdp_fs_file* file;
|
guac_rdp_fs_file* file;
|
||||||
|
|
||||||
@ -142,16 +142,17 @@ void guac_rdpdr_fs_process_query_both_directory_info(guac_rdpdr_device* device,
|
|||||||
(char*) utf16_entry_name, sizeof(utf16_entry_name));
|
(char*) utf16_entry_name, sizeof(utf16_entry_name));
|
||||||
|
|
||||||
/* Get file */
|
/* 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)
|
if (file == NULL)
|
||||||
return;
|
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\")]",
|
"%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,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
STATUS_SUCCESS, 4 + 69 + 24 + utf16_length + 2);
|
iorequest->completion_id, STATUS_SUCCESS,
|
||||||
|
4 + 69 + 24 + utf16_length + 2);
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream,
|
Stream_Write_UINT32(output_stream,
|
||||||
69 + 24 + utf16_length + 2); /* Length */
|
69 + 24 + utf16_length + 2); /* Length */
|
||||||
@ -175,12 +176,13 @@ void guac_rdpdr_fs_process_query_both_directory_info(guac_rdpdr_device* device,
|
|||||||
Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
|
Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
|
||||||
Stream_Write(output_stream, "\0\0", 2);
|
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,
|
void guac_rdpdr_fs_process_query_names_info(guac_rdp_common_svc* svc,
|
||||||
const char* entry_name, int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
const char* entry_name, int entry_file_id) {
|
||||||
|
|
||||||
guac_rdp_fs_file* file;
|
guac_rdp_fs_file* file;
|
||||||
|
|
||||||
@ -193,16 +195,17 @@ void guac_rdpdr_fs_process_query_names_info(guac_rdpdr_device* device,
|
|||||||
(char*) utf16_entry_name, sizeof(utf16_entry_name));
|
(char*) utf16_entry_name, sizeof(utf16_entry_name));
|
||||||
|
|
||||||
/* Get file */
|
/* 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)
|
if (file == NULL)
|
||||||
return;
|
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\")]",
|
"%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,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
STATUS_SUCCESS, 4 + 12 + utf16_length + 2);
|
iorequest->completion_id, STATUS_SUCCESS,
|
||||||
|
4 + 12 + utf16_length + 2);
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream,
|
Stream_Write_UINT32(output_stream,
|
||||||
12 + utf16_length + 2); /* Length */
|
12 + utf16_length + 2); /* Length */
|
||||||
@ -213,7 +216,7 @@ void guac_rdpdr_fs_process_query_names_info(guac_rdpdr_device* device,
|
|||||||
Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
|
Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
|
||||||
Stream_Write(output_stream, "\0\0", 2);
|
Stream_Write(output_stream, "\0\0", 2);
|
||||||
|
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -17,58 +17,76 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_DIR_INFO_H
|
||||||
#ifndef __GUAC_RDPDR_FS_MESSAGES_DIR_INFO_H
|
#define GUAC_RDP_CHANNELS_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
|
* Handlers for directory queries received over the RDPDR channel via the
|
||||||
* IRP_MJ_DIRECTORY_CONTROL major function and the IRP_MN_QUERY_DIRECTORY minor
|
* IRP_MJ_DIRECTORY_CONTROL major function and the IRP_MN_QUERY_DIRECTORY minor
|
||||||
* function.
|
* function.
|
||||||
*
|
*
|
||||||
* @file rdpdr_fs_messages_dir_info.h
|
* @file rdpdr-fs-messages-dir-info.h
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/common-svc.h"
|
||||||
|
#include "channels/rdpdr/rdpdr.h"
|
||||||
|
|
||||||
#include "rdpdr_service.h"
|
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-stream.h"
|
/**
|
||||||
#endif
|
* 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
|
* Processes a query request for FileDirectoryInformation. From the
|
||||||
* documentation this is "defined as the file's name, time stamp, and size, or its
|
* documentation this is "defined as the file's name, time stamp, and size, or its
|
||||||
* attributes."
|
* attributes."
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_query_directory_info(guac_rdpdr_device* device,
|
guac_rdpdr_directory_query_handler guac_rdpdr_fs_process_query_directory_info;
|
||||||
const char* entry_name, int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a query request for FileFullDirectoryInformation. From the
|
* Processes a query request for FileFullDirectoryInformation. From the
|
||||||
* documentation, this is "defined as all the basic information, plus extended
|
* documentation, this is "defined as all the basic information, plus extended
|
||||||
* attribute size."
|
* attribute size."
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_query_full_directory_info(guac_rdpdr_device* device,
|
guac_rdpdr_directory_query_handler guac_rdpdr_fs_process_query_full_directory_info;
|
||||||
const char* entry_name, int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a query request for FileBothDirectoryInformation. From the
|
* Processes a query request for FileBothDirectoryInformation. From the
|
||||||
* documentation, this absurdly-named request is "basic information plus
|
* documentation, this absurdly-named request is "basic information plus
|
||||||
* extended attribute size and short name about a file or directory."
|
* extended attribute size and short name about a file or directory."
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_query_both_directory_info(guac_rdpdr_device* device,
|
guac_rdpdr_directory_query_handler guac_rdpdr_fs_process_query_both_directory_info;
|
||||||
const char* entry_name, int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a query request for FileNamesInformation. From the documentation,
|
* Processes a query request for FileNamesInformation. From the documentation,
|
||||||
* this is "detailed information on the names of files in a directory."
|
* this is "detailed information on the names of files in a directory."
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_query_names_info(guac_rdpdr_device* device,
|
guac_rdpdr_directory_query_handler guac_rdpdr_fs_process_query_names_info;
|
||||||
const char* entry_name, int file_id, int completion_id);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -17,44 +17,38 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/rdpdr/rdpdr-fs-messages-file-info.h"
|
||||||
|
#include "channels/rdpdr/rdpdr.h"
|
||||||
#include "rdpdr_service.h"
|
#include "download.h"
|
||||||
#include "rdp_fs.h"
|
#include "fs.h"
|
||||||
#include "rdp_status.h"
|
|
||||||
#include "unicode.h"
|
#include "unicode.h"
|
||||||
|
|
||||||
#include <freerdp/utils/svc_plugin.h>
|
#include <guacamole/client.h>
|
||||||
|
#include <winpr/file.h>
|
||||||
#ifdef ENABLE_WINPR
|
#include <winpr/nt.h>
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#include <winpr/wtypes.h>
|
#include <winpr/wtypes.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-stream.h"
|
|
||||||
#include "compat/winpr-wtypes.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_query_basic_info(guac_rdpdr_device* device, wStream* input_stream,
|
void guac_rdpdr_fs_process_query_basic_info(guac_rdp_common_svc* svc,
|
||||||
int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
wStream* output_stream;
|
wStream* output_stream;
|
||||||
guac_rdp_fs_file* file;
|
guac_rdp_fs_file* file;
|
||||||
|
|
||||||
/* Get file */
|
/* 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, iorequest->file_id);
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||||
"%s: [file_id=%i]",
|
iorequest->file_id);
|
||||||
__func__, file_id);
|
|
||||||
|
|
||||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
STATUS_SUCCESS, 40);
|
iorequest->completion_id, STATUS_SUCCESS, 40);
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream, 36);
|
Stream_Write_UINT32(output_stream, 36);
|
||||||
Stream_Write_UINT64(output_stream, file->ctime); /* CreationTime */
|
Stream_Write_UINT64(output_stream, file->ctime); /* CreationTime */
|
||||||
@ -65,31 +59,31 @@ void guac_rdpdr_fs_process_query_basic_info(guac_rdpdr_device* device, wStream*
|
|||||||
|
|
||||||
/* Reserved field must not be sent */
|
/* Reserved field must not be sent */
|
||||||
|
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_query_standard_info(guac_rdpdr_device* device, wStream* input_stream,
|
void guac_rdpdr_fs_process_query_standard_info(guac_rdp_common_svc* svc,
|
||||||
int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
wStream* output_stream;
|
wStream* output_stream;
|
||||||
guac_rdp_fs_file* file;
|
guac_rdp_fs_file* file;
|
||||||
BOOL is_directory = FALSE;
|
BOOL is_directory = FALSE;
|
||||||
|
|
||||||
/* Get file */
|
/* 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, iorequest->file_id);
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||||
"%s: [file_id=%i]",
|
iorequest->file_id);
|
||||||
__func__, file_id);
|
|
||||||
|
|
||||||
if (file->attributes & FILE_ATTRIBUTE_DIRECTORY)
|
if (file->attributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||||
is_directory = TRUE;
|
is_directory = TRUE;
|
||||||
|
|
||||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
STATUS_SUCCESS, 26);
|
iorequest->completion_id, STATUS_SUCCESS, 26);
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream, 22);
|
Stream_Write_UINT32(output_stream, 22);
|
||||||
Stream_Write_UINT64(output_stream, file->size); /* AllocationSize */
|
Stream_Write_UINT64(output_stream, file->size); /* AllocationSize */
|
||||||
@ -100,27 +94,27 @@ void guac_rdpdr_fs_process_query_standard_info(guac_rdpdr_device* device, wStrea
|
|||||||
|
|
||||||
/* Reserved field must not be sent */
|
/* Reserved field must not be sent */
|
||||||
|
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_query_attribute_tag_info(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_query_attribute_tag_info(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
wStream* output_stream;
|
wStream* output_stream;
|
||||||
guac_rdp_fs_file* file;
|
guac_rdp_fs_file* file;
|
||||||
|
|
||||||
/* Get file */
|
/* 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, iorequest->file_id);
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||||
"%s: [file_id=%i]",
|
iorequest->file_id);
|
||||||
__func__, file_id);
|
|
||||||
|
|
||||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
STATUS_SUCCESS, 12);
|
iorequest->completion_id, STATUS_SUCCESS, 12);
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream, 8);
|
Stream_Write_UINT32(output_stream, 8);
|
||||||
Stream_Write_UINT32(output_stream, file->attributes); /* FileAttributes */
|
Stream_Write_UINT32(output_stream, file->attributes); /* FileAttributes */
|
||||||
@ -128,12 +122,13 @@ void guac_rdpdr_fs_process_query_attribute_tag_info(guac_rdpdr_device* device,
|
|||||||
|
|
||||||
/* Reserved field must not be sent */
|
/* Reserved field must not be sent */
|
||||||
|
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_set_rename_info(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_set_rename_info(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id, int length) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
int length, wStream* input_stream) {
|
||||||
|
|
||||||
int result;
|
int result;
|
||||||
int filename_length;
|
int filename_length;
|
||||||
@ -149,9 +144,9 @@ void guac_rdpdr_fs_process_set_rename_info(guac_rdpdr_device* device,
|
|||||||
guac_rdp_utf16_to_utf8(Stream_Pointer(input_stream), filename_length/2,
|
guac_rdp_utf16_to_utf8(Stream_Pointer(input_stream), filename_length/2,
|
||||||
destination_path, sizeof(destination_path));
|
destination_path, sizeof(destination_path));
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]"
|
||||||
"%s: [file_id=%i] destination_path=\"%s\"",
|
"destination_path=\"%s\"", __func__, iorequest->file_id,
|
||||||
__func__, file_id, destination_path);
|
destination_path);
|
||||||
|
|
||||||
/* If file moving to \Download folder, start stream, do not move */
|
/* If file moving to \Download folder, start stream, do not move */
|
||||||
if (strncmp(destination_path, "\\Download\\", 10) == 0) {
|
if (strncmp(destination_path, "\\Download\\", 10) == 0) {
|
||||||
@ -159,38 +154,39 @@ void guac_rdpdr_fs_process_set_rename_info(guac_rdpdr_device* device,
|
|||||||
guac_rdp_fs_file* file;
|
guac_rdp_fs_file* file;
|
||||||
|
|
||||||
/* Get file */
|
/* 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, iorequest->file_id);
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Initiate download, pretend move succeeded */
|
/* Initiate download, pretend move succeeded */
|
||||||
guac_rdpdr_start_download(device, file->absolute_path);
|
guac_client_for_owner(svc->client, guac_rdp_download_to_user, file->absolute_path);
|
||||||
output_stream = guac_rdpdr_new_io_completion(device,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
completion_id, STATUS_SUCCESS, 4);
|
iorequest->completion_id, STATUS_SUCCESS, 4);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise, rename as requested */
|
/* Otherwise, rename as requested */
|
||||||
else {
|
else {
|
||||||
|
|
||||||
result = guac_rdp_fs_rename((guac_rdp_fs*) device->data, file_id,
|
result = guac_rdp_fs_rename((guac_rdp_fs*) device->data,
|
||||||
destination_path);
|
iorequest->file_id, destination_path);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
output_stream = guac_rdpdr_new_io_completion(device,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
completion_id, guac_rdp_fs_get_status(result), 4);
|
iorequest->completion_id, guac_rdp_fs_get_status(result), 4);
|
||||||
else
|
else
|
||||||
output_stream = guac_rdpdr_new_io_completion(device,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
completion_id, STATUS_SUCCESS, 4);
|
iorequest->completion_id, STATUS_SUCCESS, 4);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream, length);
|
Stream_Write_UINT32(output_stream, length);
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_set_allocation_info(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_set_allocation_info(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id, int length) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
int length, wStream* input_stream) {
|
||||||
|
|
||||||
int result;
|
int result;
|
||||||
UINT64 size;
|
UINT64 size;
|
||||||
@ -199,50 +195,50 @@ void guac_rdpdr_fs_process_set_allocation_info(guac_rdpdr_device* device,
|
|||||||
/* Read new size */
|
/* Read new size */
|
||||||
Stream_Read_UINT64(input_stream, size); /* AllocationSize */
|
Stream_Read_UINT64(input_stream, size); /* AllocationSize */
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] "
|
||||||
"%s: [file_id=%i] size=%" PRIu64,
|
"size=%" PRIu64, __func__, iorequest->file_id, (uint64_t) size);
|
||||||
__func__, file_id, (uint64_t) size);
|
|
||||||
|
|
||||||
/* Truncate file */
|
/* Truncate file */
|
||||||
result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, file_id, size);
|
result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, iorequest->file_id, size);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
output_stream = guac_rdpdr_new_io_completion(device,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
completion_id, guac_rdp_fs_get_status(result), 4);
|
iorequest->completion_id, guac_rdp_fs_get_status(result), 4);
|
||||||
else
|
else
|
||||||
output_stream = guac_rdpdr_new_io_completion(device,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
completion_id, STATUS_SUCCESS, 4);
|
iorequest->completion_id, STATUS_SUCCESS, 4);
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream, length);
|
Stream_Write_UINT32(output_stream, length);
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_set_disposition_info(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_set_disposition_info(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id, int length) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
int length, wStream* input_stream) {
|
||||||
|
|
||||||
wStream* output_stream;
|
wStream* output_stream;
|
||||||
|
|
||||||
/* Delete file */
|
/* Delete file */
|
||||||
int result = guac_rdp_fs_delete((guac_rdp_fs*) device->data, file_id);
|
int result = guac_rdp_fs_delete((guac_rdp_fs*) device->data, iorequest->file_id);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
output_stream = guac_rdpdr_new_io_completion(device,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
completion_id, guac_rdp_fs_get_status(result), 4);
|
iorequest->completion_id, guac_rdp_fs_get_status(result), 4);
|
||||||
else
|
else
|
||||||
output_stream = guac_rdpdr_new_io_completion(device,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
completion_id, STATUS_SUCCESS, 4);
|
iorequest->completion_id, STATUS_SUCCESS, 4);
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||||
"%s: [file_id=%i]",
|
iorequest->file_id);
|
||||||
__func__, file_id);
|
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream, length);
|
Stream_Write_UINT32(output_stream, length);
|
||||||
|
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_set_end_of_file_info(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_set_end_of_file_info(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id, int length) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
int length, wStream* input_stream) {
|
||||||
|
|
||||||
int result;
|
int result;
|
||||||
UINT64 size;
|
UINT64 size;
|
||||||
@ -251,38 +247,37 @@ void guac_rdpdr_fs_process_set_end_of_file_info(guac_rdpdr_device* device,
|
|||||||
/* Read new size */
|
/* Read new size */
|
||||||
Stream_Read_UINT64(input_stream, size); /* AllocationSize */
|
Stream_Read_UINT64(input_stream, size); /* AllocationSize */
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] "
|
||||||
"%s: [file_id=%i] size=%" PRIu64,
|
"size=%" PRIu64, __func__, iorequest->file_id, (uint64_t) size);
|
||||||
__func__, file_id, (uint64_t) size);
|
|
||||||
|
|
||||||
/* Truncate file */
|
/* Truncate file */
|
||||||
result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, file_id, size);
|
result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, iorequest->file_id, size);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
output_stream = guac_rdpdr_new_io_completion(device,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
completion_id, guac_rdp_fs_get_status(result), 4);
|
iorequest->completion_id, guac_rdp_fs_get_status(result), 4);
|
||||||
else
|
else
|
||||||
output_stream = guac_rdpdr_new_io_completion(device,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
completion_id, STATUS_SUCCESS, 4);
|
iorequest->completion_id, STATUS_SUCCESS, 4);
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream, length);
|
Stream_Write_UINT32(output_stream, length);
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_set_basic_info(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_set_basic_info(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id, int length) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
int length, wStream* input_stream) {
|
||||||
|
|
||||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
completion_id, STATUS_SUCCESS, 4);
|
iorequest->completion_id, STATUS_SUCCESS, 4);
|
||||||
|
|
||||||
/* Currently do nothing, just respond */
|
/* Currently do nothing, just respond */
|
||||||
Stream_Write_UINT32(output_stream, length);
|
Stream_Write_UINT32(output_stream, length);
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] IGNORED",
|
||||||
"%s: [file_id=%i] IGNORED",
|
__func__, iorequest->file_id);
|
||||||
__func__, file_id);
|
|
||||||
|
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -17,85 +17,103 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_FILE_INFO_H
|
||||||
#ifndef __GUAC_RDPDR_FS_MESSAGES_FILE_INFO_H
|
#define GUAC_RDP_CHANNELS_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
|
* Handlers for file queries received over the RDPDR channel via the
|
||||||
* IRP_MJ_QUERY_INFORMATION major function.
|
* IRP_MJ_QUERY_INFORMATION major function.
|
||||||
*
|
*
|
||||||
* @file rdpdr_fs_messages_file_info.h
|
* @file rdpdr-fs-messages-file-info.h
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/common-svc.h"
|
||||||
|
#include "channels/rdpdr/rdpdr.h"
|
||||||
|
|
||||||
#include "rdpdr_service.h"
|
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-stream.h"
|
/**
|
||||||
#endif
|
* 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
|
* Processes a query for FileBasicInformation. From the documentation, this is
|
||||||
* "used to query a file for the times of creation, last access, last write,
|
* "used to query a file for the times of creation, last access, last write,
|
||||||
* and change, in addition to file attribute information."
|
* and change, in addition to file attribute information."
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_query_basic_info(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_basic_info;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a query for FileStandardInformation. From the documentation, this
|
* Processes a query for FileStandardInformation. From the documentation, this
|
||||||
* is "used to query for file information such as allocation size, end-of-file
|
* is "used to query for file information such as allocation size, end-of-file
|
||||||
* position, and number of links."
|
* position, and number of links."
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_query_standard_info(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_standard_info;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a query for FileAttributeTagInformation. From the documentation
|
* Processes a query for FileAttributeTagInformation. From the documentation
|
||||||
* this is "used to query for file attribute and reparse tag information."
|
* 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,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_attribute_tag_info;
|
||||||
wStream* input_stream, int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a set operation for FileRenameInformation. From the documentation,
|
* Process a set operation for FileRenameInformation. From the documentation,
|
||||||
* this operation is used to rename a file.
|
* this operation is used to rename a file.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_set_rename_info(guac_rdpdr_device* device,
|
guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_rename_info;
|
||||||
wStream* input_stream, int file_id, int completion_id, int length);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a set operation for FileAllocationInformation. From the
|
* Process a set operation for FileAllocationInformation. From the
|
||||||
* documentation, this operation is used to set a file's allocation size.
|
* documentation, this operation is used to set a file's allocation size.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_set_allocation_info(guac_rdpdr_device* device,
|
guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_allocation_info;
|
||||||
wStream* input_stream, int file_id, int completion_id, int length);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a set operation for FileDispositionInformation. From the
|
* Process a set operation for FileDispositionInformation. From the
|
||||||
* documentation, this operation is used to mark a file for deletion.
|
* documentation, this operation is used to mark a file for deletion.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_set_disposition_info(guac_rdpdr_device* device,
|
guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_disposition_info;
|
||||||
wStream* input_stream, int file_id, int completion_id, int length);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a set operation for FileEndOfFileInformation. From the
|
* Process a set operation for FileEndOfFileInformation. From the
|
||||||
* documentation, this operation is used "to set end-of-file information for
|
* documentation, this operation is used "to set end-of-file information for
|
||||||
* a file."
|
* a file."
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_set_end_of_file_info(guac_rdpdr_device* device,
|
guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_end_of_file_info;
|
||||||
wStream* input_stream, int file_id, int completion_id, int length);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a set operation for FileBasicInformation. From the documentation,
|
* Process a set operation for FileBasicInformation. From the documentation,
|
||||||
* this is "used to set file information such as the times of creation, last
|
* this is "used to set file information such as the times of creation, last
|
||||||
* access, last write, and change, in addition to file attributes."
|
* access, last write, and change, in addition to file attributes."
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_set_basic_info(guac_rdpdr_device* device,
|
guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_basic_info;
|
||||||
wStream* input_stream, int file_id, int completion_id, int length);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -17,33 +17,29 @@
|
|||||||
* under the License.
|
* 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 <guacamole/client.h>
|
||||||
#include "rdpdr_service.h"
|
|
||||||
#include "rdp_fs.h"
|
|
||||||
#include "rdp_status.h"
|
|
||||||
|
|
||||||
#include <freerdp/utils/svc_plugin.h>
|
|
||||||
#include <guacamole/unicode.h>
|
#include <guacamole/unicode.h>
|
||||||
|
#include <winpr/file.h>
|
||||||
#ifdef ENABLE_WINPR
|
#include <winpr/io.h>
|
||||||
|
#include <winpr/nt.h>
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#include <winpr/wtypes.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,
|
void guac_rdpdr_fs_process_query_volume_info(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
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,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||||
"%s: [file_id=%i]",
|
iorequest->file_id);
|
||||||
__func__, file_id);
|
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream, 17 + GUAC_FILESYSTEM_LABEL_LENGTH);
|
Stream_Write_UINT32(output_stream, 17 + GUAC_FILESYSTEM_LABEL_LENGTH);
|
||||||
Stream_Write_UINT64(output_stream, 0); /* VolumeCreationTime */
|
Stream_Write_UINT64(output_stream, 0); /* VolumeCreationTime */
|
||||||
@ -53,22 +49,22 @@ void guac_rdpdr_fs_process_query_volume_info(guac_rdpdr_device* device,
|
|||||||
/* Reserved field must not be sent */
|
/* Reserved field must not be sent */
|
||||||
Stream_Write(output_stream, GUAC_FILESYSTEM_LABEL, GUAC_FILESYSTEM_LABEL_LENGTH);
|
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,
|
void guac_rdpdr_fs_process_query_size_info(guac_rdp_common_svc* svc,
|
||||||
int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
guac_rdp_fs_info info = {0};
|
guac_rdp_fs_info info = {0};
|
||||||
guac_rdp_fs_get_info((guac_rdp_fs*) device->data, &info);
|
guac_rdp_fs_get_info((guac_rdp_fs*) device->data, &info);
|
||||||
|
|
||||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
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,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||||
"%s: [file_id=%i]",
|
iorequest->file_id);
|
||||||
__func__, file_id);
|
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream, 24);
|
Stream_Write_UINT32(output_stream, 24);
|
||||||
Stream_Write_UINT64(output_stream, info.blocks_total); /* TotalAllocationUnits */
|
Stream_Write_UINT64(output_stream, info.blocks_total); /* TotalAllocationUnits */
|
||||||
@ -76,39 +72,39 @@ void guac_rdpdr_fs_process_query_size_info(guac_rdpdr_device* device, wStream* i
|
|||||||
Stream_Write_UINT32(output_stream, 1); /* SectorsPerAllocationUnit */
|
Stream_Write_UINT32(output_stream, 1); /* SectorsPerAllocationUnit */
|
||||||
Stream_Write_UINT32(output_stream, info.block_size); /* BytesPerSector */
|
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,
|
void guac_rdpdr_fs_process_query_device_info(guac_rdp_common_svc* svc,
|
||||||
int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
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,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||||
"%s: [file_id=%i]",
|
iorequest->file_id);
|
||||||
__func__, file_id);
|
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream, 8);
|
Stream_Write_UINT32(output_stream, 8);
|
||||||
Stream_Write_UINT32(output_stream, FILE_DEVICE_DISK); /* DeviceType */
|
Stream_Write_UINT32(output_stream, FILE_DEVICE_DISK); /* DeviceType */
|
||||||
Stream_Write_UINT32(output_stream, 0); /* Characteristics */
|
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,
|
void guac_rdpdr_fs_process_query_attribute_info(guac_rdp_common_svc* svc,
|
||||||
int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
int name_len = guac_utf8_strlen(device->device_name);
|
int name_len = guac_utf8_strlen(device->device_name);
|
||||||
|
|
||||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
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,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||||
"%s: [file_id=%i]",
|
iorequest->file_id);
|
||||||
__func__, file_id);
|
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream, 12 + name_len);
|
Stream_Write_UINT32(output_stream, 12 + name_len);
|
||||||
Stream_Write_UINT32(output_stream,
|
Stream_Write_UINT32(output_stream,
|
||||||
@ -119,22 +115,22 @@ void guac_rdpdr_fs_process_query_attribute_info(guac_rdpdr_device* device, wStre
|
|||||||
Stream_Write_UINT32(output_stream, name_len);
|
Stream_Write_UINT32(output_stream, name_len);
|
||||||
Stream_Write(output_stream, device->device_name, 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,
|
void guac_rdpdr_fs_process_query_full_size_info(guac_rdp_common_svc* svc,
|
||||||
int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
guac_rdp_fs_info info = {0};
|
guac_rdp_fs_info info = {0};
|
||||||
guac_rdp_fs_get_info((guac_rdp_fs*) device->data, &info);
|
guac_rdp_fs_get_info((guac_rdp_fs*) device->data, &info);
|
||||||
|
|
||||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
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,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||||
"%s: [file_id=%i]",
|
iorequest->file_id);
|
||||||
__func__, file_id);
|
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream, 32);
|
Stream_Write_UINT32(output_stream, 32);
|
||||||
Stream_Write_UINT64(output_stream, info.blocks_total); /* TotalAllocationUnits */
|
Stream_Write_UINT64(output_stream, info.blocks_total); /* TotalAllocationUnits */
|
||||||
@ -143,7 +139,7 @@ void guac_rdpdr_fs_process_query_full_size_info(guac_rdpdr_device* device, wStre
|
|||||||
Stream_Write_UINT32(output_stream, 1); /* SectorsPerAllocationUnit */
|
Stream_Write_UINT32(output_stream, 1); /* SectorsPerAllocationUnit */
|
||||||
Stream_Write_UINT32(output_stream, info.block_size); /* BytesPerSector */
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -17,59 +17,47 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_VOL_INFO_H
|
||||||
#ifndef __GUAC_RDPDR_FS_MESSAGES_VOL_INFO_H
|
#define GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_VOL_INFO_H
|
||||||
#define __GUAC_RDPDR_FS_MESSAGES_VOL_INFO_H
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handlers for directory queries received over the RDPDR channel via the
|
* Handlers for directory queries received over the RDPDR channel via the
|
||||||
* IRP_MJ_DIRECTORY_CONTROL major function and the IRP_MN_QUERY_DIRECTORY minor
|
* IRP_MJ_DIRECTORY_CONTROL major function and the IRP_MN_QUERY_DIRECTORY minor
|
||||||
* function.
|
* 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>
|
#include <winpr/stream.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-stream.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a query request for FileFsVolumeInformation. According to the
|
* Processes a query request for FileFsVolumeInformation. According to the
|
||||||
* documentation, this is "used to query information for a volume on which a
|
* documentation, this is "used to query information for a volume on which a
|
||||||
* file system is mounted."
|
* file system is mounted."
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_query_volume_info(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_volume_info;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a query request for FileFsSizeInformation.
|
* Processes a query request for FileFsSizeInformation.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_query_size_info(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_size_info;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a query request for FileFsAttributeInformation.
|
* Processes a query request for FileFsAttributeInformation.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_query_attribute_info(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_attribute_info;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a query request for FileFsFullSizeInformation.
|
* Processes a query request for FileFsFullSizeInformation.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_query_full_size_info(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_full_size_info;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a query request for FileFsDeviceInformation.
|
* Processes a query request for FileFsDeviceInformation.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_query_device_info(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_device_info;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -17,36 +17,29 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/common-svc.h"
|
||||||
|
#include "channels/rdpdr/rdpdr-fs-messages-dir-info.h"
|
||||||
#include "rdpdr_fs_messages_dir_info.h"
|
#include "channels/rdpdr/rdpdr-fs-messages-file-info.h"
|
||||||
#include "rdpdr_fs_messages_file_info.h"
|
#include "channels/rdpdr/rdpdr-fs-messages-vol-info.h"
|
||||||
#include "rdpdr_fs_messages.h"
|
#include "channels/rdpdr/rdpdr-fs-messages.h"
|
||||||
#include "rdpdr_fs_messages_vol_info.h"
|
#include "channels/rdpdr/rdpdr.h"
|
||||||
#include "rdpdr_messages.h"
|
#include "download.h"
|
||||||
#include "rdpdr_service.h"
|
#include "fs.h"
|
||||||
#include "rdp_fs.h"
|
|
||||||
#include "rdp_status.h"
|
|
||||||
#include "unicode.h"
|
#include "unicode.h"
|
||||||
|
|
||||||
#include <freerdp/utils/svc_plugin.h>
|
#include <freerdp/channels/rdpdr.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
#include <winpr/nt.h>
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#include <winpr/wtypes.h>
|
#include <winpr/wtypes.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-stream.h"
|
|
||||||
#include "compat/winpr-wtypes.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_create(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
wStream* output_stream;
|
wStream* output_stream;
|
||||||
int file_id;
|
int file_id;
|
||||||
@ -73,7 +66,7 @@ void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
|
|||||||
desired_access, file_attributes,
|
desired_access, file_attributes,
|
||||||
create_disposition, create_options);
|
create_disposition, create_options);
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||||
"%s: [file_id=%i] "
|
"%s: [file_id=%i] "
|
||||||
"desired_access=0x%x, file_attributes=0x%x, "
|
"desired_access=0x%x, file_attributes=0x%x, "
|
||||||
"create_disposition=0x%x, create_options=0x%x, path=\"%s\"",
|
"create_disposition=0x%x, create_options=0x%x, path=\"%s\"",
|
||||||
@ -83,11 +76,11 @@ void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
|
|||||||
|
|
||||||
/* If an error occurred, notify server */
|
/* If an error occurred, notify server */
|
||||||
if (file_id < 0) {
|
if (file_id < 0) {
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
|
guac_client_log(svc->client, GUAC_LOG_ERROR,
|
||||||
"File open refused (%i): \"%s\"", file_id, path);
|
"File open refused (%i): \"%s\"", file_id, path);
|
||||||
|
|
||||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
guac_rdp_fs_get_status(file_id), 5);
|
iorequest->completion_id, guac_rdp_fs_get_status(file_id), 5);
|
||||||
Stream_Write_UINT32(output_stream, 0); /* fileId */
|
Stream_Write_UINT32(output_stream, 0); /* fileId */
|
||||||
Stream_Write_UINT8(output_stream, 0); /* information */
|
Stream_Write_UINT8(output_stream, 0); /* information */
|
||||||
}
|
}
|
||||||
@ -97,8 +90,8 @@ void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
|
|||||||
|
|
||||||
guac_rdp_fs_file* file;
|
guac_rdp_fs_file* file;
|
||||||
|
|
||||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
STATUS_SUCCESS, 5);
|
iorequest->completion_id, STATUS_SUCCESS, 5);
|
||||||
Stream_Write_UINT32(output_stream, file_id); /* fileId */
|
Stream_Write_UINT32(output_stream, file_id); /* fileId */
|
||||||
Stream_Write_UINT8(output_stream, 0); /* information */
|
Stream_Write_UINT8(output_stream, 0); /* information */
|
||||||
|
|
||||||
@ -107,8 +100,7 @@ void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
|
|||||||
if (file != NULL && strcmp(file->absolute_path, "\\") == 0) {
|
if (file != NULL && strcmp(file->absolute_path, "\\") == 0) {
|
||||||
int download_id =
|
int download_id =
|
||||||
guac_rdp_fs_open((guac_rdp_fs*) device->data, "\\Download",
|
guac_rdp_fs_open((guac_rdp_fs*) device->data, "\\Download",
|
||||||
ACCESS_GENERIC_READ, 0,
|
GENERIC_READ, 0, FILE_OPEN_IF, FILE_DIRECTORY_FILE);
|
||||||
DISP_FILE_OPEN_IF, FILE_DIRECTORY_FILE);
|
|
||||||
|
|
||||||
if (download_id >= 0)
|
if (download_id >= 0)
|
||||||
guac_rdp_fs_close((guac_rdp_fs*) device->data, download_id);
|
guac_rdp_fs_close((guac_rdp_fs*) device->data, download_id);
|
||||||
@ -116,12 +108,13 @@ void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_read(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_read(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
UINT32 length;
|
UINT32 length;
|
||||||
UINT64 offset;
|
UINT64 offset;
|
||||||
@ -134,9 +127,9 @@ void guac_rdpdr_fs_process_read(guac_rdpdr_device* device,
|
|||||||
Stream_Read_UINT32(input_stream, length);
|
Stream_Read_UINT32(input_stream, length);
|
||||||
Stream_Read_UINT64(input_stream, offset);
|
Stream_Read_UINT64(input_stream, offset);
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||||
"%s: [file_id=%i] length=%i, offset=%" PRIu64,
|
"%s: [file_id=%i] length=%i, offset=%" PRIu64,
|
||||||
__func__, file_id, length, (uint64_t) offset);
|
__func__, iorequest->file_id, length, (uint64_t) offset);
|
||||||
|
|
||||||
/* Ensure buffer size does not exceed a safe maximum */
|
/* Ensure buffer size does not exceed a safe maximum */
|
||||||
if (length > GUAC_RDP_MAX_READ_BUFFER)
|
if (length > GUAC_RDP_MAX_READ_BUFFER)
|
||||||
@ -146,31 +139,32 @@ void guac_rdpdr_fs_process_read(guac_rdpdr_device* device,
|
|||||||
buffer = malloc(length);
|
buffer = malloc(length);
|
||||||
|
|
||||||
/* Attempt read */
|
/* Attempt read */
|
||||||
bytes_read = guac_rdp_fs_read((guac_rdp_fs*) device->data, file_id, offset,
|
bytes_read = guac_rdp_fs_read((guac_rdp_fs*) device->data,
|
||||||
buffer, length);
|
iorequest->file_id, offset, buffer, length);
|
||||||
|
|
||||||
/* If error, return invalid parameter */
|
/* If error, return invalid parameter */
|
||||||
if (bytes_read < 0) {
|
if (bytes_read < 0) {
|
||||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
guac_rdp_fs_get_status(bytes_read), 4);
|
iorequest->completion_id, guac_rdp_fs_get_status(bytes_read), 4);
|
||||||
Stream_Write_UINT32(output_stream, 0); /* Length */
|
Stream_Write_UINT32(output_stream, 0); /* Length */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise, send bytes read */
|
/* Otherwise, send bytes read */
|
||||||
else {
|
else {
|
||||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
STATUS_SUCCESS, 4+bytes_read);
|
iorequest->completion_id, STATUS_SUCCESS, 4+bytes_read);
|
||||||
Stream_Write_UINT32(output_stream, bytes_read); /* Length */
|
Stream_Write_UINT32(output_stream, bytes_read); /* Length */
|
||||||
Stream_Write(output_stream, buffer, bytes_read); /* ReadData */
|
Stream_Write(output_stream, buffer, bytes_read); /* ReadData */
|
||||||
}
|
}
|
||||||
|
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
free(buffer);
|
free(buffer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_write(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_write(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
UINT32 length;
|
UINT32 length;
|
||||||
UINT64 offset;
|
UINT64 offset;
|
||||||
@ -183,69 +177,70 @@ void guac_rdpdr_fs_process_write(guac_rdpdr_device* device,
|
|||||||
Stream_Read_UINT64(input_stream, offset);
|
Stream_Read_UINT64(input_stream, offset);
|
||||||
Stream_Seek(input_stream, 20); /* Padding */
|
Stream_Seek(input_stream, 20); /* Padding */
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||||
"%s: [file_id=%i] length=%i, offset=%" PRIu64,
|
"%s: [file_id=%i] length=%i, offset=%" PRIu64,
|
||||||
__func__, file_id, length, (uint64_t) offset);
|
__func__, iorequest->file_id, length, (uint64_t) offset);
|
||||||
|
|
||||||
/* Attempt write */
|
/* Attempt write */
|
||||||
bytes_written = guac_rdp_fs_write((guac_rdp_fs*) device->data, file_id,
|
bytes_written = guac_rdp_fs_write((guac_rdp_fs*) device->data,
|
||||||
offset, Stream_Pointer(input_stream), length);
|
iorequest->file_id, offset, Stream_Pointer(input_stream), length);
|
||||||
|
|
||||||
/* If error, return invalid parameter */
|
/* If error, return invalid parameter */
|
||||||
if (bytes_written < 0) {
|
if (bytes_written < 0) {
|
||||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
guac_rdp_fs_get_status(bytes_written), 5);
|
iorequest->completion_id, guac_rdp_fs_get_status(bytes_written), 5);
|
||||||
Stream_Write_UINT32(output_stream, 0); /* Length */
|
Stream_Write_UINT32(output_stream, 0); /* Length */
|
||||||
Stream_Write_UINT8(output_stream, 0); /* Padding */
|
Stream_Write_UINT8(output_stream, 0); /* Padding */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise, send success */
|
/* Otherwise, send success */
|
||||||
else {
|
else {
|
||||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
STATUS_SUCCESS, 5);
|
iorequest->completion_id, STATUS_SUCCESS, 5);
|
||||||
Stream_Write_UINT32(output_stream, bytes_written); /* Length */
|
Stream_Write_UINT32(output_stream, bytes_written); /* Length */
|
||||||
Stream_Write_UINT8(output_stream, 0); /* Padding */
|
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_fs_process_close(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_close(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
wStream* output_stream;
|
wStream* output_stream;
|
||||||
guac_rdp_fs_file* file;
|
guac_rdp_fs_file* file;
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]",
|
||||||
"%s: [file_id=%i]",
|
__func__, iorequest->file_id);
|
||||||
__func__, file_id);
|
|
||||||
|
|
||||||
/* Get file */
|
/* 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, iorequest->file_id);
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* If file was written to, and it's in the \Download folder, start stream */
|
/* If file was written to, and it's in the \Download folder, start stream */
|
||||||
if (file->bytes_written > 0 &&
|
if (file->bytes_written > 0 &&
|
||||||
strncmp(file->absolute_path, "\\Download\\", 10) == 0) {
|
strncmp(file->absolute_path, "\\Download\\", 10) == 0) {
|
||||||
guac_rdpdr_start_download(device, file->absolute_path);
|
guac_client_for_owner(svc->client, guac_rdp_download_to_user, file->absolute_path);
|
||||||
guac_rdp_fs_delete((guac_rdp_fs*) device->data, file_id);
|
guac_rdp_fs_delete((guac_rdp_fs*) device->data, iorequest->file_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Close file */
|
/* Close file */
|
||||||
guac_rdp_fs_close((guac_rdp_fs*) device->data, file_id);
|
guac_rdp_fs_close((guac_rdp_fs*) device->data, iorequest->file_id);
|
||||||
|
|
||||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
STATUS_SUCCESS, 4);
|
iorequest->completion_id, STATUS_SUCCESS, 4);
|
||||||
Stream_Write(output_stream, "\0\0\0\0", 4); /* Padding */
|
Stream_Write(output_stream, "\0\0\0\0", 4); /* Padding */
|
||||||
|
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_volume_info(guac_rdpdr_device* device, wStream* input_stream,
|
void guac_rdpdr_fs_process_volume_info(guac_rdp_common_svc* svc,
|
||||||
int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
int fs_information_class;
|
int fs_information_class;
|
||||||
|
|
||||||
@ -255,39 +250,35 @@ void guac_rdpdr_fs_process_volume_info(guac_rdpdr_device* device, wStream* input
|
|||||||
switch (fs_information_class) {
|
switch (fs_information_class) {
|
||||||
|
|
||||||
case FileFsVolumeInformation:
|
case FileFsVolumeInformation:
|
||||||
guac_rdpdr_fs_process_query_volume_info(device, input_stream,
|
guac_rdpdr_fs_process_query_volume_info(svc, device, iorequest, input_stream);
|
||||||
file_id, completion_id);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileFsSizeInformation:
|
case FileFsSizeInformation:
|
||||||
guac_rdpdr_fs_process_query_size_info(device, input_stream,
|
guac_rdpdr_fs_process_query_size_info(svc, device, iorequest, input_stream);
|
||||||
file_id, completion_id);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileFsDeviceInformation:
|
case FileFsDeviceInformation:
|
||||||
guac_rdpdr_fs_process_query_device_info(device, input_stream,
|
guac_rdpdr_fs_process_query_device_info(svc, device, iorequest, input_stream);
|
||||||
file_id, completion_id);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileFsAttributeInformation:
|
case FileFsAttributeInformation:
|
||||||
guac_rdpdr_fs_process_query_attribute_info(device, input_stream,
|
guac_rdpdr_fs_process_query_attribute_info(svc, device, iorequest, input_stream);
|
||||||
file_id, completion_id);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileFsFullSizeInformation:
|
case FileFsFullSizeInformation:
|
||||||
guac_rdpdr_fs_process_query_full_size_info(device, input_stream,
|
guac_rdpdr_fs_process_query_full_size_info(svc, device, iorequest, input_stream);
|
||||||
file_id, completion_id);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||||
"Unknown volume information class: 0x%x", fs_information_class);
|
"Unknown volume information class: 0x%x", fs_information_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_file_info(guac_rdpdr_device* device, wStream* input_stream,
|
void guac_rdpdr_fs_process_file_info(guac_rdp_common_svc* svc,
|
||||||
int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
int fs_information_class;
|
int fs_information_class;
|
||||||
|
|
||||||
@ -297,43 +288,42 @@ void guac_rdpdr_fs_process_file_info(guac_rdpdr_device* device, wStream* input_s
|
|||||||
switch (fs_information_class) {
|
switch (fs_information_class) {
|
||||||
|
|
||||||
case FileBasicInformation:
|
case FileBasicInformation:
|
||||||
guac_rdpdr_fs_process_query_basic_info(device, input_stream,
|
guac_rdpdr_fs_process_query_basic_info(svc, device, iorequest, input_stream);
|
||||||
file_id, completion_id);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileStandardInformation:
|
case FileStandardInformation:
|
||||||
guac_rdpdr_fs_process_query_standard_info(device, input_stream,
|
guac_rdpdr_fs_process_query_standard_info(svc, device, iorequest, input_stream);
|
||||||
file_id, completion_id);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileAttributeTagInformation:
|
case FileAttributeTagInformation:
|
||||||
guac_rdpdr_fs_process_query_attribute_tag_info(device, input_stream,
|
guac_rdpdr_fs_process_query_attribute_tag_info(svc, device, iorequest, input_stream);
|
||||||
file_id, completion_id);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||||
"Unknown file information class: 0x%x", fs_information_class);
|
"Unknown file information class: 0x%x", fs_information_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_set_volume_info(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_set_volume_info(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
completion_id, STATUS_NOT_SUPPORTED, 0);
|
iorequest->completion_id, STATUS_NOT_SUPPORTED, 0);
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||||
"%s: [file_id=%i] Set volume info not supported",
|
"%s: [file_id=%i] Set volume info not supported",
|
||||||
__func__, file_id);
|
__func__, iorequest->file_id);
|
||||||
|
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_set_file_info(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_set_file_info(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
int fs_information_class;
|
int fs_information_class;
|
||||||
int length;
|
int length;
|
||||||
@ -346,66 +336,62 @@ void guac_rdpdr_fs_process_set_file_info(guac_rdpdr_device* device,
|
|||||||
switch (fs_information_class) {
|
switch (fs_information_class) {
|
||||||
|
|
||||||
case FileBasicInformation:
|
case FileBasicInformation:
|
||||||
guac_rdpdr_fs_process_set_basic_info(device, input_stream,
|
guac_rdpdr_fs_process_set_basic_info(svc, device, iorequest, length, input_stream);
|
||||||
file_id, completion_id, length);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileEndOfFileInformation:
|
case FileEndOfFileInformation:
|
||||||
guac_rdpdr_fs_process_set_end_of_file_info(device, input_stream,
|
guac_rdpdr_fs_process_set_end_of_file_info(svc, device, iorequest, length, input_stream);
|
||||||
file_id, completion_id, length);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileDispositionInformation:
|
case FileDispositionInformation:
|
||||||
guac_rdpdr_fs_process_set_disposition_info(device, input_stream,
|
guac_rdpdr_fs_process_set_disposition_info(svc, device, iorequest, length, input_stream);
|
||||||
file_id, completion_id, length);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileRenameInformation:
|
case FileRenameInformation:
|
||||||
guac_rdpdr_fs_process_set_rename_info(device, input_stream,
|
guac_rdpdr_fs_process_set_rename_info(svc, device, iorequest, length, input_stream);
|
||||||
file_id, completion_id, length);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileAllocationInformation:
|
case FileAllocationInformation:
|
||||||
guac_rdpdr_fs_process_set_allocation_info(device, input_stream,
|
guac_rdpdr_fs_process_set_allocation_info(svc, device, iorequest, length, input_stream);
|
||||||
file_id, completion_id, length);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||||
"Unknown file information class: 0x%x",
|
"Unknown file information class: 0x%x",
|
||||||
fs_information_class);
|
fs_information_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_device_control(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_device_control(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
completion_id, STATUS_INVALID_PARAMETER, 4);
|
iorequest->completion_id, STATUS_INVALID_PARAMETER, 4);
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] IGNORED",
|
||||||
"%s: [file_id=%i] IGNORED",
|
__func__, iorequest->file_id);
|
||||||
__func__, file_id);
|
|
||||||
|
|
||||||
/* No content for now */
|
/* No content for now */
|
||||||
Stream_Write_UINT32(output_stream, 0);
|
Stream_Write_UINT32(output_stream, 0);
|
||||||
|
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_notify_change_directory(guac_rdpdr_device* device,
|
void guac_rdpdr_fs_process_notify_change_directory(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] Not "
|
||||||
"%s: [file_id=%i] Not implemented",
|
"implemented", __func__, iorequest->file_id);
|
||||||
__func__, file_id);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* input_stream,
|
void guac_rdpdr_fs_process_query_directory(guac_rdp_common_svc* svc,
|
||||||
int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
wStream* output_stream;
|
wStream* output_stream;
|
||||||
|
|
||||||
@ -416,7 +402,7 @@ void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* i
|
|||||||
const char* entry_name;
|
const char* entry_name;
|
||||||
|
|
||||||
/* Get file */
|
/* 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, iorequest->file_id);
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -436,13 +422,13 @@ void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* i
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] "
|
||||||
"%s: [file_id=%i] initial_query=%i, dir_pattern=\"%s\"",
|
"initial_query=%i, dir_pattern=\"%s\"", __func__,
|
||||||
__func__, file_id, initial_query, file->dir_pattern);
|
iorequest->file_id, initial_query, file->dir_pattern);
|
||||||
|
|
||||||
/* Find first matching entry in directory */
|
/* Find first matching entry in directory */
|
||||||
while ((entry_name = guac_rdp_fs_read_dir((guac_rdp_fs*) device->data,
|
while ((entry_name = guac_rdp_fs_read_dir((guac_rdp_fs*) device->data,
|
||||||
file_id)) != NULL) {
|
iorequest->file_id)) != NULL) {
|
||||||
|
|
||||||
/* Convert to absolute path */
|
/* Convert to absolute path */
|
||||||
char entry_path[GUAC_RDP_FS_MAX_PATH];
|
char entry_path[GUAC_RDP_FS_MAX_PATH];
|
||||||
@ -457,7 +443,7 @@ void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* i
|
|||||||
|
|
||||||
/* Open directory entry */
|
/* Open directory entry */
|
||||||
entry_file_id = guac_rdp_fs_open((guac_rdp_fs*) device->data,
|
entry_file_id = guac_rdp_fs_open((guac_rdp_fs*) device->data,
|
||||||
entry_path, ACCESS_FILE_READ_DATA, 0, DISP_FILE_OPEN, 0);
|
entry_path, FILE_READ_DATA, 0, FILE_OPEN, 0);
|
||||||
|
|
||||||
if (entry_file_id >= 0) {
|
if (entry_file_id >= 0) {
|
||||||
|
|
||||||
@ -465,27 +451,27 @@ void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* i
|
|||||||
switch (fs_information_class) {
|
switch (fs_information_class) {
|
||||||
|
|
||||||
case FileDirectoryInformation:
|
case FileDirectoryInformation:
|
||||||
guac_rdpdr_fs_process_query_directory_info(device,
|
guac_rdpdr_fs_process_query_directory_info(svc, device,
|
||||||
entry_name, entry_file_id, completion_id);
|
iorequest, entry_name, entry_file_id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileFullDirectoryInformation:
|
case FileFullDirectoryInformation:
|
||||||
guac_rdpdr_fs_process_query_full_directory_info(device,
|
guac_rdpdr_fs_process_query_full_directory_info(svc,
|
||||||
entry_name, entry_file_id, completion_id);
|
device, iorequest, entry_name, entry_file_id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileBothDirectoryInformation:
|
case FileBothDirectoryInformation:
|
||||||
guac_rdpdr_fs_process_query_both_directory_info(device,
|
guac_rdpdr_fs_process_query_both_directory_info(svc,
|
||||||
entry_name, entry_file_id, completion_id);
|
device, iorequest, entry_name, entry_file_id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileNamesInformation:
|
case FileNamesInformation:
|
||||||
guac_rdpdr_fs_process_query_names_info(device,
|
guac_rdpdr_fs_process_query_names_info(svc, device,
|
||||||
entry_name, entry_file_id, completion_id);
|
iorequest, entry_name, entry_file_id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||||
"Unknown dir information class: 0x%x",
|
"Unknown dir information class: 0x%x",
|
||||||
fs_information_class);
|
fs_information_class);
|
||||||
}
|
}
|
||||||
@ -501,29 +487,30 @@ void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* i
|
|||||||
* Handle errors as a lack of files.
|
* Handle errors as a lack of files.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
STATUS_NO_MORE_FILES, 5);
|
iorequest->completion_id, STATUS_NO_MORE_FILES, 5);
|
||||||
|
|
||||||
Stream_Write_UINT32(output_stream, 0); /* Length */
|
Stream_Write_UINT32(output_stream, 0); /* Length */
|
||||||
Stream_Write_UINT8(output_stream, 0); /* Padding */
|
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_fs_process_lock_control(guac_rdpdr_device* device, wStream* input_stream,
|
void guac_rdpdr_fs_process_lock_control(guac_rdp_common_svc* svc,
|
||||||
int file_id, int completion_id) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||||
completion_id, STATUS_NOT_SUPPORTED, 5);
|
iorequest->completion_id, STATUS_NOT_SUPPORTED, 5);
|
||||||
|
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||||
"%s: [file_id=%i] Lock not supported",
|
"%s: [file_id=%i] Lock not supported",
|
||||||
__func__, file_id);
|
__func__, iorequest->file_id);
|
||||||
|
|
||||||
Stream_Zero(output_stream, 5); /* Padding */
|
Stream_Zero(output_stream, 5); /* Padding */
|
||||||
|
|
||||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -17,59 +17,47 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_H
|
||||||
#ifndef __GUAC_RDPDR_FS_MESSAGES_H
|
#define GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_H
|
||||||
#define __GUAC_RDPDR_FS_MESSAGES_H
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handlers for core drive I/O requests. Requests handled here may be simple
|
* Handlers for core drive I/O requests. Requests handled here may be simple
|
||||||
* messages handled directly, or more complex multi-type messages handled
|
* messages handled directly, or more complex multi-type messages handled
|
||||||
* elsewhere.
|
* elsewhere.
|
||||||
*
|
*
|
||||||
* @file rdpdr_fs_messages.h
|
* @file rdpdr-fs-messages.h
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/rdpdr/rdpdr.h"
|
||||||
|
|
||||||
#include "rdpdr_service.h"
|
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-stream.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Server Create Drive Request. Despite its name, this request opens
|
* Handles a Server Create Drive Request. Despite its name, this request opens
|
||||||
* a file.
|
* a file.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_create;
|
||||||
wStream* input_stream, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Server Close Drive Reqiest. This request closes an open file.
|
* Handles a Server Close Drive Request. This request closes an open file.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_close(guac_rdpdr_device* device,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_close;
|
||||||
wStream* input_stream, int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Server Drive Read Request. This request reads from a file.
|
* Handles a Server Drive Read Request. This request reads from a file.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_read(guac_rdpdr_device* device,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_read;
|
||||||
wStream* input_stream, int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Server Drive Write Request. This request writes to a file.
|
* Handles a Server Drive Write Request. This request writes to a file.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_write(guac_rdpdr_device* device,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_write;
|
||||||
wStream* input_stream, int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Server Drive Control Request. This request handles one of any
|
* Handles a Server Drive Control Request. This request handles one of any
|
||||||
* number of Windows FSCTL_* control functions.
|
* number of Windows FSCTL_* control functions.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_device_control(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_device_control;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Server Drive Query Volume Information Request. This request
|
* Handles a Server Drive Query Volume Information Request. This request
|
||||||
@ -77,53 +65,46 @@ void guac_rdpdr_fs_process_device_control(guac_rdpdr_device* device, wStream* in
|
|||||||
* has several query types which have their own handlers defined in a
|
* has several query types which have their own handlers defined in a
|
||||||
* separate file.
|
* separate file.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_volume_info(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_volume_info;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Server Drive Set Volume Information Request. Currently, this
|
* Handles a Server Drive Set Volume Information Request. Currently, this
|
||||||
* RDPDR implementation does not support setting of volume information.
|
* RDPDR implementation does not support setting of volume information.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_set_volume_info(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_set_volume_info;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Server Drive Query Information Request. This request queries
|
* Handles a Server Drive Query Information Request. This request queries
|
||||||
* information about a specific file. This request has several query types
|
* information about a specific file. This request has several query types
|
||||||
* which have their own handlers defined in a separate file.
|
* which have their own handlers defined in a separate file.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_file_info(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_file_info;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Server Drive Set Information Request. This request sets
|
* Handles a Server Drive Set Information Request. This request sets
|
||||||
* information about a specific file. Currently, this RDPDR implementation does
|
* information about a specific file. Currently, this RDPDR implementation does
|
||||||
* not support setting of file information.
|
* not support setting of file information.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_set_file_info(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_set_file_info;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Server Drive Query Directory Request. This request queries
|
* Handles a Server Drive Query Directory Request. This request queries
|
||||||
* information about a specific directory. This request has several query types
|
* information about a specific directory. This request has several query types
|
||||||
* which have their own handlers defined in a separate file.
|
* which have their own handlers defined in a separate file.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_directory;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Server Drive NotifyChange Directory Request. This request requests
|
* Handles a Server Drive NotifyChange Directory Request. This request requests
|
||||||
* directory change notification.
|
* directory change notification.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_notify_change_directory(guac_rdpdr_device* device,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_notify_change_directory;
|
||||||
wStream* input_stream, int file_id, int completion_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Server Drive Lock Control Request. This request locks or unlocks
|
* Handles a Server Drive Lock Control Request. This request locks or unlocks
|
||||||
* portions of a file.
|
* portions of a file.
|
||||||
*/
|
*/
|
||||||
void guac_rdpdr_fs_process_lock_control(guac_rdpdr_device* device, wStream* input_stream,
|
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_lock_control;
|
||||||
int file_id, int completion_id);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -17,119 +17,116 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "channels/rdpdr/rdpdr-fs.h"
|
||||||
#include "config.h"
|
#include "channels/rdpdr/rdpdr-fs-messages.h"
|
||||||
|
#include "channels/rdpdr/rdpdr.h"
|
||||||
#include "rdp.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/client.h>
|
||||||
#include <guacamole/protocol.h>
|
|
||||||
#include <guacamole/socket.h>
|
|
||||||
#include <guacamole/unicode.h>
|
#include <guacamole/unicode.h>
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-stream.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void guac_rdpdr_device_fs_iorequest_handler(guac_rdpdr_device* device,
|
#include <stddef.h>
|
||||||
wStream* input_stream, int file_id, int completion_id, int major_func, int minor_func) {
|
|
||||||
|
|
||||||
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 */
|
/* File open */
|
||||||
case IRP_MJ_CREATE:
|
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;
|
break;
|
||||||
|
|
||||||
/* File close */
|
/* File close */
|
||||||
case IRP_MJ_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;
|
break;
|
||||||
|
|
||||||
/* File read */
|
/* File read */
|
||||||
case IRP_MJ_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;
|
break;
|
||||||
|
|
||||||
/* File write */
|
/* File write */
|
||||||
case IRP_MJ_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;
|
break;
|
||||||
|
|
||||||
/* Device control request (Windows FSCTL_ control codes) */
|
/* Device control request (Windows FSCTL_ control codes) */
|
||||||
case IRP_MJ_DEVICE_CONTROL:
|
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;
|
break;
|
||||||
|
|
||||||
/* Query volume (drive) information */
|
/* Query volume (drive) information */
|
||||||
case IRP_MJ_QUERY_VOLUME_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;
|
break;
|
||||||
|
|
||||||
/* Set volume (drive) information */
|
/* Set volume (drive) information */
|
||||||
case IRP_MJ_SET_VOLUME_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;
|
break;
|
||||||
|
|
||||||
/* Query file information */
|
/* Query file information */
|
||||||
case IRP_MJ_QUERY_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;
|
break;
|
||||||
|
|
||||||
/* Set file information */
|
/* Set file information */
|
||||||
case IRP_MJ_SET_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;
|
break;
|
||||||
|
|
||||||
case IRP_MJ_DIRECTORY_CONTROL:
|
case IRP_MJ_DIRECTORY_CONTROL:
|
||||||
|
|
||||||
/* Enumerate directory contents */
|
/* Enumerate directory contents */
|
||||||
if (minor_func == IRP_MN_QUERY_DIRECTORY)
|
if (iorequest->minor_func == IRP_MN_QUERY_DIRECTORY)
|
||||||
guac_rdpdr_fs_process_query_directory(device, input_stream, file_id, completion_id);
|
guac_rdpdr_fs_process_query_directory(svc, device, iorequest,
|
||||||
|
input_stream);
|
||||||
|
|
||||||
/* Request notification of changes to directory */
|
/* Request notification of changes to directory */
|
||||||
else if (minor_func == IRP_MN_NOTIFY_CHANGE_DIRECTORY)
|
else if (iorequest->minor_func == IRP_MN_NOTIFY_CHANGE_DIRECTORY)
|
||||||
guac_rdpdr_fs_process_notify_change_directory(device, input_stream,
|
guac_rdpdr_fs_process_notify_change_directory(svc, device,
|
||||||
file_id, completion_id);
|
iorequest, input_stream);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Lock/unlock portions of a file */
|
/* Lock/unlock portions of a file */
|
||||||
case IRP_MJ_LOCK_CONTROL:
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
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",
|
"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);
|
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_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
|
|
||||||
|
guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data;
|
||||||
int id = rdpdr->devices_registered++;
|
int id = rdpdr->devices_registered++;
|
||||||
|
|
||||||
/* Get new device */
|
/* Get new device */
|
||||||
guac_rdpdr_device* device = &(rdpdr->devices[id]);
|
guac_rdpdr_device* device = &(rdpdr->devices[id]);
|
||||||
|
|
||||||
/* Init device */
|
/* Init device */
|
||||||
device->rdpdr = rdpdr;
|
|
||||||
device->device_id = id;
|
device->device_id = id;
|
||||||
device->device_name = drive_name;
|
device->device_name = drive_name;
|
||||||
int device_name_len = guac_utf8_strlen(device->device_name);
|
int device_name_len = guac_utf8_strlen(device->device_name);
|
@ -17,9 +17,8 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_RDPDR_FS_H
|
||||||
#ifndef __GUAC_RDPDR_FS_H
|
#define GUAC_RDP_RDPDR_FS_H
|
||||||
#define __GUAC_RDPDR_FS_H
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Functions and macros specific to filesystem handling and initialization
|
* Functions and macros specific to filesystem handling and initialization
|
||||||
@ -29,26 +28,35 @@
|
|||||||
* system calls and structures, using the guac_rdpdr_device structure as a home
|
* system calls and structures, using the guac_rdpdr_device structure as a home
|
||||||
* for common data.
|
* for common data.
|
||||||
*
|
*
|
||||||
* @file rdpdr_fs.h
|
* @file rdpdr-fs.h
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/common-svc.h"
|
||||||
|
#include "channels/rdpdr/rdpdr.h"
|
||||||
#include "rdpdr_service.h"
|
|
||||||
|
|
||||||
#include <guacamole/pool.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
|
* Registers a new filesystem device within the RDPDR plugin. This must be done
|
||||||
* before RDPDR connection finishes.
|
* before RDPDR connection finishes.
|
||||||
*
|
*
|
||||||
* @param rdpdr
|
* @param svc
|
||||||
* The RDP device redirection plugin with which to register the device.
|
* The static virtual channel instance being used for RDPDR.
|
||||||
*
|
*
|
||||||
* @param drive_name
|
* @param drive_name
|
||||||
* The name of the redirected drive to display in the RDP connection.
|
* 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
|
#endif
|
||||||
|
|
349
src/protocols/rdp/channels/rdpdr/rdpdr-messages.c
Normal file
349
src/protocols/rdp/channels/rdpdr/rdpdr-messages.c
Normal file
@ -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");
|
||||||
|
}
|
136
src/protocols/rdp/channels/rdpdr/rdpdr-messages.h
Normal file
136
src/protocols/rdp/channels/rdpdr/rdpdr-messages.h
Normal file
@ -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
|
||||||
|
|
@ -17,41 +17,26 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/rdpdr/rdpdr-printer.h"
|
||||||
|
#include "channels/rdpdr/rdpdr.h"
|
||||||
#include "rdpdr_messages.h"
|
#include "print-job.h"
|
||||||
#include "rdpdr_printer.h"
|
|
||||||
#include "rdpdr_service.h"
|
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
#include "rdp_print_job.h"
|
|
||||||
#include "rdp_status.h"
|
|
||||||
#include "unicode.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/client.h>
|
||||||
#include <guacamole/protocol.h>
|
|
||||||
#include <guacamole/socket.h>
|
|
||||||
#include <guacamole/stream.h>
|
|
||||||
#include <guacamole/unicode.h>
|
#include <guacamole/unicode.h>
|
||||||
#include <guacamole/user.h>
|
#include <winpr/nt.h>
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/stream.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 <stdlib.h>
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
void guac_rdpdr_process_print_job_create(guac_rdpdr_device* device,
|
void guac_rdpdr_process_print_job_create(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int completion_id) {
|
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_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
|
|
||||||
/* Log creation of print job */
|
/* Log creation of print job */
|
||||||
@ -63,17 +48,18 @@ void guac_rdpdr_process_print_job_create(guac_rdpdr_device* device,
|
|||||||
|
|
||||||
/* Respond with success */
|
/* Respond with success */
|
||||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
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 */
|
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,
|
void guac_rdpdr_process_print_job_write(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int completion_id) {
|
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_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
|
guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
|
||||||
|
|
||||||
@ -100,19 +86,20 @@ void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
|
|||||||
}
|
}
|
||||||
|
|
||||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
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_UINT32(output_stream, length);
|
||||||
Stream_Write_UINT8(output_stream, 0); /* Padding */
|
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,
|
void guac_rdpdr_process_print_job_close(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int completion_id) {
|
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_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
|
guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
|
||||||
|
|
||||||
@ -123,61 +110,63 @@ void guac_rdpdr_process_print_job_close(guac_rdpdr_device* device,
|
|||||||
}
|
}
|
||||||
|
|
||||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
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 */
|
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 */
|
/* Log end of print job */
|
||||||
guac_client_log(client, GUAC_LOG_INFO, "Print job closed");
|
guac_client_log(client, GUAC_LOG_INFO, "Print job closed");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void guac_rdpdr_device_printer_iorequest_handler(guac_rdpdr_device* device,
|
void guac_rdpdr_device_printer_iorequest_handler(guac_rdp_common_svc* svc,
|
||||||
wStream* input_stream, int file_id, int completion_id, int major_func, int minor_func) {
|
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||||
|
wStream* input_stream) {
|
||||||
|
|
||||||
switch (major_func) {
|
switch (iorequest->major_func) {
|
||||||
|
|
||||||
/* Print job create */
|
/* Print job create */
|
||||||
case IRP_MJ_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;
|
break;
|
||||||
|
|
||||||
/* Printer job write */
|
/* Printer job write */
|
||||||
case IRP_MJ_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;
|
break;
|
||||||
|
|
||||||
/* Printer job close */
|
/* Printer job close */
|
||||||
case IRP_MJ_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;
|
break;
|
||||||
|
|
||||||
/* Log unknown */
|
/* Log unknown */
|
||||||
default:
|
default:
|
||||||
guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
|
guac_client_log(svc->client, GUAC_LOG_ERROR, "Unknown printer "
|
||||||
"Unknown printer I/O request function: 0x%x/0x%x",
|
"I/O request function: 0x%x/0x%x", iorequest->major_func,
|
||||||
major_func, minor_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);
|
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++;
|
int id = rdpdr->devices_registered++;
|
||||||
|
|
||||||
/* Get new device */
|
/* Get new device */
|
||||||
guac_rdpdr_device* device = &(rdpdr->devices[id]);
|
guac_rdpdr_device* device = &(rdpdr->devices[id]);
|
||||||
|
|
||||||
/* Init device */
|
/* Init device */
|
||||||
device->rdpdr = rdpdr;
|
|
||||||
device->device_id = id;
|
device->device_id = id;
|
||||||
device->device_name = printer_name;
|
device->device_name = printer_name;
|
||||||
int device_name_len = guac_utf8_strlen(device->device_name);
|
int device_name_len = guac_utf8_strlen(device->device_name);
|
81
src/protocols/rdp/channels/rdpdr/rdpdr-printer.h
Normal file
81
src/protocols/rdp/channels/rdpdr/rdpdr-printer.h
Normal file
@ -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
|
||||||
|
|
184
src/protocols/rdp/channels/rdpdr/rdpdr.c
Normal file
184
src/protocols/rdp/channels/rdpdr/rdpdr.c
Normal file
@ -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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
250
src/protocols/rdp/channels/rdpdr/rdpdr.h
Normal file
250
src/protocols/rdp/channels/rdpdr/rdpdr.h
Normal file
@ -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
|
||||||
|
|
@ -17,30 +17,20 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/rdpsnd/rdpsnd-messages.h"
|
||||||
|
#include "channels/rdpsnd/rdpsnd.h"
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
#include "rdpsnd_messages.h"
|
|
||||||
#include "rdpsnd_service.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <freerdp/codec/audio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <freerdp/utils/svc_plugin.h>
|
|
||||||
#include <guacamole/audio.h>
|
#include <guacamole/audio.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#include <winpr/wtypes.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) {
|
wStream* input_stream, guac_rdpsnd_pdu_header* header) {
|
||||||
|
|
||||||
int server_format_count;
|
int server_format_count;
|
||||||
@ -51,11 +41,10 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
int output_body_size;
|
int output_body_size;
|
||||||
unsigned char* output_stream_end;
|
unsigned char* output_stream_end;
|
||||||
|
|
||||||
/* Get associated client data */
|
guac_client* client = svc->client;
|
||||||
guac_client* client = rdpsnd->client;
|
guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
|
||||||
|
|
||||||
/* Get audio stream from client data */
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
guac_audio_stream* audio = rdp_client->audio;
|
guac_audio_stream* audio = rdp_client->audio;
|
||||||
|
|
||||||
/* Reset own format count */
|
/* Reset own format count */
|
||||||
@ -188,8 +177,7 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
Stream_SetPointer(output_stream, output_stream_end);
|
Stream_SetPointer(output_stream, output_stream_end);
|
||||||
|
|
||||||
/* Send accepted formats */
|
/* Send accepted formats */
|
||||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
svc_plugin_send((rdpSvcPlugin*)rdpsnd, output_stream);
|
|
||||||
|
|
||||||
/* If version greater than 6, must send Quality Mode PDU */
|
/* If version greater than 6, must send Quality Mode PDU */
|
||||||
if (server_version >= 6) {
|
if (server_version >= 6) {
|
||||||
@ -202,23 +190,20 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
Stream_Write_UINT16(output_stream, HIGH_QUALITY);
|
Stream_Write_UINT16(output_stream, HIGH_QUALITY);
|
||||||
Stream_Write_UINT16(output_stream, 0);
|
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 */
|
/* 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) {
|
wStream* input_stream, guac_rdpsnd_pdu_header* header) {
|
||||||
|
|
||||||
int data_size;
|
int data_size;
|
||||||
wStream* output_stream;
|
wStream* output_stream;
|
||||||
|
|
||||||
/* Get associated client data */
|
guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
|
||||||
guac_client* client = rdpsnd->client;
|
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
|
||||||
|
|
||||||
/* Read timestamp and data size */
|
/* Read timestamp and data size */
|
||||||
Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);
|
Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);
|
||||||
@ -232,22 +217,19 @@ void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp);
|
Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp);
|
||||||
Stream_Write_UINT16(output_stream, data_size);
|
Stream_Write_UINT16(output_stream, data_size);
|
||||||
|
|
||||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
svc_plugin_send((rdpSvcPlugin*) rdpsnd, output_stream);
|
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
wStream* input_stream, guac_rdpsnd_pdu_header* header) {
|
||||||
|
|
||||||
int format;
|
int format;
|
||||||
|
|
||||||
/* Get associated client data */
|
guac_client* client = svc->client;
|
||||||
guac_client* client = rdpsnd->client;
|
guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
|
||||||
|
|
||||||
/* Get audio stream from client data */
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
guac_audio_stream* audio = rdp_client->audio;
|
guac_audio_stream* audio = rdp_client->audio;
|
||||||
|
|
||||||
/* Read wave information */
|
/* Read wave information */
|
||||||
@ -276,16 +258,13 @@ void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
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;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
|
|
||||||
/* Get audio stream from client data */
|
|
||||||
guac_audio_stream* audio = rdp_client->audio;
|
guac_audio_stream* audio = rdp_client->audio;
|
||||||
|
|
||||||
/* Wave Confirmation PDU */
|
/* Wave Confirmation PDU */
|
||||||
@ -313,16 +292,14 @@ void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
Stream_Write_UINT8(output_stream, 0);
|
Stream_Write_UINT8(output_stream, 0);
|
||||||
|
|
||||||
/* Send Wave Confirmation PDU */
|
/* Send Wave Confirmation PDU */
|
||||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
guac_rdp_common_svc_write(svc, output_stream);
|
||||||
svc_plugin_send(plugin, output_stream);
|
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
/* We no longer expect to receive wave data */
|
/* We no longer expect to receive wave data */
|
||||||
rdpsnd->next_pdu_is_wave = FALSE;
|
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) {
|
wStream* input_stream, guac_rdpsnd_pdu_header* header) {
|
||||||
|
|
||||||
/* Do nothing */
|
/* Do nothing */
|
@ -17,87 +17,12 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_CHANNELS_RDPSND_MESSAGES_H
|
||||||
|
#define GUAC_RDP_CHANNELS_RDPSND_MESSAGES_H
|
||||||
|
|
||||||
#ifndef __GUAC_RDPSND_MESSAGES_H
|
#include "channels/common-svc.h"
|
||||||
#define __GUAC_RDPSND_MESSAGES_H
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "rdpsnd_service.h"
|
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/stream.h>
|
#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.
|
* The header common to all RDPSND PDUs.
|
||||||
@ -121,8 +46,8 @@ typedef struct guac_rdpsnd_pdu_header {
|
|||||||
* SNDC_FORMATS PDU describes all audio formats supported by the RDP server, as
|
* SNDC_FORMATS PDU describes all audio formats supported by the RDP server, as
|
||||||
* well as the version of RDPSND implemented.
|
* well as the version of RDPSND implemented.
|
||||||
*
|
*
|
||||||
* @param rdpsnd
|
* @param svc
|
||||||
* The Guacamole RDPSND plugin receiving the SNDC_FORMATS PDU.
|
* The RDPSND channel receiving the SNDC_FORMATS PDU.
|
||||||
*
|
*
|
||||||
* @param input_stream
|
* @param input_stream
|
||||||
* The FreeRDP input stream containing the remaining raw bytes (after the
|
* The FreeRDP input stream containing the remaining raw bytes (after the
|
||||||
@ -132,7 +57,7 @@ typedef struct guac_rdpsnd_pdu_header {
|
|||||||
* The header content of the SNDC_FORMATS PDU. All RDPSND messages contain
|
* The header content of the SNDC_FORMATS PDU. All RDPSND messages contain
|
||||||
* the same header information.
|
* 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);
|
wStream* input_stream, guac_rdpsnd_pdu_header* header);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,8 +67,8 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
*
|
*
|
||||||
* https://msdn.microsoft.com/en-us/library/cc240961.aspx
|
* https://msdn.microsoft.com/en-us/library/cc240961.aspx
|
||||||
*
|
*
|
||||||
* @param rdpsnd
|
* @param svc
|
||||||
* The Guacamole RDPSND plugin receiving the SNDC_TRAINING PDU.
|
* The RDPSND channel receiving the SNDC_TRAINING PDU.
|
||||||
*
|
*
|
||||||
* @param input_stream
|
* @param input_stream
|
||||||
* The FreeRDP input stream containing the remaining raw bytes (after the
|
* The FreeRDP input stream containing the remaining raw bytes (after the
|
||||||
@ -153,7 +78,7 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
* The header content of the SNDC_TRAINING PDU. All RDPSND messages contain
|
* The header content of the SNDC_TRAINING PDU. All RDPSND messages contain
|
||||||
* the same header information.
|
* 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);
|
wStream* input_stream, guac_rdpsnd_pdu_header* header);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -165,8 +90,8 @@ void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
*
|
*
|
||||||
* https://msdn.microsoft.com/en-us/library/cc240963.aspx
|
* https://msdn.microsoft.com/en-us/library/cc240963.aspx
|
||||||
*
|
*
|
||||||
* @param rdpsnd
|
* @param svc
|
||||||
* The Guacamole RDPSND plugin receiving the SNDC_WAVE PDU.
|
* The RDPSND channel receiving the SNDC_WAVE PDU.
|
||||||
*
|
*
|
||||||
* @param input_stream
|
* @param input_stream
|
||||||
* The FreeRDP input stream containing the remaining raw bytes (after the
|
* The FreeRDP input stream containing the remaining raw bytes (after the
|
||||||
@ -176,7 +101,7 @@ void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
* The header content of the SNDC_WAVE PDU. All RDPSND messages contain
|
* The header content of the SNDC_WAVE PDU. All RDPSND messages contain
|
||||||
* the same header information.
|
* 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);
|
wStream* input_stream, guac_rdpsnd_pdu_header* header);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -184,8 +109,8 @@ void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
* PDU contains the actual audio data, less the four bytes of audio data
|
* PDU contains the actual audio data, less the four bytes of audio data
|
||||||
* included in the SNDC_WAVE PDU.
|
* included in the SNDC_WAVE PDU.
|
||||||
*
|
*
|
||||||
* @param rdpsnd
|
* @param svc
|
||||||
* The Guacamole RDPSND plugin receiving the SNDWAV PDU.
|
* The RDPSND channel receiving the SNDWAV PDU.
|
||||||
*
|
*
|
||||||
* @param input_stream
|
* @param input_stream
|
||||||
* The FreeRDP input stream containing the remaining raw bytes (after the
|
* The FreeRDP input stream containing the remaining raw bytes (after the
|
||||||
@ -195,7 +120,7 @@ void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
* The header content of the SNDWAV PDU. All RDPSND messages contain
|
* The header content of the SNDWAV PDU. All RDPSND messages contain
|
||||||
* the same header information.
|
* 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);
|
wStream* input_stream, guac_rdpsnd_pdu_header* header);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -204,8 +129,8 @@ void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
*
|
*
|
||||||
* https://msdn.microsoft.com/en-us/library/cc240970.aspx
|
* https://msdn.microsoft.com/en-us/library/cc240970.aspx
|
||||||
*
|
*
|
||||||
* @param rdpsnd
|
* @param svc
|
||||||
* The Guacamole RDPSND plugin receiving the SNDC_CLOSE PDU.
|
* The RDPSND channel receiving the SNDC_CLOSE PDU.
|
||||||
*
|
*
|
||||||
* @param input_stream
|
* @param input_stream
|
||||||
* The FreeRDP input stream containing the remaining raw bytes (after the
|
* The FreeRDP input stream containing the remaining raw bytes (after the
|
||||||
@ -215,7 +140,7 @@ void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
|
|||||||
* The header content of the SNDC_CLOSE PDU. All RDPSND messages contain
|
* The header content of the SNDC_CLOSE PDU. All RDPSND messages contain
|
||||||
* the same header information.
|
* 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);
|
wStream* input_stream, guac_rdpsnd_pdu_header* header);
|
||||||
|
|
||||||
#endif
|
#endif
|
105
src/protocols/rdp/channels/rdpsnd/rdpsnd.c
Normal file
105
src/protocols/rdp/channels/rdpsnd/rdpsnd.c
Normal file
@ -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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -17,21 +17,14 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_CHANNELS_RDPSND_H
|
||||||
|
#define GUAC_RDP_CHANNELS_RDPSND_H
|
||||||
|
|
||||||
#ifndef __GUAC_RDPSND_SERVICE_H
|
#include "channels/common-svc.h"
|
||||||
#define __GUAC_RDPSND_SERVICE_H
|
|
||||||
|
|
||||||
#include "config.h"
|
#include <freerdp/freerdp.h>
|
||||||
|
|
||||||
#include <freerdp/utils/svc_plugin.h>
|
|
||||||
#include <guacamole/client.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
|
* The maximum number of PCM formats to accept during the initial RDPSND
|
||||||
* handshake with the RDP server.
|
* handshake with the RDP server.
|
||||||
@ -42,7 +35,7 @@
|
|||||||
* Abstract representation of a PCM format, including the sample rate, number
|
* Abstract representation of a PCM format, including the sample rate, number
|
||||||
* of channels, and bits per sample.
|
* of channels, and bits per sample.
|
||||||
*/
|
*/
|
||||||
typedef struct guac_pcm_format {
|
typedef struct guac_rdpsnd_pcm_format {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The sample rate of this PCM format.
|
* The sample rate of this PCM format.
|
||||||
@ -61,26 +54,13 @@ typedef struct guac_pcm_format {
|
|||||||
*/
|
*/
|
||||||
int bps;
|
int bps;
|
||||||
|
|
||||||
} guac_pcm_format;
|
} guac_rdpsnd_pcm_format;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structure representing the current state of the Guacamole RDPSND plugin for
|
* Structure representing the current state of the Guacamole RDPSND plugin for
|
||||||
* FreeRDP.
|
* FreeRDP.
|
||||||
*/
|
*/
|
||||||
typedef struct guac_rdpsndPlugin {
|
typedef struct guac_rdpsnd {
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The block number of the last SNDC_WAVE (WaveInfo) PDU received.
|
* The block number of the last SNDC_WAVE (WaveInfo) PDU received.
|
||||||
@ -116,36 +96,45 @@ typedef struct guac_rdpsndPlugin {
|
|||||||
* exchange. All of these formats will be PCM, which is the only format
|
* exchange. All of these formats will be PCM, which is the only format
|
||||||
* guaranteed to be supported (based on the official RDP documentation).
|
* 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.
|
* The total number of formats.
|
||||||
*/
|
*/
|
||||||
int format_count;
|
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,
|
guac_rdp_common_svc_connect_handler guac_rdpsnd_process_connect;
|
||||||
wStream* input_stream);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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,
|
* Handler which is invoked when the RDPSND channel has disconnected and is
|
||||||
* all events will be ignored and simply free'd.
|
* about to be freed.
|
||||||
*/
|
*/
|
||||||
void guac_rdpsnd_process_event(rdpSvcPlugin* plugin, wMessage* event);
|
guac_rdp_common_svc_terminate_handler guac_rdpsnd_process_terminate;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -17,14 +17,16 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "audio_input.h"
|
|
||||||
#include "common/recording.h"
|
|
||||||
#include "client.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.h"
|
||||||
#include "rdp_disp.h"
|
#include "settings.h"
|
||||||
#include "rdp_fs.h"
|
|
||||||
#include "user.h"
|
#include "user.h"
|
||||||
|
|
||||||
#ifdef ENABLE_COMMON_SSH
|
#ifdef ENABLE_COMMON_SSH
|
||||||
@ -33,26 +35,11 @@
|
|||||||
#include "common-ssh/user.h"
|
#include "common-ssh/user.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <freerdp/cache/cache.h>
|
|
||||||
#include <freerdp/channels/channels.h>
|
|
||||||
#include <freerdp/freerdp.h>
|
|
||||||
#include <guacamole/audio.h>
|
#include <guacamole/audio.h>
|
||||||
#include <guacamole/client.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 <pthread.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
int guac_client_init(guac_client* client, int argc, char** argv) {
|
int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||||
|
|
||||||
@ -64,19 +51,19 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
|
|||||||
client->data = rdp_client;
|
client->data = rdp_client;
|
||||||
|
|
||||||
/* Init clipboard */
|
/* 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 */
|
/* Init display update module */
|
||||||
rdp_client->disp = guac_rdp_disp_alloc();
|
rdp_client->disp = guac_rdp_disp_alloc();
|
||||||
|
|
||||||
|
/* Redirect FreeRDP log messages to guac_client_log() */
|
||||||
|
guac_rdp_redirect_wlog(client);
|
||||||
|
|
||||||
/* Recursive attribute for locks */
|
/* Recursive attribute for locks */
|
||||||
pthread_mutexattr_init(&(rdp_client->attributes));
|
pthread_mutexattr_init(&(rdp_client->attributes));
|
||||||
pthread_mutexattr_settype(&(rdp_client->attributes),
|
pthread_mutexattr_settype(&(rdp_client->attributes),
|
||||||
PTHREAD_MUTEX_RECURSIVE);
|
PTHREAD_MUTEX_RECURSIVE);
|
||||||
|
|
||||||
/* Init RDP lock */
|
|
||||||
pthread_mutex_init(&(rdp_client->rdp_lock), &(rdp_client->attributes));
|
|
||||||
|
|
||||||
/* Set handlers */
|
/* Set handlers */
|
||||||
client->join_handler = guac_rdp_user_join_handler;
|
client->join_handler = guac_rdp_user_join_handler;
|
||||||
client->free_handler = guac_rdp_client_free_handler;
|
client->free_handler = guac_rdp_client_free_handler;
|
||||||
@ -100,6 +87,9 @@ int guac_rdp_client_free_handler(guac_client* client) {
|
|||||||
if (rdp_client->settings != NULL)
|
if (rdp_client->settings != NULL)
|
||||||
guac_rdp_settings_free(rdp_client->settings);
|
guac_rdp_settings_free(rdp_client->settings);
|
||||||
|
|
||||||
|
/* Clean up clipboard */
|
||||||
|
guac_rdp_clipboard_free(rdp_client->clipboard);
|
||||||
|
|
||||||
/* Free display update module */
|
/* Free display update module */
|
||||||
guac_rdp_disp_free(rdp_client->disp);
|
guac_rdp_disp_free(rdp_client->disp);
|
||||||
|
|
||||||
@ -136,7 +126,6 @@ int guac_rdp_client_free_handler(guac_client* client) {
|
|||||||
guac_rdp_audio_buffer_free(rdp_client->audio_input);
|
guac_rdp_audio_buffer_free(rdp_client->audio_input);
|
||||||
|
|
||||||
/* Free client data */
|
/* Free client data */
|
||||||
guac_common_clipboard_free(rdp_client->clipboard);
|
|
||||||
free(rdp_client);
|
free(rdp_client);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -20,8 +20,6 @@
|
|||||||
#ifndef GUAC_RDP_CLIENT_H
|
#ifndef GUAC_RDP_CLIENT_H
|
||||||
#define GUAC_RDP_CLIENT_H
|
#define GUAC_RDP_CLIENT_H
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
76
src/protocols/rdp/color.c
Normal file
76
src/protocols/rdp/color.c
Normal file
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -21,12 +21,27 @@
|
|||||||
#define GUAC_RDP_COLOR_H
|
#define GUAC_RDP_COLOR_H
|
||||||
|
|
||||||
#include <freerdp/freerdp.h>
|
#include <freerdp/freerdp.h>
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/wtypes.h>
|
#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
|
* Converts the given color to ARGB32. The color given may be an index
|
@ -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
|
|
||||||
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
2
src/protocols/rdp/doc/svc-example/.gitignore
vendored
Normal file
2
src/protocols/rdp/doc/svc-example/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
!Makefile
|
||||||
|
*.exe
|
43
src/protocols/rdp/doc/svc-example/Makefile
Normal file
43
src/protocols/rdp/doc/svc-example/Makefile
Normal file
@ -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
|
||||||
|
|
169
src/protocols/rdp/doc/svc-example/README.md
Normal file
169
src/protocols/rdp/doc/svc-example/README.md
Normal file
@ -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."
|
||||||
|
```
|
||||||
|
|
71
src/protocols/rdp/doc/svc-example/svc-example.c
Normal file
71
src/protocols/rdp/doc/svc-example/svc-example.c
Normal file
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
222
src/protocols/rdp/download.c
Normal file
222
src/protocols/rdp/download.c
Normal file
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
69
src/protocols/rdp/download.h
Normal file
69
src/protocols/rdp/download.h
Normal file
@ -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
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -17,8 +17,6 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
|
|
||||||
|
@ -17,11 +17,19 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "fs.h"
|
||||||
|
#include "download.h"
|
||||||
|
#include "upload.h"
|
||||||
|
|
||||||
#include "rdp_fs.h"
|
#include <guacamole/client.h>
|
||||||
#include "rdp_status.h"
|
#include <guacamole/object.h>
|
||||||
#include "rdp_stream.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 <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@ -32,16 +40,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.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,
|
guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path,
|
||||||
int create_drive_path) {
|
int create_drive_path) {
|
||||||
|
|
||||||
@ -244,16 +244,16 @@ int guac_rdp_fs_open(guac_rdp_fs* fs, const char* path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Translate access into flags */
|
/* Translate access into flags */
|
||||||
if (access & ACCESS_GENERIC_ALL)
|
if (access & GENERIC_ALL)
|
||||||
flags = O_RDWR;
|
flags = O_RDWR;
|
||||||
else if ((access & ( ACCESS_GENERIC_WRITE
|
else if ((access & ( GENERIC_WRITE
|
||||||
| ACCESS_FILE_WRITE_DATA
|
| FILE_WRITE_DATA
|
||||||
| ACCESS_FILE_APPEND_DATA))
|
| FILE_APPEND_DATA))
|
||||||
&& (access & (ACCESS_GENERIC_READ | ACCESS_FILE_READ_DATA)))
|
&& (access & (GENERIC_READ | FILE_READ_DATA)))
|
||||||
flags = O_RDWR;
|
flags = O_RDWR;
|
||||||
else if (access & ( ACCESS_GENERIC_WRITE
|
else if (access & ( GENERIC_WRITE
|
||||||
| ACCESS_FILE_WRITE_DATA
|
| FILE_WRITE_DATA
|
||||||
| ACCESS_FILE_APPEND_DATA))
|
| FILE_APPEND_DATA))
|
||||||
flags = O_WRONLY;
|
flags = O_WRONLY;
|
||||||
else
|
else
|
||||||
flags = O_RDONLY;
|
flags = O_RDONLY;
|
||||||
@ -279,32 +279,32 @@ int guac_rdp_fs_open(guac_rdp_fs* fs, const char* path,
|
|||||||
switch (create_disposition) {
|
switch (create_disposition) {
|
||||||
|
|
||||||
/* Create if not exist, fail otherwise */
|
/* Create if not exist, fail otherwise */
|
||||||
case DISP_FILE_CREATE:
|
case FILE_CREATE:
|
||||||
flags |= O_CREAT | O_EXCL;
|
flags |= O_CREAT | O_EXCL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Open file if exists and do not overwrite, fail otherwise */
|
/* Open file if exists and do not overwrite, fail otherwise */
|
||||||
case DISP_FILE_OPEN:
|
case FILE_OPEN:
|
||||||
/* No flag necessary - default functionality of open */
|
/* No flag necessary - default functionality of open */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Open if exists, create otherwise */
|
/* Open if exists, create otherwise */
|
||||||
case DISP_FILE_OPEN_IF:
|
case FILE_OPEN_IF:
|
||||||
flags |= O_CREAT;
|
flags |= O_CREAT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Overwrite if exists, fail otherwise */
|
/* Overwrite if exists, fail otherwise */
|
||||||
case DISP_FILE_OVERWRITE:
|
case FILE_OVERWRITE:
|
||||||
flags |= O_TRUNC;
|
flags |= O_TRUNC;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Overwrite if exists, create otherwise */
|
/* Overwrite if exists, create otherwise */
|
||||||
case DISP_FILE_OVERWRITE_IF:
|
case FILE_OVERWRITE_IF:
|
||||||
flags |= O_CREAT | O_TRUNC;
|
flags |= O_CREAT | O_TRUNC;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Supersede (replace) if exists, otherwise create */
|
/* Supersede (replace) if exists, otherwise create */
|
||||||
case DISP_FILE_SUPERSEDE:
|
case FILE_SUPERSEDE:
|
||||||
unlink(real_path);
|
unlink(real_path);
|
||||||
flags |= O_CREAT | O_TRUNC;
|
flags |= O_CREAT | O_TRUNC;
|
||||||
break;
|
break;
|
||||||
@ -605,6 +605,21 @@ const char* guac_rdp_fs_read_dir(guac_rdp_fs* fs, int file_id) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 guac_rdp_fs_normalize_path(const char* path, char* abs_path) {
|
||||||
|
|
||||||
int path_depth = 0;
|
int path_depth = 0;
|
@ -17,9 +17,8 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_FS_H
|
||||||
#ifndef __GUAC_RDP_FS_H
|
#define GUAC_RDP_FS_H
|
||||||
#define __GUAC_RDP_FS_H
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Functions and macros specific to filesystem handling and initialization
|
* Functions and macros specific to filesystem handling and initialization
|
||||||
@ -29,13 +28,13 @@
|
|||||||
* system calls and structures, using the guac_rdp_fs structure as a home
|
* system calls and structures, using the guac_rdp_fs structure as a home
|
||||||
* for common data.
|
* for common data.
|
||||||
*
|
*
|
||||||
* @file rdp_fs.h
|
* @file fs.h
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
#include <guacamole/object.h>
|
||||||
#include <guacamole/pool.h>
|
#include <guacamole/pool.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@ -109,73 +108,6 @@
|
|||||||
*/
|
*/
|
||||||
#define GUAC_RDP_FS_ENOTSUP -10
|
#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
|
* Converts a Windows timestamp (100 nanosecond intervals since Jan 1, 1601
|
||||||
* UTC) to UNIX timestamp (seconds since Jan 1, 1970 UTC).
|
* UTC) to UNIX timestamp (seconds since Jan 1, 1970 UTC).
|
||||||
@ -438,10 +370,10 @@ int guac_rdp_fs_get_status(int err);
|
|||||||
* The absolute path to the file within the simulated filesystem.
|
* The absolute path to the file within the simulated filesystem.
|
||||||
*
|
*
|
||||||
* @param access
|
* @param access
|
||||||
* A bitwise-OR of various RDPDR access flags, such as ACCESS_GENERIC_ALL
|
* A bitwise-OR of various RDPDR access flags, such as GENERIC_ALL or
|
||||||
* or ACCESS_GENERIC_WRITE. This value will ultimately be translated to a
|
* GENERIC_WRITE. This value will ultimately be translated to a standard
|
||||||
* standard O_RDWR, O_WRONLY, etc. value when opening the real file on the
|
* O_RDWR, O_WRONLY, etc. value when opening the real file on the local
|
||||||
* local filesystem.
|
* filesystem.
|
||||||
*
|
*
|
||||||
* @param file_attributes
|
* @param file_attributes
|
||||||
* The attributes to apply to the file, if created. This parameter is
|
* The attributes to apply to the file, if created. This parameter is
|
||||||
@ -449,9 +381,9 @@ int guac_rdp_fs_get_status(int err);
|
|||||||
*
|
*
|
||||||
* @param create_disposition
|
* @param create_disposition
|
||||||
* Any one of several RDPDR file creation dispositions, such as
|
* Any one of several RDPDR file creation dispositions, such as
|
||||||
* DISP_FILE_CREATE, DISP_FILE_OPEN_IF, etc. The creation disposition
|
* FILE_CREATE, FILE_OPEN_IF, etc. The creation disposition dictates
|
||||||
* dictates whether a new file should be created, whether the file can
|
* whether a new file should be created, whether the file can already
|
||||||
* already exist, whether existing contents should be truncated, etc.
|
* exist, whether existing contents should be truncated, etc.
|
||||||
*
|
*
|
||||||
* @param create_options
|
* @param create_options
|
||||||
* A bitwise-OR of various RDPDR options dictating how a file is to be
|
* A bitwise-OR of various RDPDR options dictating how a file is to be
|
||||||
@ -592,6 +524,20 @@ int guac_rdp_fs_truncate(guac_rdp_fs* fs, int file_id, int length);
|
|||||||
*/
|
*/
|
||||||
void guac_rdp_fs_close(guac_rdp_fs* fs, int file_id);
|
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
|
* Given an arbitrary path, which may contain ".." and ".", creates an
|
||||||
* absolute path which does NOT contain ".." or ".". The given path MUST
|
* absolute path which does NOT contain ".." or ".". The given path MUST
|
@ -17,26 +17,21 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "bitmap.h"
|
||||||
|
#include "common/display.h"
|
||||||
#include "client.h"
|
|
||||||
#include "common/surface.h"
|
#include "common/surface.h"
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
#include "rdp_bitmap.h"
|
#include "settings.h"
|
||||||
#include "rdp_color.h"
|
|
||||||
#include "rdp_settings.h"
|
|
||||||
|
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
#include <freerdp/freerdp.h>
|
#include <freerdp/freerdp.h>
|
||||||
|
#include <freerdp/graphics.h>
|
||||||
|
#include <freerdp/primary.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/protocol.h>
|
#include <guacamole/protocol.h>
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/wtypes.h>
|
#include <winpr/wtypes.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-wtypes.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
guac_transfer_function guac_rdp_rop3_transfer_function(guac_client* client,
|
guac_transfer_function guac_rdp_rop3_transfer_function(guac_client* client,
|
||||||
@ -97,7 +92,7 @@ guac_transfer_function guac_rdp_rop3_transfer_function(guac_client* client,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
|
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
|
||||||
@ -140,77 +135,11 @@ void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
return TRUE;
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
|
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
|
||||||
@ -229,9 +158,11 @@ void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt) {
|
|||||||
guac_common_surface_copy(rdp_client->display->default_surface,
|
guac_common_surface_copy(rdp_client->display->default_surface,
|
||||||
x_src, y_src, w, h, current_surface, x, y);
|
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_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
|
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
|
||||||
@ -248,7 +179,7 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
|
|||||||
/* Make sure that the recieved bitmap is not NULL before processing */
|
/* Make sure that the recieved bitmap is not NULL before processing */
|
||||||
if (bitmap == NULL) {
|
if (bitmap == NULL) {
|
||||||
guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in memblt instruction.");
|
guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in memblt instruction.");
|
||||||
return;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (memblt->bRop) {
|
switch (memblt->bRop) {
|
||||||
@ -321,98 +252,11 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
return TRUE;
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
BOOL guac_rdp_gdi_set_bounds(rdpContext* context, const rdpBounds* bounds) {
|
||||||
* 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) {
|
|
||||||
|
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
@ -428,13 +272,16 @@ void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds) {
|
|||||||
bounds->right - bounds->left + 1,
|
bounds->right - bounds->left + 1,
|
||||||
bounds->bottom - bounds->top + 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 */
|
/* 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_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
@ -449,6 +296,8 @@ void guac_rdp_gdi_desktop_resize(rdpContext* context) {
|
|||||||
guac_rdp_get_width(context->instance),
|
guac_rdp_get_width(context->instance),
|
||||||
guac_rdp_get_height(context->instance));
|
guac_rdp_get_height(context->instance));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17,9 +17,8 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_GDI_H
|
||||||
#ifndef _GUAC_RDP_RDP_GDI_H
|
#define GUAC_RDP_GDI_H
|
||||||
#define _GUAC_RDP_RDP_GDI_H
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
@ -46,71 +45,58 @@ guac_composite_mode guac_rdp_rop3_transfer_function(guac_client* client,
|
|||||||
int rop3);
|
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
|
* @param context
|
||||||
* The rdpContext associated with the current RDP session.
|
* The rdpContext associated with the current RDP session.
|
||||||
*
|
*
|
||||||
* @param dstblt
|
* @param dstblt
|
||||||
* The DSTBLT update to handle.
|
* 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
|
* https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegdi/a4e322b0-cd64-4dfc-8e1a-f24dc0edc99d
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* The rdpContext associated with the current RDP session.
|
* The rdpContext associated with the current RDP session.
|
||||||
*
|
*
|
||||||
* @param scrblt
|
* @param scrblt
|
||||||
* The SCRBLT update to handle.
|
* 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
|
* @param context
|
||||||
* The rdpContext associated with the current RDP session.
|
* The rdpContext associated with the current RDP session.
|
||||||
*
|
*
|
||||||
* @param memblt
|
* @param memblt
|
||||||
* The MEMBLT update to handle.
|
* The MEMBLT update to handle.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* TRUE if successful, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt);
|
BOOL 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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler called prior to calling the handlers for specific updates when
|
* Handler called prior to calling the handlers for specific updates when
|
||||||
@ -124,8 +110,11 @@ void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette);
|
|||||||
* @param bounds
|
* @param bounds
|
||||||
* The clipping rectangle to set, or NULL to remove any applied clipping
|
* The clipping rectangle to set, or NULL to remove any applied clipping
|
||||||
* rectangle.
|
* 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
|
* Handler called when a paint operation is complete. We don't actually
|
||||||
@ -133,8 +122,11 @@ void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds);
|
|||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* The rdpContext associated with the current RDP session.
|
* 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
|
* Handler called when the desktop dimensions change, either from a
|
||||||
@ -147,7 +139,10 @@ void guac_rdp_gdi_end_paint(rdpContext* context);
|
|||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* The rdpContext associated with the current RDP session.
|
* 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
|
#endif
|
@ -17,25 +17,16 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "color.h"
|
||||||
|
|
||||||
#include "client.h"
|
|
||||||
#include "common/surface.h"
|
#include "common/surface.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "glyph.h"
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
#include "rdp_color.h"
|
|
||||||
#include "rdp_glyph.h"
|
|
||||||
#include "rdp_settings.h"
|
|
||||||
|
|
||||||
#include <freerdp/freerdp.h>
|
#include <freerdp/freerdp.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/wtypes.h>
|
#include <winpr/wtypes.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-wtypes.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@ -44,7 +35,7 @@
|
|||||||
#define cairo_format_stride_for_width(format, width) (width*4)
|
#define cairo_format_stride_for_width(format, width) (width*4)
|
||||||
#endif
|
#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 x, y, i;
|
||||||
int stride;
|
int stride;
|
||||||
@ -95,9 +86,15 @@ void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph) {
|
|||||||
((guac_rdp_glyph*) glyph)->surface = cairo_image_surface_create_for_data(
|
((guac_rdp_glyph*) glyph)->surface = cairo_image_surface_create_for_data(
|
||||||
image_buffer, CAIRO_FORMAT_ARGB32, width, height, stride);
|
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_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
@ -110,6 +107,8 @@ void guac_rdp_glyph_draw(rdpContext* context, rdpGlyph* glyph, int x, int y) {
|
|||||||
(fgcolor & 0x00FF00) >> 8,
|
(fgcolor & 0x00FF00) >> 8,
|
||||||
fgcolor & 0x0000FF);
|
fgcolor & 0x0000FF);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdp_glyph_free(rdpContext* context, rdpGlyph* glyph) {
|
void guac_rdp_glyph_free(rdpContext* context, rdpGlyph* glyph) {
|
||||||
@ -121,17 +120,26 @@ void guac_rdp_glyph_free(rdpContext* context, rdpGlyph* glyph) {
|
|||||||
cairo_surface_destroy(((guac_rdp_glyph*) glyph)->surface);
|
cairo_surface_destroy(((guac_rdp_glyph*) glyph)->surface);
|
||||||
free(image_buffer);
|
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,
|
BOOL guac_rdp_glyph_begindraw(rdpContext* context,
|
||||||
int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor) {
|
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_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client =
|
guac_rdp_client* rdp_client =
|
||||||
(guac_rdp_client*) client->data;
|
(guac_rdp_client*) client->data;
|
||||||
|
|
||||||
/* Fill background with color if specified */
|
/* Fill background with color if specified */
|
||||||
if (width != 0 && height != 0) {
|
if (width != 0 && height != 0 && !redundant) {
|
||||||
|
|
||||||
/* Convert background color */
|
/* Convert background color */
|
||||||
bgcolor = guac_rdp_convert_color(context, bgcolor);
|
bgcolor = guac_rdp_convert_color(context, bgcolor);
|
||||||
@ -148,10 +156,15 @@ void guac_rdp_glyph_begindraw(rdpContext* context,
|
|||||||
/* Convert foreground color */
|
/* Convert foreground color */
|
||||||
rdp_client->glyph_color = guac_rdp_convert_color(context, fgcolor);
|
rdp_client->glyph_color = guac_rdp_convert_color(context, fgcolor);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdp_glyph_enddraw(rdpContext* context,
|
BOOL guac_rdp_glyph_enddraw(rdpContext* context,
|
||||||
int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor) {
|
GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
|
||||||
|
GLYPH_CALLBACK_INT32 width, GLYPH_CALLBACK_INT32 height,
|
||||||
|
UINT32 fgcolor, UINT32 bgcolor) {
|
||||||
/* IGNORE */
|
/* IGNORE */
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
@ -17,19 +17,28 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_GLYPH_H
|
||||||
#ifndef _GUAC_RDP_RDP_GLYPH_H
|
#define GUAC_RDP_GLYPH_H
|
||||||
#define _GUAC_RDP_RDP_GLYPH_H
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
#include <freerdp/freerdp.h>
|
#include <freerdp/freerdp.h>
|
||||||
|
#include <freerdp/graphics.h>
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/wtypes.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
|
#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
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,8 +67,11 @@ typedef struct guac_rdp_glyph {
|
|||||||
*
|
*
|
||||||
* @param glyph
|
* @param glyph
|
||||||
* The glyph to cache.
|
* 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
|
* Draws a previously-cached glyph at the given coordinates within the current
|
||||||
@ -76,8 +88,32 @@ void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph);
|
|||||||
*
|
*
|
||||||
* @param y
|
* @param y
|
||||||
* The destination Y coordinate of the upper-left corner of the glyph.
|
* 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
|
* Frees any Guacamole-specific data associated with the given glyph, such that
|
||||||
@ -125,9 +161,17 @@ void guac_rdp_glyph_free(rdpContext* context, rdpGlyph* glyph);
|
|||||||
* colorspace of the RDP session, and may even be a palette index, and must
|
* 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
|
* be translated via guac_rdp_convert_color(). If the background is
|
||||||
* transparent, this value is undefined.
|
* 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,
|
BOOL guac_rdp_glyph_begindraw(rdpContext* context,
|
||||||
int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor);
|
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
|
* Called immediately after rendering a series of glyphs. Unlike
|
||||||
@ -162,8 +206,13 @@ void guac_rdp_glyph_begindraw(rdpContext* context,
|
|||||||
* colorspace of the RDP session, and may even be a palette index, and must
|
* 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
|
* be translated via guac_rdp_convert_color(). If the background is
|
||||||
* transparent, this value is undefined.
|
* transparent, this value is undefined.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* TRUE if successful, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
void guac_rdp_glyph_enddraw(rdpContext* context,
|
BOOL guac_rdp_glyph_enddraw(rdpContext* context,
|
||||||
int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor);
|
GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
|
||||||
|
GLYPH_CALLBACK_INT32 width, GLYPH_CALLBACK_INT32 height,
|
||||||
|
UINT32 fgcolor, UINT32 bgcolor);
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -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");
|
|
||||||
}
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -17,20 +17,20 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/disp.h"
|
||||||
|
#include "common/cursor.h"
|
||||||
#include "client.h"
|
#include "common/display.h"
|
||||||
#include "common/recording.h"
|
#include "common/recording.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
#include "rdp_disp.h"
|
#include "settings.h"
|
||||||
|
|
||||||
#include <freerdp/freerdp.h>
|
#include <freerdp/freerdp.h>
|
||||||
#include <freerdp/input.h>
|
#include <freerdp/input.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
|
int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
|
||||||
@ -38,14 +38,10 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
|
|||||||
guac_client* client = user->client;
|
guac_client* client = user->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
|
|
||||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
/* Skip if not yet connected */
|
/* Skip if not yet connected */
|
||||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||||
if (rdp_inst == NULL) {
|
if (rdp_inst == NULL)
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
/* Store current mouse location/state */
|
/* Store current mouse location/state */
|
||||||
guac_common_cursor_update(rdp_client->display->cursor, user, x, y, mask);
|
guac_common_cursor_update(rdp_client->display->cursor, user, x, y, mask);
|
||||||
@ -118,8 +114,6 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
|
|||||||
rdp_client->mouse_button_mask = mask;
|
rdp_client->mouse_button_mask = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,9 +149,7 @@ int guac_rdp_user_size_handler(guac_user* user, int width, int height) {
|
|||||||
height = height * settings->resolution / user->info.optimal_resolution;
|
height = height * settings->resolution / user->info.optimal_resolution;
|
||||||
|
|
||||||
/* Send display update */
|
/* Send display update */
|
||||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
|
||||||
guac_rdp_disp_set_size(rdp_client->disp, settings, rdp_inst, width, height);
|
guac_rdp_disp_set_size(rdp_client->disp, settings, rdp_inst, width, height);
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -17,19 +17,15 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "client.h"
|
|
||||||
#include "decompose.h"
|
#include "decompose.h"
|
||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
|
#include "keymap.h"
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
#include "rdp_keymap.h"
|
|
||||||
|
|
||||||
#include <freerdp/freerdp.h>
|
#include <freerdp/freerdp.h>
|
||||||
#include <freerdp/input.h>
|
#include <freerdp/input.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,21 +98,15 @@ static void guac_rdp_send_key_event(guac_rdp_client* rdp_client,
|
|||||||
else
|
else
|
||||||
pressed_flags = KBD_FLAGS_RELEASE;
|
pressed_flags = KBD_FLAGS_RELEASE;
|
||||||
|
|
||||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
/* Skip if not yet connected */
|
/* Skip if not yet connected */
|
||||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||||
if (rdp_inst == NULL) {
|
if (rdp_inst == NULL)
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
/* Send actual key */
|
/* Send actual key */
|
||||||
rdp_inst->input->KeyboardEvent(rdp_inst->input,
|
rdp_inst->input->KeyboardEvent(rdp_inst->input,
|
||||||
flags | pressed_flags, scancode);
|
flags | pressed_flags, scancode);
|
||||||
|
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,22 +126,16 @@ static void guac_rdp_send_key_event(guac_rdp_client* rdp_client,
|
|||||||
static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client,
|
static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client,
|
||||||
int codepoint) {
|
int codepoint) {
|
||||||
|
|
||||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
/* Skip if not yet connected */
|
/* Skip if not yet connected */
|
||||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||||
if (rdp_inst == NULL) {
|
if (rdp_inst == NULL)
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
/* Send Unicode event */
|
/* Send Unicode event */
|
||||||
rdp_inst->input->UnicodeKeyboardEvent(
|
rdp_inst->input->UnicodeKeyboardEvent(
|
||||||
rdp_inst->input,
|
rdp_inst->input,
|
||||||
0, codepoint);
|
0, codepoint);
|
||||||
|
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,20 +155,14 @@ static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client,
|
|||||||
static void guac_rdp_send_synchronize_event(guac_rdp_client* rdp_client,
|
static void guac_rdp_send_synchronize_event(guac_rdp_client* rdp_client,
|
||||||
int flags) {
|
int flags) {
|
||||||
|
|
||||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
/* Skip if not yet connected */
|
/* Skip if not yet connected */
|
||||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||||
if (rdp_inst == NULL) {
|
if (rdp_inst == NULL)
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
/* Synchronize lock key states */
|
/* Synchronize lock key states */
|
||||||
rdp_inst->input->SynchronizeEvent(rdp_inst->input, flags);
|
rdp_inst->input->SynchronizeEvent(rdp_inst->input, flags);
|
||||||
|
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#ifndef GUAC_RDP_KEYBOARD_H
|
#ifndef GUAC_RDP_KEYBOARD_H
|
||||||
#define GUAC_RDP_KEYBOARD_H
|
#define GUAC_RDP_KEYBOARD_H
|
||||||
|
|
||||||
#include "rdp_keymap.h"
|
#include "keymap.h"
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
|
@ -17,9 +17,7 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "keymap.h"
|
||||||
|
|
||||||
#include "rdp_keymap.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -17,17 +17,10 @@
|
|||||||
* under the License.
|
* 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>
|
#include <winpr/wtypes.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-wtypes.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a keysym-to-scancode mapping for RDP, with extra information
|
* Represents a keysym-to-scancode mapping for RDP, with extra information
|
@ -43,14 +43,9 @@ my @keymaps = ();
|
|||||||
open OUTPUT, ">", "_generated_keymaps.c";
|
open OUTPUT, ">", "_generated_keymaps.c";
|
||||||
print OUTPUT
|
print OUTPUT
|
||||||
'#include "config.h"' . "\n"
|
'#include "config.h"' . "\n"
|
||||||
. '#include "rdp_keymap.h"' . "\n"
|
. '#include "keymap.h"' . "\n"
|
||||||
. '#include <freerdp/input.h>' . "\n"
|
. '#include <freerdp/input.h>' . "\n"
|
||||||
. "\n"
|
|
||||||
. '#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H' . "\n"
|
|
||||||
. '#include <freerdp/locale/keyboard.h>' . "\n"
|
. '#include <freerdp/locale/keyboard.h>' . "\n"
|
||||||
. '#else' . "\n"
|
|
||||||
. '#include <freerdp/kbd/layouts.h>' . "\n"
|
|
||||||
. '#endif' . "\n"
|
|
||||||
. "\n"
|
. "\n"
|
||||||
. '#include <stddef.h>' . "\n"
|
. '#include <stddef.h>' . "\n"
|
||||||
. "\n";
|
. "\n";
|
||||||
|
74
src/protocols/rdp/log.c
Normal file
74
src/protocols/rdp/log.c
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -17,23 +17,19 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_LOG_H
|
||||||
|
#define GUAC_RDP_LOG_H
|
||||||
|
|
||||||
#ifndef __GUAC_WINPR_WTYPES_COMPAT_H
|
#include <guacamole/client.h>
|
||||||
#define __GUAC_WINPR_WTYPES_COMPAT_H
|
|
||||||
|
|
||||||
#include "config.h"
|
/**
|
||||||
|
* Redirects the core FreeRDP logging facility, wLog, such that it logs all
|
||||||
#include <freerdp/types.h>
|
* messages at the debug level using guac_client_log().
|
||||||
|
*
|
||||||
typedef uint8 BYTE;
|
* @param client
|
||||||
typedef uint8 UINT8;
|
* The guac_client that should receive all log messages.
|
||||||
typedef uint16 UINT16;
|
*/
|
||||||
typedef uint32 UINT32;
|
void guac_rdp_redirect_wlog(guac_client* client);
|
||||||
typedef uint64 UINT64;
|
|
||||||
typedef boolean BOOL;
|
|
||||||
|
|
||||||
#define TRUE true
|
|
||||||
#define FALSE false
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
122
src/protocols/rdp/ls.c
Normal file
122
src/protocols/rdp/ls.c
Normal file
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
66
src/protocols/rdp/ls.h
Normal file
66
src/protocols/rdp/ls.h
Normal file
@ -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
|
||||||
|
|
142
src/protocols/rdp/plugins/channels.c
Normal file
142
src/protocols/rdp/plugins/channels.c
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
207
src/protocols/rdp/plugins/channels.h
Normal file
207
src/protocols/rdp/plugins/channels.h
Normal file
@ -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
|
||||||
|
|
77
src/protocols/rdp/plugins/generate-entry-wrappers.pl
Executable file
77
src/protocols/rdp/plugins/generate-entry-wrappers.pl
Executable file
@ -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";
|
||||||
|
|
306
src/protocols/rdp/plugins/guac-common-svc/guac-common-svc.c
Normal file
306
src/protocols/rdp/plugins/guac-common-svc/guac-common-svc.c
Normal file
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -17,24 +17,15 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/audio-input/audio-buffer.h"
|
||||||
|
#include "plugins/guacai/guacai-messages.h"
|
||||||
#include "ai_messages.h"
|
|
||||||
#include "audio_input.h"
|
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <freerdp/freerdp.h>
|
|
||||||
#include <freerdp/constants.h>
|
|
||||||
#include <freerdp/dvc.h>
|
#include <freerdp/dvc.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#else
|
|
||||||
#include "compat/winpr-stream.h"
|
#include <stdlib.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads AUDIO_FORMAT data from the given stream into the given struct.
|
* Reads AUDIO_FORMAT data from the given stream into the given struct.
|
||||||
@ -113,6 +104,19 @@ static void guac_rdp_ai_send_incoming_data(IWTSVirtualChannel* channel) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
static void guac_rdp_ai_send_data(IWTSVirtualChannel* channel,
|
||||||
char* buffer, int length) {
|
char* buffer, int length) {
|
||||||
|
|
||||||
@ -286,7 +290,7 @@ void guac_rdp_ai_process_formats(guac_client* client,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
IWTSVirtualChannel* channel = (IWTSVirtualChannel*) data;
|
||||||
|
|
@ -17,19 +17,15 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef GUAC_RDP_AI_MESSAGES_H
|
#ifndef GUAC_RDP_PLUGINS_GUACAI_MESSAGES_H
|
||||||
#define GUAC_RDP_AI_MESSAGES_H
|
#define GUAC_RDP_PLUGINS_GUACAI_MESSAGES_H
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/audio-input/audio-buffer.h"
|
||||||
|
|
||||||
#include <freerdp/dvc.h>
|
#include <freerdp/dvc.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#else
|
#include <winpr/wtypes.h>
|
||||||
#include "compat/winpr-stream.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The format tag associated with raw wave audio (WAVE_FORMAT_PCM). This format
|
* The format tag associated with raw wave audio (WAVE_FORMAT_PCM). This format
|
||||||
@ -212,5 +208,14 @@ void guac_rdp_ai_process_open(guac_client* client,
|
|||||||
void guac_rdp_ai_process_formatchange(guac_client* client,
|
void guac_rdp_ai_process_formatchange(guac_client* client,
|
||||||
IWTSVirtualChannel* channel, wStream* stream);
|
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
|
#endif
|
||||||
|
|
@ -17,27 +17,20 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/audio-input/audio-buffer.h"
|
||||||
|
#include "plugins/guacai/guacai.h"
|
||||||
#include "ai_messages.h"
|
#include "plugins/guacai/guacai-messages.h"
|
||||||
#include "ai_service.h"
|
#include "plugins/ptr-string.h"
|
||||||
#include "audio_input.h"
|
|
||||||
#include "ptr_string.h"
|
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <freerdp/freerdp.h>
|
|
||||||
#include <freerdp/constants.h>
|
|
||||||
#include <freerdp/dvc.h>
|
#include <freerdp/dvc.h>
|
||||||
|
#include <freerdp/settings.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
#ifdef ENABLE_WINPR
|
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#else
|
#include <winpr/wtsapi.h>
|
||||||
#include "compat/winpr-stream.h"
|
#include <winpr/wtypes.h>
|
||||||
#endif
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the given data received along the AUDIO_INPUT channel of the RDP
|
* Handles the given data received along the AUDIO_INPUT channel of the RDP
|
||||||
@ -95,43 +88,9 @@ static void guac_rdp_ai_handle_data(guac_client* client,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LEGACY_IWTSVIRTUALCHANNELCALLBACK
|
|
||||||
/**
|
/**
|
||||||
* Callback which is invoked when data is received along a connection to the
|
* 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.
|
* AUDIO_INPUT plugin.
|
||||||
*
|
|
||||||
* @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.
|
|
||||||
*
|
*
|
||||||
* @param channel_callback
|
* @param channel_callback
|
||||||
* The IWTSVirtualChannelCallback structure to which this callback was
|
* The IWTSVirtualChannelCallback structure to which this callback was
|
||||||
@ -143,7 +102,7 @@ static int guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
|
|||||||
* @return
|
* @return
|
||||||
* Always zero.
|
* Always zero.
|
||||||
*/
|
*/
|
||||||
static int guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
|
static UINT guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
|
||||||
wStream* stream) {
|
wStream* stream) {
|
||||||
|
|
||||||
guac_rdp_ai_channel_callback* ai_channel_callback =
|
guac_rdp_ai_channel_callback* ai_channel_callback =
|
||||||
@ -153,10 +112,9 @@ static int guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
|
|||||||
/* Invoke generalized (API-independent) data handler */
|
/* Invoke generalized (API-independent) data handler */
|
||||||
guac_rdp_ai_handle_data(ai_channel_callback->client, channel, stream);
|
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
|
* Callback which is invoked when a connection to the AUDIO_INPUT plugin is
|
||||||
@ -169,7 +127,7 @@ static int guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
|
|||||||
* @return
|
* @return
|
||||||
* Always zero.
|
* 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* ai_channel_callback =
|
||||||
(guac_rdp_ai_channel_callback*) channel_callback;
|
(guac_rdp_ai_channel_callback*) channel_callback;
|
||||||
@ -184,7 +142,7 @@ static int guac_rdp_ai_close(IWTSVirtualChannelCallback* channel_callback) {
|
|||||||
|
|
||||||
guac_rdp_audio_buffer_end(audio_buffer);
|
guac_rdp_audio_buffer_end(audio_buffer);
|
||||||
free(ai_channel_callback);
|
free(ai_channel_callback);
|
||||||
return 0;
|
return CHANNEL_RC_OK;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,7 +177,7 @@ static int guac_rdp_ai_close(IWTSVirtualChannelCallback* channel_callback) {
|
|||||||
* @return
|
* @return
|
||||||
* Always zero.
|
* Always zero.
|
||||||
*/
|
*/
|
||||||
static int guac_rdp_ai_new_connection(
|
static UINT guac_rdp_ai_new_connection(
|
||||||
IWTSListenerCallback* listener_callback, IWTSVirtualChannel* channel,
|
IWTSListenerCallback* listener_callback, IWTSVirtualChannel* channel,
|
||||||
BYTE* data, int* accept,
|
BYTE* data, int* accept,
|
||||||
IWTSVirtualChannelCallback** channel_callback) {
|
IWTSVirtualChannelCallback** channel_callback) {
|
||||||
@ -243,7 +201,8 @@ static int guac_rdp_ai_new_connection(
|
|||||||
|
|
||||||
/* Return callback through pointer */
|
/* Return callback through pointer */
|
||||||
*channel_callback = (IWTSVirtualChannelCallback*) ai_channel_callback;
|
*channel_callback = (IWTSVirtualChannelCallback*) ai_channel_callback;
|
||||||
return 0;
|
|
||||||
|
return CHANNEL_RC_OK;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +220,7 @@ static int guac_rdp_ai_new_connection(
|
|||||||
* @return
|
* @return
|
||||||
* Always zero.
|
* Always zero.
|
||||||
*/
|
*/
|
||||||
static int guac_rdp_ai_initialize(IWTSPlugin* plugin,
|
static UINT guac_rdp_ai_initialize(IWTSPlugin* plugin,
|
||||||
IWTSVirtualChannelManager* manager) {
|
IWTSVirtualChannelManager* manager) {
|
||||||
|
|
||||||
/* Allocate new listener callback */
|
/* Allocate new listener callback */
|
||||||
@ -281,7 +240,7 @@ static int guac_rdp_ai_initialize(IWTSPlugin* plugin,
|
|||||||
manager->CreateListener(manager, "AUDIO_INPUT", 0,
|
manager->CreateListener(manager, "AUDIO_INPUT", 0,
|
||||||
(IWTSListenerCallback*) ai_listener_callback, NULL);
|
(IWTSListenerCallback*) ai_listener_callback, NULL);
|
||||||
|
|
||||||
return 0;
|
return CHANNEL_RC_OK;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,7 +254,7 @@ static int guac_rdp_ai_initialize(IWTSPlugin* plugin,
|
|||||||
* @return
|
* @return
|
||||||
* Always zero.
|
* 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_rdp_ai_plugin* ai_plugin = (guac_rdp_ai_plugin*) plugin;
|
||||||
guac_client* client = ai_plugin->client;
|
guac_client* client = ai_plugin->client;
|
||||||
@ -305,7 +264,7 @@ static int guac_rdp_ai_terminated(IWTSPlugin* plugin) {
|
|||||||
free(ai_plugin);
|
free(ai_plugin);
|
||||||
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG, "AUDIO_INPUT plugin unloaded.");
|
guac_client_log(client, GUAC_LOG_DEBUG, "AUDIO_INPUT plugin unloaded.");
|
||||||
return 0;
|
return CHANNEL_RC_OK;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,13 +274,8 @@ static int guac_rdp_ai_terminated(IWTSPlugin* plugin) {
|
|||||||
int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) {
|
int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) {
|
||||||
|
|
||||||
/* Pull guac_client from arguments */
|
/* Pull guac_client from arguments */
|
||||||
#ifdef HAVE_ADDIN_ARGV
|
|
||||||
ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);
|
ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);
|
||||||
guac_client* client = (guac_client*) guac_rdp_string_to_ptr(args->argv[1]);
|
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 */
|
/* Pull previously-allocated plugin */
|
||||||
guac_rdp_ai_plugin* ai_plugin = (guac_rdp_ai_plugin*)
|
guac_rdp_ai_plugin* ai_plugin = (guac_rdp_ai_plugin*)
|
||||||
@ -343,7 +297,7 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) {
|
|||||||
guac_client_log(client, GUAC_LOG_DEBUG, "AUDIO_INPUT plugin loaded.");
|
guac_client_log(client, GUAC_LOG_DEBUG, "AUDIO_INPUT plugin loaded.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return CHANNEL_RC_OK;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -17,14 +17,12 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef GUAC_RDP_AI_SERVICE_H
|
#ifndef GUAC_RDP_PLUGINS_GUACAI_H
|
||||||
#define GUAC_RDP_AI_SERVICE_H
|
#define GUAC_RDP_PLUGINS_GUACAI_H
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <freerdp/freerdp.h>
|
|
||||||
#include <freerdp/constants.h>
|
#include <freerdp/constants.h>
|
||||||
#include <freerdp/dvc.h>
|
#include <freerdp/dvc.h>
|
||||||
|
#include <freerdp/freerdp.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
/**
|
/**
|
@ -17,13 +17,9 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "plugins/ptr-string.h"
|
||||||
#include "ptr_string.h"
|
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
void guac_rdp_ptr_to_string(void* data, char* str) {
|
void guac_rdp_ptr_to_string(void* data, char* str) {
|
||||||
|
|
@ -17,10 +17,8 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef GUAC_RDP_PTR_STRING_H
|
#ifndef GUAC_RDP_PLUGINS_PTR_STRING_H
|
||||||
#define GUAC_RDP_PTR_STRING_H
|
#define GUAC_RDP_PLUGINS_PTR_STRING_H
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
|
@ -17,21 +17,21 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "color.h"
|
||||||
|
|
||||||
#include "client.h"
|
|
||||||
#include "common/cursor.h"
|
#include "common/cursor.h"
|
||||||
#include "common/display.h"
|
#include "common/display.h"
|
||||||
|
#include "common/surface.h"
|
||||||
|
#include "pointer.h"
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
#include "rdp_pointer.h"
|
|
||||||
|
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
|
#include <freerdp/codec/color.h>
|
||||||
#include <freerdp/freerdp.h>
|
#include <freerdp/freerdp.h>
|
||||||
|
#include <freerdp/gdi/gdi.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
#include <winpr/crt.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
BOOL guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
|
||||||
|
|
||||||
void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
|
|
||||||
|
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
@ -41,17 +41,18 @@ void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
|
|||||||
rdp_client->display, pointer->width, pointer->height);
|
rdp_client->display, pointer->width, pointer->height);
|
||||||
|
|
||||||
/* Allocate data for image */
|
/* Allocate data for image */
|
||||||
unsigned char* data =
|
unsigned char* data = _aligned_malloc(pointer->width * pointer->height * 4, 16);
|
||||||
(unsigned char*) malloc(pointer->width * pointer->height * 4);
|
|
||||||
|
|
||||||
cairo_surface_t* surface;
|
cairo_surface_t* surface;
|
||||||
|
|
||||||
/* Convert to alpha cursor if mask data present */
|
/* Convert to alpha cursor if mask data present */
|
||||||
if (pointer->andMaskData && pointer->xorMaskData)
|
if (pointer->andMaskData && pointer->xorMaskData)
|
||||||
freerdp_alpha_cursor_convert(data,
|
freerdp_image_copy_from_pointer_data(data,
|
||||||
pointer->xorMaskData, pointer->andMaskData,
|
guac_rdp_get_native_pixel_format(TRUE), 0, 0, 0,
|
||||||
pointer->width, pointer->height, pointer->xorBpp,
|
pointer->width, pointer->height, pointer->xorMaskData,
|
||||||
((rdp_freerdp_context*) context)->clrconv);
|
pointer->lengthXorMask, pointer->andMaskData,
|
||||||
|
pointer->lengthAndMask, pointer->xorBpp,
|
||||||
|
&context->gdi->palette);
|
||||||
|
|
||||||
/* Create surface from image data */
|
/* Create surface from image data */
|
||||||
surface = cairo_image_surface_create_for_data(
|
surface = cairo_image_surface_create_for_data(
|
||||||
@ -63,14 +64,16 @@ void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
|
|||||||
|
|
||||||
/* Free surface */
|
/* Free surface */
|
||||||
cairo_surface_destroy(surface);
|
cairo_surface_destroy(surface);
|
||||||
free(data);
|
_aligned_free(data);
|
||||||
|
|
||||||
/* Remember buffer */
|
/* Remember buffer */
|
||||||
((guac_rdp_pointer*) pointer)->layer = 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_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
@ -80,6 +83,8 @@ void guac_rdp_pointer_set(rdpContext* context, rdpPointer* pointer) {
|
|||||||
pointer->xPos, pointer->yPos,
|
pointer->xPos, pointer->yPos,
|
||||||
((guac_rdp_pointer*) pointer)->layer->surface);
|
((guac_rdp_pointer*) pointer)->layer->surface);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_rdp_pointer_free(rdpContext* context, rdpPointer* pointer) {
|
void guac_rdp_pointer_free(rdpContext* context, rdpPointer* pointer) {
|
||||||
@ -91,13 +96,31 @@ void guac_rdp_pointer_free(rdpContext* context, rdpPointer* pointer) {
|
|||||||
/* Free buffer */
|
/* Free buffer */
|
||||||
guac_common_display_free_buffer(rdp_client->display, 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) {
|
BOOL guac_rdp_pointer_set_null(rdpContext* context) {
|
||||||
/* STUB */
|
|
||||||
|
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) {
|
BOOL guac_rdp_pointer_set_default(rdpContext* context) {
|
||||||
/* STUB */
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
@ -17,14 +17,14 @@
|
|||||||
* under the License.
|
* 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 "common/display.h"
|
||||||
|
|
||||||
#include <freerdp/freerdp.h>
|
#include <freerdp/freerdp.h>
|
||||||
|
#include <freerdp/graphics.h>
|
||||||
|
#include <winpr/wtypes.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Guacamole-specific rdpPointer data.
|
* Guacamole-specific rdpPointer data.
|
||||||
@ -52,8 +52,11 @@ typedef struct guac_rdp_pointer {
|
|||||||
*
|
*
|
||||||
* @param pointer
|
* @param pointer
|
||||||
* The pointer to cache.
|
* 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
|
* Sets the given cached pointer as the current pointer. The given pointer must
|
||||||
@ -64,8 +67,11 @@ void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer);
|
|||||||
*
|
*
|
||||||
* @param pointer
|
* @param pointer
|
||||||
* The pointer to set as the current mouse 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
|
* Frees all Guacamole-related data associated with the given pointer, allowing
|
||||||
@ -84,8 +90,11 @@ void guac_rdp_pointer_free(rdpContext* context, rdpPointer* pointer);
|
|||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* The rdpContext associated with the current RDP session.
|
* 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
|
* Sets the system-dependent (as in dependent on the client system) default
|
||||||
@ -93,7 +102,10 @@ void guac_rdp_pointer_set_null(rdpContext* context);
|
|||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* The rdpContext associated with the current RDP session.
|
* 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
|
#endif
|
@ -17,9 +17,7 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "print-job.h"
|
||||||
#include "config.h"
|
|
||||||
#include "rdp_print_job.h"
|
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/protocol.h>
|
#include <guacamole/protocol.h>
|
@ -20,8 +20,6 @@
|
|||||||
#ifndef GUAC_RDP_PRINT_JOB_H
|
#ifndef GUAC_RDP_PRINT_JOB_H
|
||||||
#define GUAC_RDP_PRINT_JOB_H
|
#define GUAC_RDP_PRINT_JOB_H
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/stream.h>
|
#include <guacamole/stream.h>
|
||||||
#include <guacamole/user.h>
|
#include <guacamole/user.h>
|
||||||
@ -79,6 +77,9 @@ typedef enum guac_rdp_print_job_state {
|
|||||||
*/
|
*/
|
||||||
typedef struct guac_rdp_print_job {
|
typedef struct guac_rdp_print_job {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Guacamole client associated with the RDP session.
|
||||||
|
*/
|
||||||
guac_client* client;
|
guac_client* client;
|
||||||
|
|
||||||
/**
|
/**
|
@ -17,28 +17,30 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "bitmap.h"
|
||||||
|
#include "channels/audio-input/audio-buffer.h"
|
||||||
#include "audio_input.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 "client.h"
|
||||||
|
#include "color.h"
|
||||||
#include "common/cursor.h"
|
#include "common/cursor.h"
|
||||||
#include "common/display.h"
|
#include "common/display.h"
|
||||||
#include "common/recording.h"
|
#include "common/recording.h"
|
||||||
#include "dvc.h"
|
#include "config.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "gdi.h"
|
||||||
|
#include "glyph.h"
|
||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
|
#include "plugins/channels.h"
|
||||||
|
#include "pointer.h"
|
||||||
|
#include "print-job.h"
|
||||||
#include "rdp.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
|
#ifdef ENABLE_COMMON_SSH
|
||||||
#include "common-ssh/sftp.h"
|
#include "common-ssh/sftp.h"
|
||||||
@ -46,6 +48,7 @@
|
|||||||
#include "common-ssh/user.h"
|
#include "common-ssh/user.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <freerdp/addin.h>
|
||||||
#include <freerdp/cache/bitmap.h>
|
#include <freerdp/cache/bitmap.h>
|
||||||
#include <freerdp/cache/brush.h>
|
#include <freerdp/cache/brush.h>
|
||||||
#include <freerdp/cache/glyph.h>
|
#include <freerdp/cache/glyph.h>
|
||||||
@ -53,349 +56,130 @@
|
|||||||
#include <freerdp/cache/palette.h>
|
#include <freerdp/cache/palette.h>
|
||||||
#include <freerdp/cache/pointer.h>
|
#include <freerdp/cache/pointer.h>
|
||||||
#include <freerdp/channels/channels.h>
|
#include <freerdp/channels/channels.h>
|
||||||
|
#include <freerdp/client/channels.h>
|
||||||
#include <freerdp/freerdp.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/audio.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/protocol.h>
|
#include <guacamole/protocol.h>
|
||||||
#include <guacamole/socket.h>
|
#include <guacamole/socket.h>
|
||||||
#include <guacamole/timestamp.h>
|
#include <guacamole/timestamp.h>
|
||||||
|
#include <winpr/error.h>
|
||||||
#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
|
#include <winpr/synch.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/wtypes.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 <stdlib.h>
|
||||||
#include <string.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <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) {
|
BOOL rdp_freerdp_pre_connect(freerdp* instance) {
|
||||||
|
|
||||||
rdpContext* context = instance->context;
|
rdpContext* context = instance->context;
|
||||||
rdpChannels* channels = context->channels;
|
rdpGraphics* graphics = context->graphics;
|
||||||
|
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
guac_rdp_settings* settings = rdp_client->settings;
|
guac_rdp_settings* settings = rdp_client->settings;
|
||||||
|
|
||||||
rdpBitmap* bitmap;
|
/* Push desired settings to FreeRDP */
|
||||||
rdpGlyph* glyph;
|
guac_rdp_push_settings(client, settings, instance);
|
||||||
rdpPointer* pointer;
|
|
||||||
rdpPrimaryUpdate* primary;
|
|
||||||
CLRCONV* clrconv;
|
|
||||||
|
|
||||||
guac_rdp_dvc_list* dvc_list = guac_rdp_dvc_list_alloc();
|
|
||||||
|
|
||||||
#ifdef HAVE_FREERDP_REGISTER_ADDIN_PROVIDER
|
|
||||||
/* Init FreeRDP add-in provider */
|
/* Init FreeRDP add-in provider */
|
||||||
freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0);
|
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 */
|
/* Load "disp" plugin for display update */
|
||||||
if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE)
|
if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE)
|
||||||
guac_rdp_disp_load_plugin(instance->context, dvc_list);
|
guac_rdp_disp_load_plugin(context);
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Load "AUDIO_INPUT" plugin for audio input*/
|
/* Load "AUDIO_INPUT" plugin for audio input*/
|
||||||
if (settings->enable_audio_input) {
|
if (settings->enable_audio_input) {
|
||||||
rdp_client->audio_input = guac_rdp_audio_buffer_alloc();
|
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 */
|
/* Load "cliprdr" service if not disabled */
|
||||||
if (!(settings->disable_copy && settings->disable_paste)
|
if (!(settings->disable_copy && settings->disable_paste))
|
||||||
&& freerdp_channels_load_plugin(channels, instance->settings,
|
guac_rdp_clipboard_load_plugin(rdp_client->clipboard, context);
|
||||||
"cliprdr", NULL)) {
|
else
|
||||||
guac_client_log(client, GUAC_LOG_WARNING,
|
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 RDPSND/RDPDR required, load them */
|
||||||
if (settings->printing_enabled
|
if (settings->printing_enabled
|
||||||
|| settings->drive_enabled
|
|| settings->drive_enabled
|
||||||
|| settings->audio_enabled) {
|
|| settings->audio_enabled) {
|
||||||
|
guac_rdpdr_load_plugin(context);
|
||||||
/* Load RDPDR plugin */
|
guac_rdpsnd_load_plugin(context);
|
||||||
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.");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load RAIL plugin if RemoteApp in use */
|
/* Load RAIL plugin if RemoteApp in use */
|
||||||
if (settings->remote_app != NULL) {
|
if (settings->remote_app != NULL)
|
||||||
|
guac_rdp_rail_load_plugin(context);
|
||||||
#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
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load SVC plugin instances for all static channels */
|
/* Load SVC plugin instances for all static channels */
|
||||||
if (settings->svc_names != NULL) {
|
if (settings->svc_names != NULL) {
|
||||||
|
|
||||||
char** current = settings->svc_names;
|
char** current = settings->svc_names;
|
||||||
do {
|
do {
|
||||||
|
guac_rdp_pipe_svc_load_plugin(context, *current);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (*(++current) != NULL);
|
} while (*(++current) != NULL);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load DRDYNVC plugin if required */
|
/* Load plugin providing Dynamic Virtual Channel support, if required */
|
||||||
if (guac_rdp_load_drdynvc(instance->context, dvc_list))
|
if (instance->settings->SupportDynamicChannels &&
|
||||||
|
guac_freerdp_channels_load_plugin(context, "drdynvc",
|
||||||
|
instance->settings)) {
|
||||||
guac_client_log(client, GUAC_LOG_WARNING,
|
guac_client_log(client, GUAC_LOG_WARNING,
|
||||||
"Failed to load drdynvc plugin. Display update and audio "
|
"Failed to load drdynvc plugin. Display update and audio "
|
||||||
"input support will be disabled.");
|
"input support will be disabled.");
|
||||||
|
}
|
||||||
|
|
||||||
/* Dynamic virtual channel list is no longer needed */
|
/* Init FreeRDP internal GDI implementation */
|
||||||
guac_rdp_dvc_list_free(dvc_list);
|
if (!gdi_init(instance, guac_rdp_get_native_pixel_format(FALSE)))
|
||||||
|
return FALSE;
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* Set up bitmap handling */
|
/* Set up bitmap handling */
|
||||||
bitmap = calloc(1, sizeof(rdpBitmap));
|
rdpBitmap bitmap = *graphics->Bitmap_Prototype;
|
||||||
bitmap->size = sizeof(guac_rdp_bitmap);
|
bitmap.size = sizeof(guac_rdp_bitmap);
|
||||||
bitmap->New = guac_rdp_bitmap_new;
|
bitmap.New = guac_rdp_bitmap_new;
|
||||||
bitmap->Free = guac_rdp_bitmap_free;
|
bitmap.Free = guac_rdp_bitmap_free;
|
||||||
bitmap->Paint = guac_rdp_bitmap_paint;
|
bitmap.Paint = guac_rdp_bitmap_paint;
|
||||||
bitmap->Decompress = guac_rdp_bitmap_decompress;
|
bitmap.SetSurface = guac_rdp_bitmap_setsurface;
|
||||||
bitmap->SetSurface = guac_rdp_bitmap_setsurface;
|
graphics_register_bitmap(graphics, &bitmap);
|
||||||
graphics_register_bitmap(context->graphics, bitmap);
|
|
||||||
free(bitmap);
|
|
||||||
|
|
||||||
/* Set up glyph handling */
|
/* Set up glyph handling */
|
||||||
glyph = calloc(1, sizeof(rdpGlyph));
|
rdpGlyph glyph = *graphics->Glyph_Prototype;
|
||||||
glyph->size = sizeof(guac_rdp_glyph);
|
glyph.size = sizeof(guac_rdp_glyph);
|
||||||
glyph->New = guac_rdp_glyph_new;
|
glyph.New = guac_rdp_glyph_new;
|
||||||
glyph->Free = guac_rdp_glyph_free;
|
glyph.Free = guac_rdp_glyph_free;
|
||||||
glyph->Draw = guac_rdp_glyph_draw;
|
glyph.Draw = guac_rdp_glyph_draw;
|
||||||
glyph->BeginDraw = guac_rdp_glyph_begindraw;
|
glyph.BeginDraw = guac_rdp_glyph_begindraw;
|
||||||
glyph->EndDraw = guac_rdp_glyph_enddraw;
|
glyph.EndDraw = guac_rdp_glyph_enddraw;
|
||||||
graphics_register_glyph(context->graphics, glyph);
|
graphics_register_glyph(graphics, &glyph);
|
||||||
free(glyph);
|
|
||||||
|
|
||||||
/* Set up pointer handling */
|
/* Set up pointer handling */
|
||||||
pointer = calloc(1, sizeof(rdpPointer));
|
rdpPointer pointer = *graphics->Pointer_Prototype;
|
||||||
pointer->size = sizeof(guac_rdp_pointer);
|
pointer.size = sizeof(guac_rdp_pointer);
|
||||||
pointer->New = guac_rdp_pointer_new;
|
pointer.New = guac_rdp_pointer_new;
|
||||||
pointer->Free = guac_rdp_pointer_free;
|
pointer.Free = guac_rdp_pointer_free;
|
||||||
pointer->Set = guac_rdp_pointer_set;
|
pointer.Set = guac_rdp_pointer_set;
|
||||||
#ifdef HAVE_RDPPOINTER_SETNULL
|
pointer.SetNull = guac_rdp_pointer_set_null;
|
||||||
pointer->SetNull = guac_rdp_pointer_set_null;
|
pointer.SetDefault = guac_rdp_pointer_set_default;
|
||||||
#endif
|
graphics_register_pointer(graphics, &pointer);
|
||||||
#ifdef HAVE_RDPPOINTER_SETDEFAULT
|
|
||||||
pointer->SetDefault = guac_rdp_pointer_set_default;
|
|
||||||
#endif
|
|
||||||
graphics_register_pointer(context->graphics, pointer);
|
|
||||||
free(pointer);
|
|
||||||
|
|
||||||
/* Set up GDI */
|
/* Set up GDI */
|
||||||
instance->update->DesktopResize = guac_rdp_gdi_desktop_resize;
|
instance->update->DesktopResize = guac_rdp_gdi_desktop_resize;
|
||||||
instance->update->EndPaint = guac_rdp_gdi_end_paint;
|
instance->update->EndPaint = guac_rdp_gdi_end_paint;
|
||||||
instance->update->Palette = guac_rdp_gdi_palette_update;
|
|
||||||
instance->update->SetBounds = guac_rdp_gdi_set_bounds;
|
instance->update->SetBounds = guac_rdp_gdi_set_bounds;
|
||||||
|
|
||||||
primary = instance->update->primary;
|
rdpPrimaryUpdate* primary = instance->update->primary;
|
||||||
primary->DstBlt = guac_rdp_gdi_dstblt;
|
primary->DstBlt = guac_rdp_gdi_dstblt;
|
||||||
primary->PatBlt = guac_rdp_gdi_patblt;
|
|
||||||
primary->ScrBlt = guac_rdp_gdi_scrblt;
|
primary->ScrBlt = guac_rdp_gdi_scrblt;
|
||||||
primary->MemBlt = guac_rdp_gdi_memblt;
|
primary->MemBlt = guac_rdp_gdi_memblt;
|
||||||
primary->OpaqueRect = guac_rdp_gdi_opaquerect;
|
|
||||||
|
|
||||||
pointer_cache_register_callbacks(instance->update);
|
pointer_cache_register_callbacks(instance->update);
|
||||||
glyph_cache_register_callbacks(instance->update);
|
glyph_cache_register_callbacks(instance->update);
|
||||||
@ -404,39 +188,6 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
|
|||||||
offscreen_cache_register_callbacks(instance->update);
|
offscreen_cache_register_callbacks(instance->update);
|
||||||
palette_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;
|
return TRUE;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -499,8 +250,9 @@ static BOOL rdp_freerdp_authenticate(freerdp* instance, char** username,
|
|||||||
* @return
|
* @return
|
||||||
* TRUE if the certificate passes verification, FALSE otherwise.
|
* TRUE if the certificate passes verification, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
static BOOL rdp_freerdp_verify_certificate(freerdp* instance, char* subject,
|
static DWORD rdp_freerdp_verify_certificate(freerdp* instance,
|
||||||
char* issuer, char* fingerprint) {
|
const char* common_name, const char* subject, const char* issuer,
|
||||||
|
const char* fingerprint, BOOL host_mismatch) {
|
||||||
|
|
||||||
rdpContext* context = instance->context;
|
rdpContext* context = instance->context;
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
@ -510,44 +262,14 @@ static BOOL rdp_freerdp_verify_certificate(freerdp* instance, char* subject,
|
|||||||
/* Bypass validation if ignore_certificate given */
|
/* Bypass validation if ignore_certificate given */
|
||||||
if (rdp_client->settings->ignore_certificate) {
|
if (rdp_client->settings->ignore_certificate) {
|
||||||
guac_client_log(client, GUAC_LOG_INFO, "Certificate validation bypassed");
|
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");
|
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.
|
* Waits for messages from the RDP server for the given number of milliseconds.
|
||||||
*
|
*
|
||||||
@ -566,78 +288,30 @@ static int rdp_guac_client_wait_for_messages(guac_client* client,
|
|||||||
|
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||||
rdpChannels* channels = rdp_inst->context->channels;
|
|
||||||
|
|
||||||
int result;
|
HANDLE handles[GUAC_RDP_MAX_FILE_DESCRIPTORS];
|
||||||
int index;
|
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 */
|
/* Wait for data and construct a reasonable frame */
|
||||||
void* read_fds[GUAC_RDP_MAX_FILE_DESCRIPTORS];
|
int result = WaitForMultipleObjects(num_handles, handles, FALSE,
|
||||||
int read_count = 0;
|
timeout_msecs);
|
||||||
|
|
||||||
/* List of all file descriptors which data may be written to. These will
|
/* Translate WaitForMultipleObjects() return values */
|
||||||
* ultimately be ignored, but FreeRDP requires that both read and write
|
switch (result) {
|
||||||
* file descriptors be retrieved simultaneously. */
|
|
||||||
void* write_fds[GUAC_RDP_MAX_FILE_DESCRIPTORS];
|
|
||||||
int write_count = 0;
|
|
||||||
|
|
||||||
struct pollfd fds[GUAC_RDP_MAX_FILE_DESCRIPTORS];
|
/* Timeout elapsed before wait could complete */
|
||||||
|
case WAIT_TIMEOUT:
|
||||||
/* 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)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Otherwise, return as error */
|
/* Attempt to wait failed due to an error */
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
|
case WAIT_FAILED:
|
||||||
"Error waiting for file descriptor.");
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return wait result */
|
/* Wait was successful */
|
||||||
return result;
|
return 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -686,29 +360,16 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
|||||||
|
|
||||||
rdp_client->current_surface = rdp_client->display->default_surface;
|
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();
|
rdp_client->available_svc = guac_common_list_alloc();
|
||||||
|
|
||||||
#ifdef HAVE_FREERDP_CHANNELS_GLOBAL_INIT
|
|
||||||
freerdp_channels_global_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Init client */
|
/* Init client */
|
||||||
freerdp* rdp_inst = freerdp_new();
|
freerdp* rdp_inst = freerdp_new();
|
||||||
rdp_inst->PreConnect = rdp_freerdp_pre_connect;
|
rdp_inst->PreConnect = rdp_freerdp_pre_connect;
|
||||||
rdp_inst->PostConnect = rdp_freerdp_post_connect;
|
|
||||||
rdp_inst->Authenticate = rdp_freerdp_authenticate;
|
rdp_inst->Authenticate = rdp_freerdp_authenticate;
|
||||||
rdp_inst->VerifyCertificate = rdp_freerdp_verify_certificate;
|
rdp_inst->VerifyCertificate = rdp_freerdp_verify_certificate;
|
||||||
rdp_inst->ReceiveChannelData = __guac_receive_channel_data;
|
|
||||||
|
|
||||||
/* Allocate FreeRDP context */
|
/* Allocate FreeRDP context */
|
||||||
#ifdef LEGACY_FREERDP
|
|
||||||
rdp_inst->context_size = sizeof(rdp_freerdp_context);
|
|
||||||
#else
|
|
||||||
rdp_inst->ContextSize = sizeof(rdp_freerdp_context);
|
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);
|
freerdp_context_new(rdp_inst);
|
||||||
((rdp_freerdp_context*) rdp_inst->context)->client = client;
|
((rdp_freerdp_context*) rdp_inst->context)->client = client;
|
||||||
@ -720,9 +381,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
|||||||
/* Set default pointer */
|
/* Set default pointer */
|
||||||
guac_common_cursor_set_pointer(rdp_client->display->cursor);
|
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 */
|
/* Connect to RDP server */
|
||||||
if (!freerdp_connect(rdp_inst)) {
|
if (!freerdp_connect(rdp_inst)) {
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND,
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND,
|
||||||
@ -732,7 +390,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
|||||||
|
|
||||||
/* Connection complete */
|
/* Connection complete */
|
||||||
rdp_client->rdp_inst = rdp_inst;
|
rdp_client->rdp_inst = rdp_inst;
|
||||||
rdpChannels* channels = rdp_inst->context->channels;
|
|
||||||
|
|
||||||
guac_timestamp last_frame_end = guac_timestamp_current();
|
guac_timestamp last_frame_end = guac_timestamp_current();
|
||||||
|
|
||||||
@ -744,9 +401,7 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
|||||||
&& !guac_rdp_disp_reconnect_needed(rdp_client->disp)) {
|
&& !guac_rdp_disp_reconnect_needed(rdp_client->disp)) {
|
||||||
|
|
||||||
/* Update remote display size */
|
/* Update remote display size */
|
||||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
|
||||||
guac_rdp_disp_update_size(rdp_client->disp, settings, rdp_inst);
|
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 */
|
/* Wait for data and construct a reasonable frame */
|
||||||
int wait_result = rdp_guac_client_wait_for_messages(client,
|
int wait_result = rdp_guac_client_wait_for_messages(client,
|
||||||
@ -762,42 +417,15 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
|||||||
guac_timestamp frame_end;
|
guac_timestamp frame_end;
|
||||||
int frame_remaining;
|
int frame_remaining;
|
||||||
|
|
||||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
/* Check the libfreerdp fds */
|
/* Check the libfreerdp fds */
|
||||||
if (!freerdp_check_fds(rdp_inst)
|
if (!freerdp_check_event_handles(rdp_inst->context)) {
|
||||||
|| !freerdp_channels_check_fds(channels, rdp_inst)) {
|
|
||||||
|
|
||||||
/* Flag connection failure */
|
/* Flag connection failure */
|
||||||
wait_result = -1;
|
wait_result = -1;
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
break;
|
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 */
|
/* Calculate time remaining in frame */
|
||||||
frame_end = guac_timestamp_current();
|
frame_end = guac_timestamp_current();
|
||||||
frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION
|
frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION
|
||||||
@ -830,9 +458,7 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Test whether the RDP server is closing the connection */
|
/* Test whether the RDP server is closing the connection */
|
||||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
|
||||||
int connection_closing = freerdp_shall_disconnect(rdp_inst);
|
int connection_closing = freerdp_shall_disconnect(rdp_inst);
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
/* Close connection cleanly if server is disconnecting */
|
/* Close connection cleanly if server is disconnecting */
|
||||||
if (connection_closing)
|
if (connection_closing)
|
||||||
@ -858,16 +484,13 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
|||||||
guac_rdp_print_job_free(rdp_client->active_job);
|
guac_rdp_print_job_free(rdp_client->active_job);
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
/* Disconnect client and channels */
|
/* Disconnect client and channels */
|
||||||
freerdp_channels_close(channels, rdp_inst);
|
|
||||||
freerdp_channels_free(channels);
|
|
||||||
freerdp_disconnect(rdp_inst);
|
freerdp_disconnect(rdp_inst);
|
||||||
|
|
||||||
|
/* Clean up FreeRDP internal GDI implementation */
|
||||||
|
gdi_free(rdp_inst);
|
||||||
|
|
||||||
/* Clean up RDP client context */
|
/* 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);
|
freerdp_context_free(rdp_inst);
|
||||||
|
|
||||||
/* Clean up RDP client */
|
/* Clean up RDP client */
|
||||||
@ -883,8 +506,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
|||||||
/* Free display */
|
/* Free display */
|
||||||
guac_common_display_free(rdp_client->display);
|
guac_common_display_free(rdp_client->display);
|
||||||
|
|
||||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
|
||||||
|
|
||||||
/* Client is now disconnected */
|
/* Client is now disconnected */
|
||||||
guac_client_log(client, GUAC_LOG_INFO, "Internal RDP client disconnected");
|
guac_client_log(client, GUAC_LOG_INFO, "Internal RDP client disconnected");
|
||||||
|
|
||||||
|
@ -20,24 +20,19 @@
|
|||||||
#ifndef GUAC_RDP_H
|
#ifndef GUAC_RDP_H
|
||||||
#define GUAC_RDP_H
|
#define GUAC_RDP_H
|
||||||
|
|
||||||
#include "config.h"
|
#include "channels/audio-input/audio-buffer.h"
|
||||||
|
#include "channels/cliprdr.h"
|
||||||
#include "audio_input.h"
|
#include "channels/disp.h"
|
||||||
#include "common/clipboard.h"
|
#include "common/clipboard.h"
|
||||||
#include "common/display.h"
|
#include "common/display.h"
|
||||||
#include "common/list.h"
|
#include "common/list.h"
|
||||||
#include "common/recording.h"
|
#include "common/recording.h"
|
||||||
#include "common/surface.h"
|
#include "common/surface.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "fs.h"
|
||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
#include "rdp_disp.h"
|
#include "print-job.h"
|
||||||
#include "rdp_fs.h"
|
#include "settings.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>
|
|
||||||
|
|
||||||
#ifdef ENABLE_COMMON_SSH
|
#ifdef ENABLE_COMMON_SSH
|
||||||
#include "common-ssh/sftp.h"
|
#include "common-ssh/sftp.h"
|
||||||
@ -45,6 +40,12 @@
|
|||||||
#include "common-ssh/user.h"
|
#include "common-ssh/user.h"
|
||||||
#endif
|
#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 <pthread.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
@ -95,17 +96,9 @@ typedef struct guac_rdp_client {
|
|||||||
guac_rdp_keyboard* keyboard;
|
guac_rdp_keyboard* keyboard;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current clipboard contents.
|
* The current state of the clipboard and the CLIPRDR channel.
|
||||||
*/
|
*/
|
||||||
guac_common_clipboard* clipboard;
|
guac_rdp_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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Audio output, if any.
|
* Audio output, if any.
|
||||||
@ -160,13 +153,6 @@ typedef struct guac_rdp_client {
|
|||||||
*/
|
*/
|
||||||
guac_common_list* available_svc;
|
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.
|
* Common attributes for locks.
|
||||||
*/
|
*/
|
||||||
@ -191,11 +177,6 @@ typedef struct rdp_freerdp_context {
|
|||||||
*/
|
*/
|
||||||
guac_client* client;
|
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.
|
* The current color palette, as received from the RDP server.
|
||||||
*/
|
*/
|
||||||
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user