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=" \
|
||||
autoconf \
|
||||
automake \
|
||||
freerdp2-dev \
|
||||
gcc \
|
||||
libcairo2-dev \
|
||||
libfreerdp-dev \
|
||||
libjpeg62-turbo-dev \
|
||||
libossp-uuid-dev \
|
||||
libpango1.0-dev \
|
||||
@ -71,7 +71,7 @@ RUN ${PREFIX_DIR}/bin/build-guacd.sh "$BUILD_DIR" "$PREFIX_DIR"
|
||||
RUN ${PREFIX_DIR}/bin/list-dependencies.sh \
|
||||
${PREFIX_DIR}/sbin/guacd \
|
||||
${PREFIX_DIR}/lib/libguac-client-*.so \
|
||||
${PREFIX_DIR}/lib/freerdp/guac*.so \
|
||||
${PREFIX_DIR}/lib/freerdp2/guac*.so \
|
||||
> ${PREFIX_DIR}/DEPENDENCIES
|
||||
|
||||
# Use same Debian as the base for the runtime image
|
||||
@ -92,7 +92,6 @@ ENV GUACD_LOG_LEVEL=info
|
||||
ARG RUNTIME_DEPENDENCIES=" \
|
||||
ca-certificates \
|
||||
ghostscript \
|
||||
libfreerdp-plugins-standard \
|
||||
fonts-liberation \
|
||||
fonts-dejavu \
|
||||
xfonts-terminus"
|
||||
@ -108,7 +107,7 @@ RUN apt-get update && \
|
||||
|
||||
# Link FreeRDP plugins into proper path
|
||||
RUN ${PREFIX_DIR}/bin/link-freerdp-plugins.sh \
|
||||
${PREFIX_DIR}/lib/freerdp/guac*.so
|
||||
${PREFIX_DIR}/lib/freerdp2/libguac*.so
|
||||
|
||||
# Expose the default listener port
|
||||
EXPOSE 4822
|
||||
|
610
configure.ac
610
configure.ac
@ -555,537 +555,143 @@ then
|
||||
fi
|
||||
|
||||
#
|
||||
# FreeRDP
|
||||
# FreeRDP 2 (libfreerdp2, libfreerdp-client2, and libwinpr2)
|
||||
#
|
||||
|
||||
have_freerdp=disabled
|
||||
RDP_LIBS=
|
||||
WINPR_LIBS=
|
||||
have_freerdp2=disabled
|
||||
AC_ARG_WITH([rdp],
|
||||
[AS_HELP_STRING([--with-rdp],
|
||||
[support RDP @<:@default=check@:>@])],
|
||||
[],
|
||||
[with_rdp=check])
|
||||
|
||||
# Preserve CPPFLAGS so it can be restored later, following the addition of
|
||||
# options specific to FreeRDP tests
|
||||
OLDCPPFLAGS="$CPPFLAGS"
|
||||
|
||||
if test "x$with_rdp" != "xno"
|
||||
then
|
||||
have_winpr=yes
|
||||
have_freerdp=yes
|
||||
legacy_freerdp_extensions=no
|
||||
rdpsettings_interface=unknown
|
||||
rdpsettings_audiocapture=yes
|
||||
rdpsettings_audioplayback=yes
|
||||
rdpsettings_deviceredirection=yes
|
||||
freerdp_interface=unknown
|
||||
event_interface=unknown
|
||||
|
||||
# libfreerdp-core / libfreerdp
|
||||
AC_CHECK_LIB([freerdp-core], [freerdp_new],
|
||||
[RDP_LIBS="$RDP_LIBS -lfreerdp-core"],
|
||||
[AC_CHECK_LIB([freerdp], [freerdp_new],
|
||||
[RDP_LIBS="$RDP_LIBS -lfreerdp -lfreerdp-client"],
|
||||
[AC_MSG_WARN([
|
||||
have_freerdp2=yes
|
||||
PKG_CHECK_MODULES([RDP], [freerdp2 freerdp-client2 winpr2],
|
||||
[CPPFLAGS="${RDP_CFLAGS} -Werror $CPPFLAGS"],
|
||||
[AC_MSG_WARN([
|
||||
--------------------------------------------
|
||||
Unable to find libfreerdp-core / libfreerdp
|
||||
Unable to find FreeRDP (libfreerdp2 / libfreerdp-client2 / libwinpr2)
|
||||
RDP will be disabled.
|
||||
--------------------------------------------])
|
||||
have_freerdp=no])])
|
||||
fi
|
||||
|
||||
|
||||
# libfreerdp-cache
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_LIB([freerdp-cache], [glyph_cache_register_callbacks],
|
||||
[RDP_LIBS="$RDP_LIBS -lfreerdp-cache"])
|
||||
fi
|
||||
|
||||
# libfreerdp-channels (1.0) / libfreerdp-client + libfreerdp-core (1.1)
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_LIB([freerdp-client], [freerdp_channels_new],
|
||||
[RDP_LIBS="$RDP_LIBS -lfreerdp-client"],
|
||||
[AC_CHECK_LIB([freerdp-channels], [freerdp_channels_new],
|
||||
[RDP_LIBS="$RDP_LIBS -lfreerdp-channels"
|
||||
legacy_freerdp_extensions=yes])],
|
||||
[-lfreerdp-core])
|
||||
fi
|
||||
|
||||
# libfreerdp-utils
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_LIB([freerdp-utils], [svc_plugin_init],
|
||||
[RDP_LIBS="$RDP_LIBS -lfreerdp-utils"])
|
||||
fi
|
||||
|
||||
# libfreerdp-codec
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_LIB([freerdp-codec], [freerdp_image_convert],
|
||||
[RDP_LIBS="$RDP_LIBS -lfreerdp-codec"])
|
||||
have_freerdp2=no])
|
||||
fi
|
||||
|
||||
# Available color conversion functions
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
if test "x$have_freerdp2" = "xyes"
|
||||
then
|
||||
AC_CHECK_DECL([freerdp_convert_gdi_order_color],
|
||||
[AC_DEFINE([HAVE_FREERDP_CONVERT_GDI_ORDER_COLOR],,
|
||||
[Whether freerdp_convert_gdi_order_color() is defined])],,
|
||||
[#include <freerdp/codec/color.h>])
|
||||
|
||||
AC_CHECK_DECL([freerdp_color_convert_drawing_order_color_to_gdi_color],
|
||||
[AC_DEFINE([HAVE_FREERDP_COLOR_CONVERT_DRAWING_ORDER_COLOR_TO_GDI_COLOR],,
|
||||
[Whether freerdp_color_convert_drawing_order_color_to_gdi_color() is defined])],,
|
||||
[#include <freerdp/codec/color.h>])
|
||||
# FreeRDP 2.0.0-rc3 and older referred to FreeRDPConvertColor() as
|
||||
# ConvertColor()
|
||||
AC_CHECK_DECL([FreeRDPConvertColor],
|
||||
[AC_DEFINE([HAVE_FREERDPCONVERTCOLOR],,
|
||||
[Whether FreeRDPConvertColor() is defined])],,
|
||||
[#include <freerdp/codec/color.h>])
|
||||
|
||||
fi
|
||||
|
||||
# Check for interval polling in plugins
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
# Glyph callback variants
|
||||
if test "x${have_freerdp2}" = "xyes"
|
||||
then
|
||||
AC_CHECK_MEMBERS([rdpSvcPlugin.interval_ms],,,
|
||||
[[#include <freerdp/utils/svc_plugin.h>]])
|
||||
|
||||
# FreeRDP 2.0.0-rc3 and older used UINT32 for integer parameters to all
|
||||
# rdpGlyph callbacks
|
||||
AC_MSG_CHECKING([whether rdpGlyph callbacks accept INT32 integer parameters])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/graphics.h>
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
BOOL test_begindraw(rdpContext* context, INT32 x, INT32 y,
|
||||
INT32 width, INT32 height, UINT32 fgcolor, UINT32 bgcolor,
|
||||
BOOL redundant);
|
||||
|
||||
rdpGlyph glyph = {
|
||||
.BeginDraw = test_begindraw
|
||||
};
|
||||
|
||||
int main() {
|
||||
return (int) glyph.BeginDraw(NULL, 0, 0, 0, 0, 0, 0, FALSE);
|
||||
}
|
||||
|
||||
]])],
|
||||
[AC_MSG_RESULT([yes])]
|
||||
[AC_DEFINE([FREERDP_GLYPH_CALLBACKS_ACCEPT_INT32],,
|
||||
[Whether rdpGlyph callbacks accept INT32 integer parameters])],
|
||||
[AC_MSG_RESULT([no])])
|
||||
|
||||
fi
|
||||
|
||||
# Keyboard layout header
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
# CLIPRDR callback variants
|
||||
if test "x${have_freerdp2}" = "xyes"
|
||||
then
|
||||
AC_CHECK_HEADERS([freerdp/locale/keyboard.h],,
|
||||
[AC_CHECK_HEADERS([freerdp/kbd/layouts.h],,
|
||||
[AC_MSG_WARN([
|
||||
--------------------------------------------
|
||||
Unable to find keyboard layout headers
|
||||
RDP will be disabled.
|
||||
--------------------------------------------])
|
||||
have_freerdp=no])])
|
||||
|
||||
# FreeRDP 2.0.0-rc3 and older did not use const for CLIPRDR callbacks
|
||||
AC_MSG_CHECKING([whether CLIPRDR callbacks require const for their final parameter])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
|
||||
|
||||
#include <freerdp/client/cliprdr.h>
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
UINT test_monitor_ready(CliprdrClientContext* cliprdr,
|
||||
const CLIPRDR_MONITOR_READY* monitor_ready);
|
||||
|
||||
CliprdrClientContext context = {
|
||||
.MonitorReady = test_monitor_ready
|
||||
};
|
||||
|
||||
int main() {
|
||||
return (int) context.MonitorReady(NULL, NULL);
|
||||
}
|
||||
|
||||
]])],
|
||||
[AC_MSG_RESULT([yes])]
|
||||
[AC_DEFINE([FREERDP_CLIPRDR_CALLBACKS_REQUIRE_CONST],,
|
||||
[Whether CLIPRDR callbacks require const for the final parameter])],
|
||||
[AC_MSG_RESULT([no])])
|
||||
|
||||
fi
|
||||
|
||||
# New headers defining addins
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
# RAIL callback variants
|
||||
if test "x${have_freerdp2}" = "xyes"
|
||||
then
|
||||
AC_CHECK_HEADERS([freerdp/addin.h freerdp/client/channels.h])
|
||||
|
||||
# FreeRDP 2.0.0-rc3 and older did not use const for RAIL callbacks
|
||||
AC_MSG_CHECKING([whether RAIL callbacks require const for their final parameter])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
|
||||
|
||||
#include <freerdp/client/rail.h>
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
UINT test_server_handshake(RailClientContext* rail,
|
||||
const RAIL_HANDSHAKE_ORDER* handshake);
|
||||
|
||||
RailClientContext context = {
|
||||
.ServerHandshake = test_server_handshake
|
||||
};
|
||||
|
||||
int main() {
|
||||
return (int) context.ServerHandshake(NULL, NULL);
|
||||
}
|
||||
|
||||
]])],
|
||||
[AC_MSG_RESULT([yes])]
|
||||
[AC_DEFINE([FREERDP_RAIL_CALLBACKS_REQUIRE_CONST],,
|
||||
[Whether RAIL callbacks require const for the final parameter])],
|
||||
[AC_MSG_RESULT([no])])
|
||||
|
||||
fi
|
||||
|
||||
# Header defining cliprdr
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_HEADERS([freerdp/client/cliprdr.h],,
|
||||
[AC_CHECK_HEADERS([freerdp/plugins/cliprdr.h],,
|
||||
[AC_MSG_WARN([
|
||||
--------------------------------------------
|
||||
Unable to find cliprdr headers
|
||||
RDP will be disabled.
|
||||
--------------------------------------------])
|
||||
have_freerdp=no],
|
||||
[#include <freerdp/types.h>])],
|
||||
[#include <winpr/wtypes.h>
|
||||
#include <winpr/collections.h>])
|
||||
fi
|
||||
# Restore CPPFLAGS, removing FreeRDP-specific options needed for testing
|
||||
CPPFLAGS="$OLDCPPFLAGS"
|
||||
|
||||
# Header defining display update channel
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_HEADERS([freerdp/client/disp.h],
|
||||
[AC_DEFINE([HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT],,
|
||||
[Whether FreeRDP supports the display update channel])]
|
||||
[AC_CHECK_MEMBERS([rdpSettings.SupportDisplayControl],,,
|
||||
[[#include <freerdp/freerdp.h>]])],,
|
||||
[#include <winpr/wtypes.h>
|
||||
#include <winpr/collections.h>])
|
||||
fi
|
||||
|
||||
# Support for RDP gateways
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_MEMBERS([rdpSettings.GatewayEnabled],
|
||||
[AC_DEFINE([HAVE_FREERDP_GATEWAY_SUPPORT],,
|
||||
[Whether FreeRDP supports RDP gateways])],,
|
||||
[[#include <freerdp/freerdp.h>]])
|
||||
fi
|
||||
|
||||
# Support for load balancing via connection brokers
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_MEMBERS([rdpSettings.LoadBalanceInfo],
|
||||
[AC_DEFINE([HAVE_FREERDP_LOAD_BALANCER_SUPPORT],,
|
||||
[Whether FreeRDP supports load balancers])],,
|
||||
[[#include <freerdp/freerdp.h>]])
|
||||
fi
|
||||
|
||||
# Support for "PubSub" event system
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_DECL([PubSub_SubscribeChannelConnected],
|
||||
[AC_DEFINE([HAVE_FREERDP_EVENT_PUBSUB],,
|
||||
[Whether this version of FreeRDP provides the PubSub event system])],,
|
||||
[#include <freerdp/event.h>])
|
||||
fi
|
||||
|
||||
# Addin registration variations
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_DECL([freerdp_register_addin_provider],
|
||||
[AC_DEFINE([HAVE_FREERDP_REGISTER_ADDIN_PROVIDER],,
|
||||
[Whether freerdp_register_addin_provider() is defined])],,
|
||||
[#include <freerdp/addin.h>])
|
||||
|
||||
AC_CHECK_DECL([freerdp_channels_global_init],
|
||||
[AC_DEFINE([HAVE_FREERDP_CHANNELS_GLOBAL_INIT],,
|
||||
[Whether freerdp_channels_global_init() is defined])],,
|
||||
[#include <freerdp/channels/channels.h>])
|
||||
fi
|
||||
|
||||
# Availability of ADDIN_ARGV structure for configuring plugins
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_TYPE([ADDIN_ARGV],
|
||||
[AC_DEFINE([HAVE_ADDIN_ARGV],,
|
||||
[Whether the ADDIN_ARGV type is available])],,
|
||||
[#include <freerdp/settings.h>])
|
||||
fi
|
||||
|
||||
#
|
||||
# FreeRDP: WinPR
|
||||
#
|
||||
|
||||
# Check for stream support via WinPR
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_HEADER(winpr/stream.h,,
|
||||
[have_winpr=no,
|
||||
AC_CHECK_DECL([stream_write_uint8],,
|
||||
[AC_MSG_WARN([
|
||||
--------------------------------------------
|
||||
Unable to find stream support
|
||||
RDP will be disabled.
|
||||
--------------------------------------------])
|
||||
have_freerdp=no],
|
||||
[#include <freerdp/utils/stream.h>])])
|
||||
fi
|
||||
|
||||
# Find location of Stream_New and Stream_free
|
||||
if test "x${have_freerdp}" = "xyes" -a "x${have_winpr}" = "xyes"
|
||||
then
|
||||
AC_CHECK_LIB([winpr], [Stream_New, Stream_Free],
|
||||
[WINPR_LIBS="$WINPR_LIBS -lwinpr"],
|
||||
[AC_CHECK_LIB([winpr-utils], [Stream_New, Stream_Free],
|
||||
[WINPR_LIBS="$WINPR_LIBS -lwinpr-utils"],
|
||||
[AC_MSG_WARN([
|
||||
------------------------------------------
|
||||
Unable to locate stream functions in winpr
|
||||
libraries. RDP will be disabled.
|
||||
------------------------------------------])
|
||||
have_freerdp=no])])
|
||||
fi
|
||||
|
||||
# Check for types in WinPR
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_HEADER(winpr/wtypes.h,,
|
||||
[have_winpr=no,
|
||||
AC_CHECK_HEADER(freerdp/types.h,,
|
||||
[AC_MSG_WARN([
|
||||
--------------------------------------------
|
||||
Unable to find type definitions
|
||||
RDP will be disabled.
|
||||
--------------------------------------------])
|
||||
have_freerdp=no])])
|
||||
fi
|
||||
|
||||
if test "x${have_freerdp}" = "xyes" -a "x${have_winpr}" = "xyes"
|
||||
then
|
||||
AC_DEFINE([ENABLE_WINPR],,
|
||||
[Whether library support for WinPR types was found])
|
||||
fi
|
||||
|
||||
#
|
||||
# FreeRDP: freerdp
|
||||
#
|
||||
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
# Check for current (as of 1.1) freerdp interface
|
||||
AC_CHECK_MEMBERS([freerdp.ContextSize],
|
||||
[freerdp_interface=stable],,
|
||||
[[#include <freerdp/freerdp.h>]])
|
||||
|
||||
# If not current, check for legacy interface
|
||||
if test "x${freerdp_interface}" = "xunknown"
|
||||
then
|
||||
AC_CHECK_MEMBERS([freerdp.context_size],
|
||||
[freerdp_interface=legacy],,
|
||||
[[#include <freerdp/freerdp.h>]])
|
||||
fi
|
||||
|
||||
# Set defines based on interface type, warn if unknown
|
||||
if test "x${freerdp_interface}" = "xlegacy"; then
|
||||
AC_DEFINE([LEGACY_FREERDP],,
|
||||
[Whether the older version of the FreeRDP API was found])
|
||||
elif test "x${freerdp_interface}" = "xunknown"; then
|
||||
AC_MSG_WARN([
|
||||
--------------------------------------------
|
||||
Unknown FreeRDP interface
|
||||
RDP will be disabled.
|
||||
--------------------------------------------])
|
||||
have_freerdp=no
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
# FreeRDP: rdpSettings
|
||||
#
|
||||
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
# Check for current (as of 1.1) rdpSettings interface
|
||||
AC_CHECK_MEMBERS([rdpSettings.Width,
|
||||
rdpSettings.Height,
|
||||
rdpSettings.FastPathInput,
|
||||
rdpSettings.FastPathOutput,
|
||||
rdpSettings.SendPreconnectionPdu,
|
||||
rdpSettings.OrderSupport],
|
||||
[rdpsettings_interface=stable],,
|
||||
[[#include <freerdp/freerdp.h>]])
|
||||
|
||||
# If not current, check for legacy interface
|
||||
if test "x${rdpsettings_interface}" = "xunknown"
|
||||
then
|
||||
AC_CHECK_MEMBERS([rdpSettings.width,
|
||||
rdpSettings.height,
|
||||
rdpSettings.order_support],
|
||||
[rdpsettings_interface=legacy],,
|
||||
[[#include <freerdp/freerdp.h>]])
|
||||
fi
|
||||
|
||||
# Set defines based on interface type, warn if unknown
|
||||
if test "x${rdpsettings_interface}" = "xlegacy"; then
|
||||
AC_DEFINE([LEGACY_RDPSETTINGS],,
|
||||
[Whether the legacy version of the rdpSettings API was found])
|
||||
|
||||
# Legacy interface may not have AudioPlayback settings
|
||||
AC_CHECK_MEMBERS([rdpSettings.audio_playback],,
|
||||
[rdpsettings_audioplayback=no],
|
||||
[[#include <freerdp/freerdp.h>]])
|
||||
|
||||
# Legacy interface may not have AudioCapture settings
|
||||
AC_CHECK_MEMBERS([rdpSettings.audio_capture],,
|
||||
[rdpsettings_audiocapture=no],
|
||||
[[#include <freerdp/freerdp.h>]])
|
||||
|
||||
# Legacy interface may not have DeviceRedirection settings
|
||||
AC_CHECK_MEMBERS([rdpSettings.device_redirection],,
|
||||
[rdpsettings_deviceredirection=no],
|
||||
[[#include <freerdp/freerdp.h>]])
|
||||
|
||||
elif test "x${rdpsettings_interface}" = "xunknown"; then
|
||||
AC_MSG_WARN([
|
||||
--------------------------------------------
|
||||
Unknown rdpSettings interface
|
||||
RDP will be disabled.
|
||||
--------------------------------------------])
|
||||
have_freerdp=no
|
||||
fi
|
||||
fi
|
||||
|
||||
# Activate audio playback settings if present
|
||||
if test "x${have_freerdp}" = "xyes" -a "x${rdpsettings_audioplayback}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_RDPSETTINGS_AUDIOPLAYBACK],,
|
||||
[Whether the rdpSettings structure has AudioPlayback settings])
|
||||
fi
|
||||
|
||||
# Activate audio capture settings if present
|
||||
if test "x${have_freerdp}" = "xyes" -a "x${rdpsettings_audiocapture}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_RDPSETTINGS_AUDIOCAPTURE],,
|
||||
[Whether the rdpSettings structure has AudioCapture settings])
|
||||
fi
|
||||
|
||||
# Activate device redirection settings if present
|
||||
if test "x${have_freerdp}" = "xyes" -a "x${rdpsettings_deviceredirection}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_RDPSETTINGS_DEVICEREDIRECTION],,
|
||||
[Whether the rdpSettings structure has DeviceRedirection settings])
|
||||
fi
|
||||
|
||||
# Check if the type CHANNEL_ENTRY_POINTS_FREERDP exists, if not define it to CHANNEL_ENTRY_POINTS_EX
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_TYPE([CHANNEL_ENTRY_POINTS_FREERDP],,
|
||||
AC_DEFINE([CHANNEL_ENTRY_POINTS_FREERDP],[CHANNEL_ENTRY_POINTS_EX], [Type compatibility]),
|
||||
[[#include <freerdp/svc.h>]])
|
||||
fi
|
||||
|
||||
# Check if the freerdp version header exists
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_HEADERS([freerdp/version.h])
|
||||
fi
|
||||
|
||||
#
|
||||
# FreeRDP: rdpBitmap
|
||||
#
|
||||
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_MSG_CHECKING([whether rdpBitmap.Decompress() requires the codec_id])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <winpr/wtypes.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
void __decompress(rdpContext* context,
|
||||
rdpBitmap* bitmap,
|
||||
UINT8* data,
|
||||
int width,
|
||||
int height,
|
||||
int bpp,
|
||||
int length,
|
||||
BOOL compressed,
|
||||
int codec_id);
|
||||
rdpBitmap b = { .Decompress = __decompress };]])],
|
||||
[AC_MSG_RESULT([yes])],
|
||||
[AC_MSG_RESULT([no])
|
||||
AC_DEFINE([LEGACY_RDPBITMAP],,
|
||||
[Whether the legacy rdpBitmap API was found])])
|
||||
fi
|
||||
|
||||
#
|
||||
# FreeRDP: IWTSVirtualChannelCallback
|
||||
#
|
||||
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_MSG_CHECKING([whether IWTSVirtualChannelCallback.OnDataReceived() uses a wStream])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <winpr/wtypes.h>
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
int __data_received(
|
||||
IWTSVirtualChannelCallback* channel_callback,
|
||||
wStream* stream);
|
||||
IWTSVirtualChannelCallback cb = {
|
||||
.OnDataReceived = __data_received
|
||||
};
|
||||
int main() {
|
||||
return
|
||||
cb.OnDataReceived(NULL, NULL);
|
||||
}]])],
|
||||
[AC_MSG_RESULT([yes])],
|
||||
[AC_MSG_RESULT([no])
|
||||
AC_DEFINE([LEGACY_IWTSVIRTUALCHANNELCALLBACK],,
|
||||
[Whether the legacy IWTSVirtualChannelCallback API was found])])
|
||||
fi
|
||||
|
||||
#
|
||||
# FreeRDP: Decompression function variants
|
||||
#
|
||||
|
||||
# Check whether interleaved_decompress() can handle the palette
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_MSG_CHECKING([whether interleaved_decompress() accepts an additional palette parameter])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <freerdp/codec/interleaved.h>
|
||||
|
||||
int main() {
|
||||
BYTE* palette = NULL;
|
||||
interleaved_decompress(NULL, NULL, 0, 0, NULL, 0, 0, 0, 0, 0, 0, palette);
|
||||
}]])],
|
||||
[AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([INTERLEAVED_DECOMPRESS_TAKES_PALETTE],,
|
||||
[Whether interleaved_decompress() accepts an additional palette parameter])],
|
||||
[AC_MSG_RESULT([no])])
|
||||
fi
|
||||
|
||||
# Check whether planar_decompress() will handle flipping
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_MSG_CHECKING([whether planar_decompress() can flip])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <freerdp/codec/planar.h>
|
||||
|
||||
int main() {
|
||||
BOOL* flip = TRUE;
|
||||
planar_decompress(NULL, NULL, 0, NULL, 0, 0, 0, 0, 0, 0, flip);
|
||||
}]])],
|
||||
[AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([PLANAR_DECOMPRESS_CAN_FLIP],,
|
||||
[Whether planar_decompress() can flip])],
|
||||
[AC_MSG_RESULT([no])])
|
||||
fi
|
||||
|
||||
#
|
||||
# FreeRDP: rdpContext
|
||||
#
|
||||
|
||||
# Check for rdpContext.codecs
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_MEMBERS([rdpContext.codecs],
|
||||
[AC_DEFINE([FREERDP_BITMAP_REQUIRES_ALIGNED_MALLOC],,
|
||||
[Whether this version of FreeRDP requires _aligned_malloc() for bitmap data])],,
|
||||
[[#include <freerdp/freerdp.h>]])
|
||||
fi
|
||||
|
||||
#
|
||||
# FreeRDP: rdpPalette
|
||||
#
|
||||
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_MSG_CHECKING([whether rdpPalette.entries is static])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <freerdp/update.h>
|
||||
rdpPalette p;
|
||||
PALETTE_ENTRY* foo = p.entries;]])],
|
||||
[AC_MSG_RESULT([yes])],
|
||||
[AC_MSG_RESULT([no])
|
||||
AC_DEFINE([LEGACY_RDPPALETTE],,
|
||||
[Whether the legacy rdpPalette API was found])])
|
||||
fi
|
||||
|
||||
#
|
||||
# FreeRDP: rdpPointer
|
||||
#
|
||||
|
||||
# Check for SetDefault and SetNull members of rdpPointer
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_MEMBERS([rdpPointer.SetDefault,
|
||||
rdpPointer.SetNull],
|
||||
,,
|
||||
[[#include <freerdp/freerdp.h>]])
|
||||
fi
|
||||
|
||||
#
|
||||
# FreeRDP: wMessage / RDP_EVENT
|
||||
#
|
||||
|
||||
# Check for current (as of 1.1) wMessage interface
|
||||
if test "x${have_freerdp}" = "xyes"
|
||||
then
|
||||
AC_CHECK_MEMBERS([wMessage.id],
|
||||
[event_interface=stable],,
|
||||
[[#include <winpr/collections.h>]])
|
||||
|
||||
# If not current, check for legacy (RDP_EVENT) interface
|
||||
if test "x${event_interface}" = "xunknown"
|
||||
then
|
||||
AC_CHECK_MEMBERS([RDP_EVENT.event_class],
|
||||
[event_interface=legacy],,
|
||||
[[#include <freerdp/types.h>]])
|
||||
fi
|
||||
|
||||
# Set defines based on interface type, warn if unknown
|
||||
if test "x${event_interface}" = "xlegacy"; then
|
||||
AC_DEFINE([LEGACY_EVENT],,
|
||||
[Whether the legacy RDP_EVENT API was found])
|
||||
elif test "x${event_interface}" = "xunknown"; then
|
||||
AC_MSG_WARN([
|
||||
--------------------------------------------
|
||||
Unknown event interface
|
||||
RDP will be disabled.
|
||||
--------------------------------------------])
|
||||
have_freerdp=no
|
||||
fi
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([LEGACY_FREERDP_EXTENSIONS], [test "x${legacy_freerdp_extensions}" = "xyes"])
|
||||
AM_CONDITIONAL([ENABLE_WINPR], [test "x${have_winpr}" = "xyes"])
|
||||
AM_CONDITIONAL([ENABLE_RDP], [test "x${have_freerdp}" = "xyes"])
|
||||
|
||||
AC_SUBST(RDP_LIBS)
|
||||
AC_SUBST(WINPR_LIBS)
|
||||
AM_CONDITIONAL([ENABLE_RDP], [test "x${have_freerdp2}" = "xyes"])
|
||||
|
||||
#
|
||||
# libssh2
|
||||
@ -1426,7 +1032,7 @@ $PACKAGE_NAME version $PACKAGE_VERSION
|
||||
|
||||
Library status:
|
||||
|
||||
freerdp ............. ${have_freerdp}
|
||||
freerdp2 ............ ${have_freerdp2}
|
||||
pango ............... ${have_pango}
|
||||
libavcodec .......... ${have_libavcodec}
|
||||
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
|
||||
* dirty rectangle, to be eventually flushed as a "png" instruction.
|
||||
* dirty rectangle, to be eventually flushed as image data, or would be best
|
||||
* kept independent of the current rectangle.
|
||||
*
|
||||
* @param surface The surface to be queried.
|
||||
* @param rect The update rectangle.
|
||||
* @param rect_only Non-zero if this update, by its nature, contains only
|
||||
* metainformation about the update's rectangle, zero if
|
||||
* the update also contains image data.
|
||||
* @return Non-zero if the update should be combined with any existing update,
|
||||
* zero otherwise.
|
||||
* @param surface
|
||||
* The surface being updated.
|
||||
*
|
||||
* @param rect
|
||||
* The bounding rectangle of the update being made to the surface.
|
||||
*
|
||||
* @param rect_only
|
||||
* Non-zero if this update, by its nature, contains only metainformation
|
||||
* about the update's bounding rectangle, zero if the update also contains
|
||||
* image data.
|
||||
*
|
||||
* @return
|
||||
* Non-zero if the update should be combined with any existing update, zero
|
||||
* otherwise.
|
||||
*/
|
||||
static int __guac_common_should_combine(guac_common_surface* surface, const guac_common_rect* rect, int rect_only) {
|
||||
|
||||
/* Always favor combining updates if surface is currently a purely
|
||||
* server-side scratch area */
|
||||
if (!surface->realized)
|
||||
return 1;
|
||||
|
||||
if (surface->dirty) {
|
||||
|
||||
int combined_cost, dirty_cost, update_cost;
|
||||
|
@ -70,7 +70,7 @@ while [ -n "$1" ]; do
|
||||
|
||||
# Determine correct install location for FreeRDP plugins
|
||||
FREERDP_DIR="$(where_is_freerdp "$1")"
|
||||
FREERDP_PLUGIN_DIR="${FREERDP_DIR}/freerdp"
|
||||
FREERDP_PLUGIN_DIR="${FREERDP_DIR}/freerdp2"
|
||||
|
||||
# Add symbolic link if necessary
|
||||
if [ ! -e "$FREERDP_PLUGIN_DIR/$(basename "$1")" ]; then
|
||||
|
1
src/protocols/rdp/.gitignore
vendored
1
src/protocols/rdp/.gitignore
vendored
@ -4,5 +4,6 @@ _generated_runner.c
|
||||
test_rdp
|
||||
|
||||
# Autogenerated sources
|
||||
_generated_channel_entry_wrappers.c
|
||||
_generated_keymaps.c
|
||||
|
||||
|
@ -29,218 +29,172 @@ ACLOCAL_AMFLAGS = -I m4
|
||||
lib_LTLIBRARIES = libguac-client-rdp.la
|
||||
SUBDIRS = . tests
|
||||
|
||||
nodist_libguac_client_rdp_la_SOURCES = \
|
||||
_generated_keymaps.c
|
||||
|
||||
libguac_client_rdp_la_SOURCES = \
|
||||
audio_input.c \
|
||||
client.c \
|
||||
decompose.c \
|
||||
dvc.c \
|
||||
error.c \
|
||||
input.c \
|
||||
keyboard.c \
|
||||
ptr_string.c \
|
||||
rdp.c \
|
||||
rdp_bitmap.c \
|
||||
rdp_cliprdr.c \
|
||||
rdp_color.c \
|
||||
rdp_disp.c \
|
||||
rdp_fs.c \
|
||||
rdp_gdi.c \
|
||||
rdp_glyph.c \
|
||||
rdp_keymap.c \
|
||||
rdp_print_job.c \
|
||||
rdp_pointer.c \
|
||||
rdp_rail.c \
|
||||
rdp_settings.c \
|
||||
rdp_stream.c \
|
||||
rdp_svc.c \
|
||||
resolution.c \
|
||||
unicode.c \
|
||||
user.c
|
||||
|
||||
guacai_sources = \
|
||||
audio_input.c \
|
||||
guac_ai/ai_messages.c \
|
||||
guac_ai/ai_service.c \
|
||||
ptr_string.c
|
||||
|
||||
guacsvc_sources = \
|
||||
guac_svc/svc_service.c \
|
||||
rdp_svc.c
|
||||
|
||||
guacsnd_sources = \
|
||||
guac_rdpsnd/rdpsnd_messages.c \
|
||||
guac_rdpsnd/rdpsnd_service.c
|
||||
|
||||
guacdr_sources = \
|
||||
guac_rdpdr/rdpdr_fs_messages.c \
|
||||
guac_rdpdr/rdpdr_fs_messages_dir_info.c \
|
||||
guac_rdpdr/rdpdr_fs_messages_file_info.c \
|
||||
guac_rdpdr/rdpdr_fs_messages_vol_info.c \
|
||||
guac_rdpdr/rdpdr_fs_service.c \
|
||||
guac_rdpdr/rdpdr_messages.c \
|
||||
guac_rdpdr/rdpdr_printer.c \
|
||||
guac_rdpdr/rdpdr_service.c \
|
||||
rdp_fs.c \
|
||||
rdp_print_job.c \
|
||||
rdp_stream.c \
|
||||
unicode.c
|
||||
|
||||
noinst_HEADERS = \
|
||||
compat/client-cliprdr.h \
|
||||
compat/rail.h \
|
||||
guac_ai/ai_messages.h \
|
||||
guac_ai/ai_service.h \
|
||||
guac_rdpdr/rdpdr_fs_messages.h \
|
||||
guac_rdpdr/rdpdr_fs_messages_dir_info.h \
|
||||
guac_rdpdr/rdpdr_fs_messages_file_info.h \
|
||||
guac_rdpdr/rdpdr_fs_messages_vol_info.h \
|
||||
guac_rdpdr/rdpdr_fs_service.h \
|
||||
guac_rdpdr/rdpdr_messages.h \
|
||||
guac_rdpdr/rdpdr_printer.h \
|
||||
guac_rdpdr/rdpdr_service.h \
|
||||
guac_rdpsnd/rdpsnd_messages.h \
|
||||
guac_rdpsnd/rdpsnd_service.h \
|
||||
guac_svc/svc_service.h \
|
||||
audio_input.h \
|
||||
client.h \
|
||||
decompose.h \
|
||||
dvc.h \
|
||||
error.h \
|
||||
input.h \
|
||||
keyboard.h \
|
||||
ptr_string.h \
|
||||
rdp.h \
|
||||
rdp_bitmap.h \
|
||||
rdp_cliprdr.h \
|
||||
rdp_color.h \
|
||||
rdp_disp.h \
|
||||
rdp_fs.h \
|
||||
rdp_gdi.h \
|
||||
rdp_glyph.h \
|
||||
rdp_keymap.h \
|
||||
rdp_pointer.h \
|
||||
rdp_print_job.h \
|
||||
rdp_rail.h \
|
||||
rdp_settings.h \
|
||||
rdp_status.h \
|
||||
rdp_stream.h \
|
||||
rdp_svc.h \
|
||||
resolution.h \
|
||||
unicode.h \
|
||||
user.h
|
||||
|
||||
# Add compatibility layer for WinPR if not available
|
||||
if ! ENABLE_WINPR
|
||||
noinst_HEADERS += compat/winpr-stream.h compat/winpr-wtypes.h
|
||||
libguac_client_rdp_la_SOURCES += compat/winpr-stream.c
|
||||
guacai_sources += compat/winpr-stream.c
|
||||
guacsvc_sources += compat/winpr-stream.c
|
||||
guacsnd_sources += compat/winpr-stream.c
|
||||
guacdr_sources += compat/winpr-stream.c
|
||||
endif
|
||||
|
||||
#
|
||||
# Main RDP client library
|
||||
#
|
||||
|
||||
nodist_libguac_client_rdp_la_SOURCES = \
|
||||
_generated_channel_entry_wrappers.c \
|
||||
_generated_keymaps.c
|
||||
|
||||
libguac_client_rdp_la_SOURCES = \
|
||||
bitmap.c \
|
||||
channels/audio-input/audio-buffer.c \
|
||||
channels/audio-input/audio-input.c \
|
||||
channels/cliprdr.c \
|
||||
channels/common-svc.c \
|
||||
channels/disp.c \
|
||||
channels/pipe-svc.c \
|
||||
channels/rail.c \
|
||||
channels/rdpdr/rdpdr-fs-messages-dir-info.c \
|
||||
channels/rdpdr/rdpdr-fs-messages-file-info.c \
|
||||
channels/rdpdr/rdpdr-fs-messages-vol-info.c \
|
||||
channels/rdpdr/rdpdr-fs-messages.c \
|
||||
channels/rdpdr/rdpdr-fs.c \
|
||||
channels/rdpdr/rdpdr-messages.c \
|
||||
channels/rdpdr/rdpdr-printer.c \
|
||||
channels/rdpdr/rdpdr.c \
|
||||
channels/rdpsnd/rdpsnd-messages.c \
|
||||
channels/rdpsnd/rdpsnd.c \
|
||||
client.c \
|
||||
color.c \
|
||||
decompose.c \
|
||||
download.c \
|
||||
error.c \
|
||||
fs.c \
|
||||
gdi.c \
|
||||
glyph.c \
|
||||
input.c \
|
||||
keyboard.c \
|
||||
keymap.c \
|
||||
log.c \
|
||||
ls.c \
|
||||
plugins/channels.c \
|
||||
plugins/ptr-string.c \
|
||||
pointer.c \
|
||||
print-job.c \
|
||||
rdp.c \
|
||||
resolution.c \
|
||||
settings.c \
|
||||
unicode.c \
|
||||
upload.c \
|
||||
user.c
|
||||
|
||||
noinst_HEADERS = \
|
||||
bitmap.h \
|
||||
channels/audio-input/audio-buffer.h \
|
||||
channels/audio-input/audio-input.h \
|
||||
channels/cliprdr.h \
|
||||
channels/common-svc.h \
|
||||
channels/disp.h \
|
||||
channels/pipe-svc.h \
|
||||
channels/rail.h \
|
||||
channels/rdpdr/rdpdr-fs-messages-dir-info.h \
|
||||
channels/rdpdr/rdpdr-fs-messages-file-info.h \
|
||||
channels/rdpdr/rdpdr-fs-messages-vol-info.h \
|
||||
channels/rdpdr/rdpdr-fs-messages.h \
|
||||
channels/rdpdr/rdpdr-fs.h \
|
||||
channels/rdpdr/rdpdr-messages.h \
|
||||
channels/rdpdr/rdpdr-printer.h \
|
||||
channels/rdpdr/rdpdr.h \
|
||||
channels/rdpsnd/rdpsnd-messages.h \
|
||||
channels/rdpsnd/rdpsnd.h \
|
||||
client.h \
|
||||
color.h \
|
||||
decompose.h \
|
||||
download.h \
|
||||
error.h \
|
||||
fs.h \
|
||||
gdi.h \
|
||||
glyph.h \
|
||||
input.h \
|
||||
keyboard.h \
|
||||
keymap.h \
|
||||
log.h \
|
||||
ls.h \
|
||||
plugins/channels.h \
|
||||
plugins/guacai/guacai-messages.h \
|
||||
plugins/guacai/guacai.h \
|
||||
plugins/ptr-string.h \
|
||||
pointer.h \
|
||||
print-job.h \
|
||||
rdp.h \
|
||||
resolution.h \
|
||||
settings.h \
|
||||
unicode.h \
|
||||
upload.h \
|
||||
user.h
|
||||
|
||||
libguac_client_rdp_la_CFLAGS = \
|
||||
-Werror -Wall -Iinclude \
|
||||
@COMMON_INCLUDE@ \
|
||||
@COMMON_SSH_INCLUDE@ \
|
||||
@LIBGUAC_INCLUDE@
|
||||
@LIBGUAC_INCLUDE@ \
|
||||
@RDP_CFLAGS@
|
||||
|
||||
libguac_client_rdp_la_LDFLAGS = \
|
||||
-version-info 0:0:0 \
|
||||
@CAIRO_LIBS@ \
|
||||
@PTHREAD_LIBS@ \
|
||||
@RDP_LIBS@ \
|
||||
@WINPR_LIBS@
|
||||
@RDP_LIBS@
|
||||
|
||||
libguac_client_rdp_la_LIBADD = \
|
||||
@COMMON_LTLIB@ \
|
||||
libguac_client_rdp_la_LIBADD = \
|
||||
@COMMON_LTLIB@ \
|
||||
@LIBGUAC_LTLIB@
|
||||
|
||||
#
|
||||
# RDPDR
|
||||
# Plugins for FreeRDP
|
||||
#
|
||||
|
||||
guacdr_cflags = \
|
||||
-Werror -Wall -Iinclude \
|
||||
@COMMON_INCLUDE@ \
|
||||
@COMMON_SSH_INCLUDE@ \
|
||||
@LIBGUAC_INCLUDE@
|
||||
freerdp_LTLIBRARIES = \
|
||||
libguac-common-svc-client.la \
|
||||
libguacai-client.la
|
||||
|
||||
guacdr_ldflags = \
|
||||
-module -avoid-version -shared \
|
||||
@PTHREAD_LIBS@ \
|
||||
@RDP_LIBS@ \
|
||||
@WINPR_LIBS@
|
||||
freerdpdir = ${libdir}/freerdp2
|
||||
|
||||
guacdr_libadd = \
|
||||
@COMMON_LTLIB@ \
|
||||
#
|
||||
# Common SVC plugin (shared by RDPDR, RDPSND, etc.)
|
||||
#
|
||||
|
||||
libguac_common_svc_client_la_SOURCES = \
|
||||
plugins/guac-common-svc/guac-common-svc.c
|
||||
|
||||
libguac_common_svc_client_la_CFLAGS = \
|
||||
-Werror -Wall -Iinclude \
|
||||
@LIBGUAC_INCLUDE@ \
|
||||
@RDP_CFLAGS@
|
||||
|
||||
libguac_common_svc_client_la_LDFLAGS = \
|
||||
-module -avoid-version -shared \
|
||||
@RDP_LIBS@
|
||||
|
||||
libguac_common_svc_client_la_LIBADD = \
|
||||
@LIBGUAC_LTLIB@
|
||||
|
||||
#
|
||||
# Audio Input
|
||||
#
|
||||
|
||||
guacai_cflags = \
|
||||
-Werror -Wall -Iinclude \
|
||||
@COMMON_INCLUDE@ \
|
||||
@COMMON_SSH_INCLUDE@ \
|
||||
@LIBGUAC_INCLUDE@
|
||||
libguacai_client_la_SOURCES = \
|
||||
channels/audio-input/audio-buffer.c \
|
||||
plugins/guacai/guacai-messages.c \
|
||||
plugins/guacai/guacai.c \
|
||||
plugins/ptr-string.c
|
||||
|
||||
guacai_ldflags = \
|
||||
libguacai_client_la_CFLAGS = \
|
||||
-Werror -Wall -Iinclude \
|
||||
@COMMON_INCLUDE@ \
|
||||
@COMMON_SSH_INCLUDE@ \
|
||||
@LIBGUAC_INCLUDE@ \
|
||||
@RDP_CFLAGS@
|
||||
|
||||
libguacai_client_la_LDFLAGS = \
|
||||
-module -avoid-version -shared \
|
||||
@PTHREAD_LIBS@ \
|
||||
@RDP_LIBS@ \
|
||||
@WINPR_LIBS@
|
||||
@RDP_LIBS@
|
||||
|
||||
guacai_libadd = \
|
||||
@COMMON_LTLIB@ \
|
||||
@LIBGUAC_LTLIB@
|
||||
|
||||
#
|
||||
# RDPSND
|
||||
#
|
||||
|
||||
guacsnd_cflags = \
|
||||
-Werror -Wall -Iinclude \
|
||||
@COMMON_INCLUDE@ \
|
||||
@COMMON_SSH_INCLUDE@ \
|
||||
@LIBGUAC_INCLUDE@
|
||||
|
||||
guacsnd_ldflags = \
|
||||
-module -avoid-version -shared \
|
||||
@PTHREAD_LIBS@ \
|
||||
@RDP_LIBS@ \
|
||||
@WINPR_LIBS@
|
||||
|
||||
guacsnd_libadd = \
|
||||
@COMMON_LTLIB@ \
|
||||
@LIBGUAC_LTLIB@
|
||||
|
||||
#
|
||||
# Static Virtual Channels
|
||||
#
|
||||
|
||||
guacsvc_cflags = \
|
||||
-Werror -Wall -Iinclude \
|
||||
@COMMON_INCLUDE@ \
|
||||
@COMMON_SSH_INCLUDE@ \
|
||||
@LIBGUAC_INCLUDE@
|
||||
|
||||
guacsvc_ldflags = \
|
||||
-module -avoid-version -shared \
|
||||
@PTHREAD_LIBS@ \
|
||||
@RDP_LIBS@ \
|
||||
@WINPR_LIBS@
|
||||
|
||||
guacsvc_libadd = \
|
||||
@COMMON_LTLIB@ \
|
||||
libguacai_client_la_LIBADD = \
|
||||
@COMMON_LTLIB@ \
|
||||
@LIBGUAC_LTLIB@
|
||||
|
||||
#
|
||||
@ -254,13 +208,18 @@ libguac_client_rdp_la_LIBADD += @COMMON_SSH_LTLIB@
|
||||
endif
|
||||
|
||||
#
|
||||
# Autogenerate keymaps
|
||||
# Autogenerated keymaps and channel wrapper functions
|
||||
#
|
||||
|
||||
CLEANFILES = _generated_keymaps.c
|
||||
BUILT_SOURCES = _generated_keymaps.c
|
||||
CLEANFILES = \
|
||||
_generated_channel_entry_wrappers.c \
|
||||
_generated_keymaps.c
|
||||
|
||||
rdp_keymaps = \
|
||||
BUILT_SOURCES = \
|
||||
_generated_channel_entry_wrappers.c \
|
||||
_generated_keymaps.c
|
||||
|
||||
rdp_keymaps = \
|
||||
$(srcdir)/keymaps/base.keymap \
|
||||
$(srcdir)/keymaps/failsafe.keymap \
|
||||
$(srcdir)/keymaps/de_de_qwertz.keymap \
|
||||
@ -280,71 +239,13 @@ rdp_keymaps = \
|
||||
$(srcdir)/keymaps/tr_tr_qwerty.keymap
|
||||
|
||||
_generated_keymaps.c: $(rdp_keymaps)
|
||||
$(srcdir)/keymaps/generate.pl $(rdp_keymaps)
|
||||
$(AM_V_GEN) $(srcdir)/keymaps/generate.pl $(rdp_keymaps)
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(rdp_keymaps) \
|
||||
keymaps/generate.pl
|
||||
_generated_channel_entry_wrappers.c: $(srcdir)/plugins/channels.h $(srcdir)/plugins/generate-entry-wrappers.pl
|
||||
$(AM_V_GEN) $(srcdir)/plugins/generate-entry-wrappers.pl $(srcdir)/plugins/channels.h
|
||||
|
||||
if LEGACY_FREERDP_EXTENSIONS
|
||||
|
||||
# FreeRDP 1.0-style extensions
|
||||
freerdp_LTLIBRARIES = \
|
||||
guacai.la \
|
||||
guacdr.la \
|
||||
guacsnd.la \
|
||||
guacsvc.la
|
||||
|
||||
guacai_la_SOURCES = ${guacai_sources}
|
||||
guacai_la_CFLAGS = ${guacai_cflags}
|
||||
guacai_la_LDFLAGS = ${guacai_ldflags}
|
||||
guacai_la_LIBADD = ${guacai_libadd}
|
||||
|
||||
guacdr_la_SOURCES = ${guacdr_sources}
|
||||
guacdr_la_CFLAGS = ${guacdr_cflags}
|
||||
guacdr_la_LDFLAGS = ${guacdr_ldflags}
|
||||
guacdr_la_LIBADD = ${guacdr_libadd}
|
||||
|
||||
guacsnd_la_SOURCES = ${guacsnd_sources}
|
||||
guacsnd_la_CFLAGS = ${guacsnd_cflags}
|
||||
guacsnd_la_LDFLAGS = ${guacsnd_ldflags}
|
||||
guacsnd_la_LIBADD = ${guacsnd_libadd}
|
||||
|
||||
guacsvc_la_SOURCES = ${guacsvc_sources}
|
||||
guacsvc_la_CFLAGS = ${guacsvc_cflags}
|
||||
guacsvc_la_LDFLAGS = ${guacsvc_ldflags}
|
||||
guacsvc_la_LIBADD = ${guacsvc_libadd}
|
||||
|
||||
else
|
||||
|
||||
# FreeRDP 1.1 (and hopefully onward) extensions
|
||||
freerdp_LTLIBRARIES = \
|
||||
guacai-client.la \
|
||||
guacdr-client.la \
|
||||
guacsnd-client.la \
|
||||
guacsvc-client.la
|
||||
|
||||
guacai_client_la_SOURCES = ${guacai_sources}
|
||||
guacai_client_la_CFLAGS = ${guacai_cflags}
|
||||
guacai_client_la_LDFLAGS = ${guacai_ldflags}
|
||||
guacai_client_la_LIBADD = ${guacai_libadd}
|
||||
|
||||
guacdr_client_la_SOURCES = ${guacdr_sources}
|
||||
guacdr_client_la_CFLAGS = ${guacdr_cflags}
|
||||
guacdr_client_la_LDFLAGS = ${guacdr_ldflags}
|
||||
guacdr_client_la_LIBADD = ${guacdr_libadd}
|
||||
|
||||
guacsnd_client_la_SOURCES = ${guacsnd_sources}
|
||||
guacsnd_client_la_CFLAGS = ${guacsnd_cflags}
|
||||
guacsnd_client_la_LDFLAGS = ${guacsnd_ldflags}
|
||||
guacsnd_client_la_LIBADD = ${guacsnd_libadd}
|
||||
|
||||
guacsvc_client_la_SOURCES = ${guacsvc_sources}
|
||||
guacsvc_client_la_CFLAGS = ${guacsvc_cflags}
|
||||
guacsvc_client_la_LDFLAGS = ${guacsvc_ldflags}
|
||||
guacsvc_client_la_LIBADD = ${guacsvc_libadd}
|
||||
|
||||
endif
|
||||
|
||||
freerdpdir = ${libdir}/freerdp
|
||||
EXTRA_DIST = \
|
||||
$(rdp_keymaps) \
|
||||
keymaps/generate.pl \
|
||||
plugins/generate-entry-wrappers.pl
|
||||
|
||||
|
@ -17,27 +17,16 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "bitmap.h"
|
||||
#include "common/display.h"
|
||||
#include "common/surface.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_bitmap.h"
|
||||
#include "rdp_settings.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <freerdp/codec/bitmap.h>
|
||||
#include <freerdp/codec/color.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/socket.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/wtypes.h>
|
||||
#else
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.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) {
|
||||
|
||||
/* 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;
|
||||
|
||||
}
|
||||
BOOL guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
|
||||
|
||||
/* No corresponding surface yet - caching is deferred. */
|
||||
((guac_rdp_bitmap*) bitmap)->layer = NULL;
|
||||
@ -103,9 +69,11 @@ void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
|
||||
/* Start at zero usage */
|
||||
((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_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 */
|
||||
((guac_rdp_bitmap*) bitmap)->used++;
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
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_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 */
|
||||
if (bitmap == NULL) {
|
||||
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. */
|
||||
@ -184,80 +161,7 @@ void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL pri
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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;
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
@ -17,21 +17,16 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _GUAC_RDP_RDP_BITMAP_H
|
||||
#define _GUAC_RDP_RDP_BITMAP_H
|
||||
#ifndef GUAC_RDP_BITMAP_H
|
||||
#define GUAC_RDP_BITMAP_H
|
||||
|
||||
#include "config.h"
|
||||
#include "common/display.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/graphics.h>
|
||||
#include <guacamole/layer.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/wtypes.h>
|
||||
#else
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Guacamole-specific rdpBitmap data.
|
||||
@ -77,8 +72,11 @@ void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap);
|
||||
*
|
||||
* @param bitmap
|
||||
* 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
|
||||
@ -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 paint operation to perform, including the destination X/Y
|
||||
* 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.
|
||||
@ -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
|
||||
* surface should be reset to the primary drawing surface of the remote
|
||||
* 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);
|
||||
|
||||
#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
|
@ -17,181 +17,19 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "audio_input.h"
|
||||
#include "dvc.h"
|
||||
#include "ptr_string.h"
|
||||
#include "channels/audio-input/audio-buffer.h"
|
||||
#include "rdp.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/channels/channels.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/stream.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/**
|
||||
* Parses the given raw audio mimetype, producing the corresponding rate,
|
||||
* number of channels, and bytes per sample.
|
||||
*
|
||||
* @param mimetype
|
||||
* The raw auduio mimetype to parse.
|
||||
*
|
||||
* @param rate
|
||||
* A pointer to an int where the sample rate for the PCM format described
|
||||
* by the given mimetype should be stored.
|
||||
*
|
||||
* @param channels
|
||||
* A pointer to an int where the number of channels used by the PCM format
|
||||
* described by the given mimetype should be stored.
|
||||
*
|
||||
* @param bps
|
||||
* A pointer to an int where the number of bytes used the PCM format for
|
||||
* each sample (independent of number of channels) described by the given
|
||||
* mimetype should be stored.
|
||||
*
|
||||
* @return
|
||||
* Zero if the given mimetype is a raw audio mimetype and has been parsed
|
||||
* successfully, non-zero otherwise.
|
||||
*/
|
||||
static int guac_rdp_audio_parse_mimetype(const char* mimetype,
|
||||
int* rate, int* channels, int* bps) {
|
||||
|
||||
int parsed_rate = -1;
|
||||
int parsed_channels = 1;
|
||||
int parsed_bps;
|
||||
|
||||
/* PCM audio with one byte per sample */
|
||||
if (strncmp(mimetype, "audio/L8;", 9) == 0) {
|
||||
mimetype += 8; /* Advance to semicolon ONLY */
|
||||
parsed_bps = 1;
|
||||
}
|
||||
|
||||
/* PCM audio with two bytes per sample */
|
||||
else if (strncmp(mimetype, "audio/L16;", 10) == 0) {
|
||||
mimetype += 9; /* Advance to semicolon ONLY */
|
||||
parsed_bps = 2;
|
||||
}
|
||||
|
||||
/* Unsupported mimetype */
|
||||
else
|
||||
return 1;
|
||||
|
||||
/* Parse each parameter name/value pair within the mimetype */
|
||||
do {
|
||||
|
||||
/* Advance to first character of parameter (current is either a
|
||||
* semicolon or a comma) */
|
||||
mimetype++;
|
||||
|
||||
/* Parse number of channels */
|
||||
if (strncmp(mimetype, "channels=", 9) == 0) {
|
||||
|
||||
mimetype += 9;
|
||||
parsed_channels = strtol(mimetype, (char**) &mimetype, 10);
|
||||
|
||||
/* Fail if value invalid / out of range */
|
||||
if (errno == EINVAL || errno == ERANGE)
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* Parse number of rate */
|
||||
else if (strncmp(mimetype, "rate=", 5) == 0) {
|
||||
|
||||
mimetype += 5;
|
||||
parsed_rate = strtol(mimetype, (char**) &mimetype, 10);
|
||||
|
||||
/* Fail if value invalid / out of range */
|
||||
if (errno == EINVAL || errno == ERANGE)
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* Advance to next parameter */
|
||||
mimetype = strchr(mimetype, ',');
|
||||
|
||||
} while (mimetype != NULL);
|
||||
|
||||
/* Mimetype is invalid if rate was not specified */
|
||||
if (parsed_rate == -1)
|
||||
return 1;
|
||||
|
||||
/* Parse success */
|
||||
*rate = parsed_rate;
|
||||
*channels = parsed_channels;
|
||||
*bps = parsed_bps;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_audio_handler(guac_user* user, guac_stream* stream,
|
||||
char* mimetype) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
int rate;
|
||||
int channels;
|
||||
int bps;
|
||||
|
||||
/* Parse mimetype, abort on parse error */
|
||||
if (guac_rdp_audio_parse_mimetype(mimetype, &rate, &channels, &bps)) {
|
||||
guac_user_log(user, GUAC_LOG_WARNING, "Denying user audio stream with "
|
||||
"unsupported mimetype: \"%s\"", mimetype);
|
||||
guac_protocol_send_ack(user->socket, stream, "Unsupported audio "
|
||||
"mimetype", GUAC_PROTOCOL_STATUS_CLIENT_BAD_TYPE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Init stream data */
|
||||
stream->blob_handler = guac_rdp_audio_blob_handler;
|
||||
stream->end_handler = guac_rdp_audio_end_handler;
|
||||
|
||||
/* Associate stream with audio buffer */
|
||||
guac_rdp_audio_buffer_set_stream(rdp_client->audio_input, user, stream,
|
||||
rate, channels, bps);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_audio_blob_handler(guac_user* user, guac_stream* stream,
|
||||
void* data, int length) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Write blob to audio stream, buffering if necessary */
|
||||
guac_rdp_audio_buffer_write(rdp_client->audio_input, data, length);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_audio_end_handler(guac_user* user, guac_stream* stream) {
|
||||
|
||||
/* Ignore - the AUDIO_INPUT channel will simply not receive anything */
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_audio_load_plugin(rdpContext* context, guac_rdp_dvc_list* list) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
char client_ref[GUAC_RDP_PTR_STRING_LENGTH];
|
||||
|
||||
/* Add "AUDIO_INPUT" channel */
|
||||
guac_rdp_ptr_to_string(client, client_ref);
|
||||
guac_rdp_dvc_list_add(list, "guacai", client_ref, NULL);
|
||||
|
||||
}
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
guac_rdp_audio_buffer* guac_rdp_audio_buffer_alloc() {
|
||||
guac_rdp_audio_buffer* buffer = calloc(1, sizeof(guac_rdp_audio_buffer));
|
@ -17,16 +17,11 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_AUDIO_INPUT_H
|
||||
#define GUAC_RDP_AUDIO_INPUT_H
|
||||
#ifndef GUAC_RDP_CHANNELS_AUDIO_INPUT_AUDIO_BUFFER_H
|
||||
#define GUAC_RDP_CHANNELS_AUDIO_INPUT_AUDIO_BUFFER_H
|
||||
|
||||
#include "config.h"
|
||||
#include "dvc.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <guacamole/stream.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/**
|
||||
@ -278,36 +273,5 @@ void guac_rdp_audio_buffer_end(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
|
||||
|
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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "client.h"
|
||||
#include "dvc.h"
|
||||
#include "channels/disp.h"
|
||||
#include "plugins/channels.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_disp.h"
|
||||
#include "rdp_settings.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <freerdp/client/disp.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/event.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/timestamp.h>
|
||||
|
||||
#ifdef HAVE_FREERDP_CLIENT_DISP_H
|
||||
#include <freerdp/client/disp.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
guac_rdp_disp* guac_rdp_disp_alloc() {
|
||||
|
||||
guac_rdp_disp* disp = malloc(sizeof(guac_rdp_disp));
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
/* Not yet connected */
|
||||
disp->disp = NULL;
|
||||
#endif
|
||||
|
||||
/* No requests have been made */
|
||||
disp->last_request = guac_timestamp_current();
|
||||
@ -55,23 +52,60 @@ void guac_rdp_disp_free(guac_rdp_disp* 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
|
||||
context->settings->SupportDisplayControl = TRUE;
|
||||
#endif
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdp_disp* guac_disp = rdp_client->disp;
|
||||
|
||||
/* Ignore connection event if it's not for the Display Update channel */
|
||||
if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) != 0)
|
||||
return;
|
||||
|
||||
/* Init module with current display size */
|
||||
guac_rdp_disp_set_size(guac_disp, rdp_client->settings,
|
||||
context->instance, guac_rdp_get_width(context->instance),
|
||||
guac_rdp_get_height(context->instance));
|
||||
|
||||
/* Store reference to the display update plugin once it's connected */
|
||||
DispClientContext* disp = (DispClientContext*) e->pInterface;
|
||||
guac_disp->disp = disp;
|
||||
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Display update channel "
|
||||
"will be used for display size changes.");
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_disp_load_plugin(rdpContext* context) {
|
||||
|
||||
/* Subscribe to and handle channel connected events */
|
||||
PubSub_SubscribeChannelConnected(context->pubSub,
|
||||
(pChannelConnectedEventHandler) guac_rdp_disp_channel_connected);
|
||||
|
||||
/* Add "disp" channel */
|
||||
guac_rdp_dvc_list_add(list, "disp", NULL);
|
||||
guac_freerdp_dynamic_channel_collection_add(context->settings, "disp", NULL);
|
||||
|
||||
}
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
void guac_rdp_disp_connect(guac_rdp_disp* guac_disp, DispClientContext* disp) {
|
||||
guac_disp->disp = disp;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Fits a given dimension within the allowed bounds for Display Update
|
||||
* messages, adjusting the other dimension such that aspect ratio is
|
||||
@ -172,7 +206,6 @@ void guac_rdp_disp_update_size(guac_rdp_disp* disp,
|
||||
}
|
||||
|
||||
else if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) {
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT monitors[1] = {{
|
||||
.Flags = 0x1, /* DISPLAYCONTROL_MONITOR_PRIMARY */
|
||||
.Left = 0,
|
||||
@ -189,7 +222,6 @@ void guac_rdp_disp_update_size(guac_rdp_disp* disp,
|
||||
/* Send display update notification if display channel is connected */
|
||||
if (disp->disp != NULL)
|
||||
disp->disp->SendMonitorLayout(disp->disp, 1, monitors);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
@ -17,17 +17,15 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_DISP_H
|
||||
#define GUAC_RDP_DISP_H
|
||||
#ifndef GUAC_RDP_CHANNELS_DISP_H
|
||||
#define GUAC_RDP_CHANNELS_DISP_H
|
||||
|
||||
#include "dvc.h"
|
||||
#include "rdp_settings.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#ifdef HAVE_FREERDP_CLIENT_DISP_H
|
||||
#include <freerdp/client/disp.h>
|
||||
#endif
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/timestamp.h>
|
||||
|
||||
/**
|
||||
* The minimum value for width or height, in pixels.
|
||||
@ -50,12 +48,10 @@
|
||||
*/
|
||||
typedef struct guac_rdp_disp {
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
/**
|
||||
* Display control interface.
|
||||
*/
|
||||
DispClientContext* disp;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The timestamp of the last display update request, or 0 if no request
|
||||
@ -83,53 +79,43 @@ typedef struct guac_rdp_disp {
|
||||
|
||||
/**
|
||||
* Allocates a new display update module, which will ultimately control the
|
||||
* display update channel once conected.
|
||||
* display update channel once connected.
|
||||
*
|
||||
* @return A new display update module.
|
||||
* @return
|
||||
* A newly-allocated display update module.
|
||||
*/
|
||||
guac_rdp_disp* guac_rdp_disp_alloc();
|
||||
|
||||
/**
|
||||
* Frees the given display update module.
|
||||
* Frees the resources associated with support for the RDP Display Update
|
||||
* channel. Only resources specific to Guacamole are freed. Resources specific
|
||||
* to FreeRDP's handling of the Display Update channel will be freed by
|
||||
* FreeRDP. If no resources are currently allocated for Display Update support,
|
||||
* this function has no effect.
|
||||
*
|
||||
* @param disp The display update module to free.
|
||||
* @param disp
|
||||
* The display update module to free.
|
||||
*/
|
||||
void guac_rdp_disp_free(guac_rdp_disp* disp);
|
||||
|
||||
/**
|
||||
* @param context The rdpContext associated with the active RDP session.
|
||||
*/
|
||||
/**
|
||||
* Adds FreeRDP's "disp" plugin to the list of dynamic virtual channel plugins
|
||||
* to be loaded by FreeRDP's "drdynvc" plugin. The plugin will only be loaded
|
||||
* once guac_rdp_load_drdynvc() is invoked with the guac_rdp_dvc_list passed to
|
||||
* this function. The "disp" plugin ultimately adds support for the Display
|
||||
* Update channel. NOTE: It is still up to external code to detect when the
|
||||
* "disp" channel is connected, and update the guac_rdp_disp with a call to
|
||||
* guac_rdp_disp_connect().
|
||||
* to be loaded by FreeRDP's "drdynvc" plugin. The context of the plugin will
|
||||
* automatically be assicated with the guac_rdp_disp instance pointed to by the
|
||||
* current guac_rdp_client. The plugin will only be loaded once the "drdynvc"
|
||||
* plugin is loaded. The "disp" plugin ultimately adds support for the Display
|
||||
* Update channel.
|
||||
*
|
||||
* If failures occur, messages noting the specifics of those failures will be
|
||||
* logged, and the RDP side of Display Update support will not be functional.
|
||||
*
|
||||
* This MUST be called within the PreConnect callback of the freerdp instance
|
||||
* for Display Update support to be loaded.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the active RDP session.
|
||||
*
|
||||
* @param list
|
||||
* The guac_rdp_dvc_list to which the "disp" plugin should be added, such
|
||||
* that it may later be loaded by guac_rdp_load_drdynvc().
|
||||
*/
|
||||
void guac_rdp_disp_load_plugin(rdpContext* context, guac_rdp_dvc_list* list);
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
/**
|
||||
* Stores the given DispClientContext within the given guac_rdp_disp, such that
|
||||
* display updates can be properly sent. Until this is called, changes to the
|
||||
* display size will be deferred.
|
||||
*
|
||||
* @param guac_disp The display update module to associate with the connected
|
||||
* display update channel.
|
||||
* @param disp The DispClientContext associated by FreeRDP with the connected
|
||||
* display update channel.
|
||||
*/
|
||||
void guac_rdp_disp_connect(guac_rdp_disp* guac_disp, DispClientContext* disp);
|
||||
#endif
|
||||
void guac_rdp_disp_load_plugin(rdpContext* context);
|
||||
|
||||
/**
|
||||
* Requests a display size update, which may then be sent immediately to the
|
||||
@ -200,6 +186,10 @@ void guac_rdp_disp_reconnect_complete(guac_rdp_disp* disp);
|
||||
* Returns whether a full RDP reconnect is required for display update changes
|
||||
* to take effect.
|
||||
*
|
||||
* @param disp
|
||||
* The display update module that should be checked to determine whether a
|
||||
* reconnect is required.
|
||||
*
|
||||
* @return
|
||||
* Non-zero if a reconnect is needed, zero otherwise.
|
||||
*/
|
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.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_CHANNELS_RAIL_H
|
||||
#define GUAC_RDP_CHANNELS_RAIL_H
|
||||
|
||||
#ifndef __GUAC_CLIENT_CLIPRDR_COMPAT_H
|
||||
#define __GUAC_CLIENT_CLIPRDR_COMPAT_H
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <freerdp/plugins/cliprdr.h>
|
||||
|
||||
#define CliprdrChannel_Class RDP_EVENT_CLASS_CLIPRDR
|
||||
#define CliprdrChannel_FormatList RDP_EVENT_TYPE_CB_FORMAT_LIST
|
||||
#define CliprdrChannel_MonitorReady RDP_EVENT_TYPE_CB_MONITOR_READY
|
||||
#define CliprdrChannel_DataRequest RDP_EVENT_TYPE_CB_DATA_REQUEST
|
||||
#define CliprdrChannel_DataResponse RDP_EVENT_TYPE_CB_DATA_RESPONSE
|
||||
/**
|
||||
* Initializes RemoteApp support for RDP and handling of the RAIL channel. If
|
||||
* failures occur, messages noting the specifics of those failures will be
|
||||
* logged, and RemoteApp support will not be functional.
|
||||
*
|
||||
* This MUST be called within the PreConnect callback of the freerdp instance
|
||||
* for RAIL support to be loaded.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the FreeRDP side of the RDP connection.
|
||||
*/
|
||||
void guac_rdp_rail_load_plugin(rdpContext* context);
|
||||
|
||||
#endif
|
||||
|
@ -17,26 +17,22 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "rdpdr_service.h"
|
||||
#include "rdp_fs.h"
|
||||
#include "rdp_status.h"
|
||||
#include "channels/rdpdr/rdpdr-fs-messages-dir-info.h"
|
||||
#include "channels/rdpdr/rdpdr.h"
|
||||
#include "fs.h"
|
||||
#include "unicode.h"
|
||||
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/unicode.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/nt.h>
|
||||
#include <winpr/stream.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stddef.h>
|
||||
|
||||
void guac_rdpdr_fs_process_query_directory_info(guac_rdpdr_device* device,
|
||||
const char* entry_name, int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_query_directory_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
const char* entry_name, int entry_file_id) {
|
||||
|
||||
guac_rdp_fs_file* file;
|
||||
|
||||
@ -49,16 +45,17 @@ void guac_rdpdr_fs_process_query_directory_info(guac_rdpdr_device* device,
|
||||
(char*) utf16_entry_name, sizeof(utf16_entry_name));
|
||||
|
||||
/* Get file */
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, entry_file_id);
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i (entry_name=\"%s\")]",
|
||||
__func__, file_id, entry_name);
|
||||
__func__, entry_file_id, entry_name);
|
||||
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
STATUS_SUCCESS, 4 + 64 + utf16_length + 2);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, STATUS_SUCCESS,
|
||||
4 + 64 + utf16_length + 2);
|
||||
|
||||
Stream_Write_UINT32(output_stream,
|
||||
64 + utf16_length + 2); /* Length */
|
||||
@ -77,12 +74,13 @@ 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, "\0\0", 2);
|
||||
|
||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_fs_process_query_full_directory_info(guac_rdpdr_device* device,
|
||||
const char* entry_name, int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_query_full_directory_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
const char* entry_name, int entry_file_id) {
|
||||
|
||||
guac_rdp_fs_file* file;
|
||||
|
||||
@ -95,16 +93,17 @@ void guac_rdpdr_fs_process_query_full_directory_info(guac_rdpdr_device* device,
|
||||
(char*) utf16_entry_name, sizeof(utf16_entry_name));
|
||||
|
||||
/* Get file */
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, entry_file_id);
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i (entry_name=\"%s\")]",
|
||||
__func__, file_id, entry_name);
|
||||
__func__, entry_file_id, entry_name);
|
||||
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
STATUS_SUCCESS, 4 + 68 + utf16_length + 2);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, STATUS_SUCCESS,
|
||||
4 + 68 + utf16_length + 2);
|
||||
|
||||
Stream_Write_UINT32(output_stream,
|
||||
68 + utf16_length + 2); /* Length */
|
||||
@ -124,12 +123,13 @@ 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, "\0\0", 2);
|
||||
|
||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_fs_process_query_both_directory_info(guac_rdpdr_device* device,
|
||||
const char* entry_name, int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_query_both_directory_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
const char* entry_name, int entry_file_id) {
|
||||
|
||||
guac_rdp_fs_file* file;
|
||||
|
||||
@ -142,16 +142,17 @@ void guac_rdpdr_fs_process_query_both_directory_info(guac_rdpdr_device* device,
|
||||
(char*) utf16_entry_name, sizeof(utf16_entry_name));
|
||||
|
||||
/* Get file */
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, entry_file_id);
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i (entry_name=\"%s\")]",
|
||||
__func__, file_id, entry_name);
|
||||
__func__, entry_file_id, entry_name);
|
||||
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
STATUS_SUCCESS, 4 + 69 + 24 + utf16_length + 2);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, STATUS_SUCCESS,
|
||||
4 + 69 + 24 + utf16_length + 2);
|
||||
|
||||
Stream_Write_UINT32(output_stream,
|
||||
69 + 24 + utf16_length + 2); /* Length */
|
||||
@ -175,12 +176,13 @@ 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, "\0\0", 2);
|
||||
|
||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_fs_process_query_names_info(guac_rdpdr_device* device,
|
||||
const char* entry_name, int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_query_names_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
const char* entry_name, int entry_file_id) {
|
||||
|
||||
guac_rdp_fs_file* file;
|
||||
|
||||
@ -193,16 +195,17 @@ void guac_rdpdr_fs_process_query_names_info(guac_rdpdr_device* device,
|
||||
(char*) utf16_entry_name, sizeof(utf16_entry_name));
|
||||
|
||||
/* Get file */
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, entry_file_id);
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i (entry_name=\"%s\")]",
|
||||
__func__, file_id, entry_name);
|
||||
__func__, entry_file_id, entry_name);
|
||||
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
STATUS_SUCCESS, 4 + 12 + utf16_length + 2);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, STATUS_SUCCESS,
|
||||
4 + 12 + utf16_length + 2);
|
||||
|
||||
Stream_Write_UINT32(output_stream,
|
||||
12 + utf16_length + 2); /* Length */
|
||||
@ -213,7 +216,7 @@ 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, "\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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GUAC_RDPDR_FS_MESSAGES_DIR_INFO_H
|
||||
#define __GUAC_RDPDR_FS_MESSAGES_DIR_INFO_H
|
||||
#ifndef GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_DIR_INFO_H
|
||||
#define GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_DIR_INFO_H
|
||||
|
||||
/**
|
||||
* Handlers for directory queries received over the RDPDR channel via the
|
||||
* IRP_MJ_DIRECTORY_CONTROL major function and the IRP_MN_QUERY_DIRECTORY minor
|
||||
* function.
|
||||
*
|
||||
* @file rdpdr_fs_messages_dir_info.h
|
||||
* @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>
|
||||
#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
|
||||
* documentation this is "defined as the file's name, time stamp, and size, or its
|
||||
* attributes."
|
||||
*/
|
||||
void guac_rdpdr_fs_process_query_directory_info(guac_rdpdr_device* device,
|
||||
const char* entry_name, int file_id, int completion_id);
|
||||
guac_rdpdr_directory_query_handler guac_rdpdr_fs_process_query_directory_info;
|
||||
|
||||
/**
|
||||
* Processes a query request for FileFullDirectoryInformation. From the
|
||||
* documentation, this is "defined as all the basic information, plus extended
|
||||
* attribute size."
|
||||
*/
|
||||
void guac_rdpdr_fs_process_query_full_directory_info(guac_rdpdr_device* device,
|
||||
const char* entry_name, int file_id, int completion_id);
|
||||
guac_rdpdr_directory_query_handler guac_rdpdr_fs_process_query_full_directory_info;
|
||||
|
||||
/**
|
||||
* Processes a query request for FileBothDirectoryInformation. From the
|
||||
* documentation, this absurdly-named request is "basic information plus
|
||||
* extended attribute size and short name about a file or directory."
|
||||
*/
|
||||
void guac_rdpdr_fs_process_query_both_directory_info(guac_rdpdr_device* device,
|
||||
const char* entry_name, int file_id, int completion_id);
|
||||
guac_rdpdr_directory_query_handler guac_rdpdr_fs_process_query_both_directory_info;
|
||||
|
||||
/**
|
||||
* Processes a query request for FileNamesInformation. From the documentation,
|
||||
* this is "detailed information on the names of files in a directory."
|
||||
*/
|
||||
void guac_rdpdr_fs_process_query_names_info(guac_rdpdr_device* device,
|
||||
const char* entry_name, int file_id, int completion_id);
|
||||
guac_rdpdr_directory_query_handler guac_rdpdr_fs_process_query_names_info;
|
||||
|
||||
#endif
|
||||
|
@ -17,44 +17,38 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "rdpdr_service.h"
|
||||
#include "rdp_fs.h"
|
||||
#include "rdp_status.h"
|
||||
#include "channels/rdpdr/rdpdr-fs-messages-file-info.h"
|
||||
#include "channels/rdpdr/rdpdr.h"
|
||||
#include "download.h"
|
||||
#include "fs.h"
|
||||
#include "unicode.h"
|
||||
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <guacamole/client.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/nt.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/wtypes.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
void guac_rdpdr_fs_process_query_basic_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_query_basic_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
wStream* output_stream;
|
||||
guac_rdp_fs_file* file;
|
||||
|
||||
/* Get file */
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, iorequest->file_id);
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i]",
|
||||
__func__, file_id);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||
iorequest->file_id);
|
||||
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
STATUS_SUCCESS, 40);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, STATUS_SUCCESS, 40);
|
||||
|
||||
Stream_Write_UINT32(output_stream, 36);
|
||||
Stream_Write_UINT64(output_stream, file->ctime); /* CreationTime */
|
||||
@ -65,31 +59,31 @@ void guac_rdpdr_fs_process_query_basic_info(guac_rdpdr_device* device, wStream*
|
||||
|
||||
/* 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,
|
||||
int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_query_standard_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
wStream* output_stream;
|
||||
guac_rdp_fs_file* file;
|
||||
BOOL is_directory = FALSE;
|
||||
|
||||
/* Get file */
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, iorequest->file_id);
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i]",
|
||||
__func__, file_id);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||
iorequest->file_id);
|
||||
|
||||
if (file->attributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
is_directory = TRUE;
|
||||
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
STATUS_SUCCESS, 26);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, STATUS_SUCCESS, 26);
|
||||
|
||||
Stream_Write_UINT32(output_stream, 22);
|
||||
Stream_Write_UINT64(output_stream, file->size); /* AllocationSize */
|
||||
@ -100,27 +94,27 @@ void guac_rdpdr_fs_process_query_standard_info(guac_rdpdr_device* device, wStrea
|
||||
|
||||
/* 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,
|
||||
wStream* input_stream, int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_query_attribute_tag_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
wStream* output_stream;
|
||||
guac_rdp_fs_file* file;
|
||||
|
||||
/* Get file */
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, iorequest->file_id);
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i]",
|
||||
__func__, file_id);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||
iorequest->file_id);
|
||||
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
STATUS_SUCCESS, 12);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, STATUS_SUCCESS, 12);
|
||||
|
||||
Stream_Write_UINT32(output_stream, 8);
|
||||
Stream_Write_UINT32(output_stream, file->attributes); /* FileAttributes */
|
||||
@ -128,12 +122,13 @@ void guac_rdpdr_fs_process_query_attribute_tag_info(guac_rdpdr_device* device,
|
||||
|
||||
/* 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,
|
||||
wStream* input_stream, int file_id, int completion_id, int length) {
|
||||
void guac_rdpdr_fs_process_set_rename_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
int length, wStream* input_stream) {
|
||||
|
||||
int result;
|
||||
int filename_length;
|
||||
@ -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,
|
||||
destination_path, sizeof(destination_path));
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i] destination_path=\"%s\"",
|
||||
__func__, file_id, destination_path);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]"
|
||||
"destination_path=\"%s\"", __func__, iorequest->file_id,
|
||||
destination_path);
|
||||
|
||||
/* If file moving to \Download folder, start stream, do not move */
|
||||
if (strncmp(destination_path, "\\Download\\", 10) == 0) {
|
||||
@ -159,38 +154,39 @@ void guac_rdpdr_fs_process_set_rename_info(guac_rdpdr_device* device,
|
||||
guac_rdp_fs_file* 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)
|
||||
return;
|
||||
|
||||
/* 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,
|
||||
completion_id, STATUS_SUCCESS, 4);
|
||||
iorequest->completion_id, STATUS_SUCCESS, 4);
|
||||
|
||||
}
|
||||
|
||||
/* Otherwise, rename as requested */
|
||||
else {
|
||||
|
||||
result = guac_rdp_fs_rename((guac_rdp_fs*) device->data, file_id,
|
||||
destination_path);
|
||||
result = guac_rdp_fs_rename((guac_rdp_fs*) device->data,
|
||||
iorequest->file_id, destination_path);
|
||||
if (result < 0)
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
completion_id, guac_rdp_fs_get_status(result), 4);
|
||||
iorequest->completion_id, guac_rdp_fs_get_status(result), 4);
|
||||
else
|
||||
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);
|
||||
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,
|
||||
wStream* input_stream, int file_id, int completion_id, int length) {
|
||||
void guac_rdpdr_fs_process_set_allocation_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
int length, wStream* input_stream) {
|
||||
|
||||
int result;
|
||||
UINT64 size;
|
||||
@ -199,50 +195,50 @@ void guac_rdpdr_fs_process_set_allocation_info(guac_rdpdr_device* device,
|
||||
/* Read new size */
|
||||
Stream_Read_UINT64(input_stream, size); /* AllocationSize */
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i] size=%" PRIu64,
|
||||
__func__, file_id, (uint64_t) size);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] "
|
||||
"size=%" PRIu64, __func__, iorequest->file_id, (uint64_t) size);
|
||||
|
||||
/* Truncate file */
|
||||
result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, file_id, size);
|
||||
result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, iorequest->file_id, size);
|
||||
if (result < 0)
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
completion_id, guac_rdp_fs_get_status(result), 4);
|
||||
iorequest->completion_id, guac_rdp_fs_get_status(result), 4);
|
||||
else
|
||||
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);
|
||||
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,
|
||||
wStream* input_stream, int file_id, int completion_id, int length) {
|
||||
void guac_rdpdr_fs_process_set_disposition_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
int length, wStream* input_stream) {
|
||||
|
||||
wStream* output_stream;
|
||||
|
||||
/* Delete file */
|
||||
int result = guac_rdp_fs_delete((guac_rdp_fs*) device->data, file_id);
|
||||
int result = guac_rdp_fs_delete((guac_rdp_fs*) device->data, iorequest->file_id);
|
||||
if (result < 0)
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
completion_id, guac_rdp_fs_get_status(result), 4);
|
||||
iorequest->completion_id, guac_rdp_fs_get_status(result), 4);
|
||||
else
|
||||
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,
|
||||
"%s: [file_id=%i]",
|
||||
__func__, file_id);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||
iorequest->file_id);
|
||||
|
||||
Stream_Write_UINT32(output_stream, 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,
|
||||
wStream* input_stream, int file_id, int completion_id, int length) {
|
||||
void guac_rdpdr_fs_process_set_end_of_file_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
int length, wStream* input_stream) {
|
||||
|
||||
int result;
|
||||
UINT64 size;
|
||||
@ -251,38 +247,37 @@ void guac_rdpdr_fs_process_set_end_of_file_info(guac_rdpdr_device* device,
|
||||
/* Read new size */
|
||||
Stream_Read_UINT64(input_stream, size); /* AllocationSize */
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i] size=%" PRIu64,
|
||||
__func__, file_id, (uint64_t) size);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] "
|
||||
"size=%" PRIu64, __func__, iorequest->file_id, (uint64_t) size);
|
||||
|
||||
/* Truncate file */
|
||||
result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, file_id, size);
|
||||
result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, iorequest->file_id, size);
|
||||
if (result < 0)
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
completion_id, guac_rdp_fs_get_status(result), 4);
|
||||
iorequest->completion_id, guac_rdp_fs_get_status(result), 4);
|
||||
else
|
||||
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);
|
||||
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,
|
||||
wStream* input_stream, int file_id, int completion_id, int length) {
|
||||
void guac_rdpdr_fs_process_set_basic_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
int length, wStream* input_stream) {
|
||||
|
||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||
completion_id, STATUS_SUCCESS, 4);
|
||||
iorequest->completion_id, STATUS_SUCCESS, 4);
|
||||
|
||||
/* Currently do nothing, just respond */
|
||||
Stream_Write_UINT32(output_stream, length);
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i] IGNORED",
|
||||
__func__, file_id);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] IGNORED",
|
||||
__func__, iorequest->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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GUAC_RDPDR_FS_MESSAGES_FILE_INFO_H
|
||||
#define __GUAC_RDPDR_FS_MESSAGES_FILE_INFO_H
|
||||
#ifndef GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_FILE_INFO_H
|
||||
#define GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_FILE_INFO_H
|
||||
|
||||
/**
|
||||
* Handlers for file queries received over the RDPDR channel via the
|
||||
* IRP_MJ_QUERY_INFORMATION major function.
|
||||
*
|
||||
* @file rdpdr_fs_messages_file_info.h
|
||||
* @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>
|
||||
#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
|
||||
* "used to query a file for the times of creation, last access, last write,
|
||||
* and change, in addition to file attribute information."
|
||||
*/
|
||||
void guac_rdpdr_fs_process_query_basic_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_basic_info;
|
||||
|
||||
/**
|
||||
* Processes a query for FileStandardInformation. From the documentation, this
|
||||
* is "used to query for file information such as allocation size, end-of-file
|
||||
* position, and number of links."
|
||||
*/
|
||||
void guac_rdpdr_fs_process_query_standard_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_standard_info;
|
||||
|
||||
/**
|
||||
* Processes a query for FileAttributeTagInformation. From the documentation
|
||||
* this is "used to query for file attribute and reparse tag information."
|
||||
*/
|
||||
void guac_rdpdr_fs_process_query_attribute_tag_info(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_attribute_tag_info;
|
||||
|
||||
/**
|
||||
* Process a set operation for FileRenameInformation. From the documentation,
|
||||
* this operation is used to rename a file.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_set_rename_info(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id, int length);
|
||||
guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_rename_info;
|
||||
|
||||
/**
|
||||
* Process a set operation for FileAllocationInformation. From the
|
||||
* documentation, this operation is used to set a file's allocation size.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_set_allocation_info(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id, int length);
|
||||
guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_allocation_info;
|
||||
|
||||
/**
|
||||
* Process a set operation for FileDispositionInformation. From the
|
||||
* documentation, this operation is used to mark a file for deletion.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_set_disposition_info(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id, int length);
|
||||
guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_disposition_info;
|
||||
|
||||
/**
|
||||
* Process a set operation for FileEndOfFileInformation. From the
|
||||
* documentation, this operation is used "to set end-of-file information for
|
||||
* a file."
|
||||
*/
|
||||
void guac_rdpdr_fs_process_set_end_of_file_info(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id, int length);
|
||||
guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_end_of_file_info;
|
||||
|
||||
/**
|
||||
* Process a set operation for FileBasicInformation. From the documentation,
|
||||
* this is "used to set file information such as the times of creation, last
|
||||
* access, last write, and change, in addition to file attributes."
|
||||
*/
|
||||
void guac_rdpdr_fs_process_set_basic_info(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id, int length);
|
||||
guac_rdpdr_set_information_request_handler guac_rdpdr_fs_process_set_basic_info;
|
||||
|
||||
#endif
|
@ -17,33 +17,29 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "channels/common-svc.h"
|
||||
#include "channels/rdpdr/rdpdr-fs-messages-vol-info.h"
|
||||
#include "channels/rdpdr/rdpdr-fs.h"
|
||||
#include "channels/rdpdr/rdpdr.h"
|
||||
#include "fs.h"
|
||||
|
||||
#include "rdpdr_messages.h"
|
||||
#include "rdpdr_service.h"
|
||||
#include "rdp_fs.h"
|
||||
#include "rdp_status.h"
|
||||
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/unicode.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/io.h>
|
||||
#include <winpr/nt.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/wtypes.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
void guac_rdpdr_fs_process_query_volume_info(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_query_volume_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||
completion_id, STATUS_SUCCESS, 21 + GUAC_FILESYSTEM_LABEL_LENGTH);
|
||||
iorequest->completion_id, STATUS_SUCCESS, 21 + GUAC_FILESYSTEM_LABEL_LENGTH);
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i]",
|
||||
__func__, file_id);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||
iorequest->file_id);
|
||||
|
||||
Stream_Write_UINT32(output_stream, 17 + GUAC_FILESYSTEM_LABEL_LENGTH);
|
||||
Stream_Write_UINT64(output_stream, 0); /* VolumeCreationTime */
|
||||
@ -53,22 +49,22 @@ void guac_rdpdr_fs_process_query_volume_info(guac_rdpdr_device* device,
|
||||
/* Reserved field must not be sent */
|
||||
Stream_Write(output_stream, GUAC_FILESYSTEM_LABEL, GUAC_FILESYSTEM_LABEL_LENGTH);
|
||||
|
||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_fs_process_query_size_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_query_size_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
guac_rdp_fs_info info = {0};
|
||||
guac_rdp_fs_get_info((guac_rdp_fs*) device->data, &info);
|
||||
|
||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||
completion_id, STATUS_SUCCESS, 28);
|
||||
iorequest->completion_id, STATUS_SUCCESS, 28);
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i]",
|
||||
__func__, file_id);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||
iorequest->file_id);
|
||||
|
||||
Stream_Write_UINT32(output_stream, 24);
|
||||
Stream_Write_UINT64(output_stream, info.blocks_total); /* TotalAllocationUnits */
|
||||
@ -76,39 +72,39 @@ 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, info.block_size); /* BytesPerSector */
|
||||
|
||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_fs_process_query_device_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_query_device_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||
completion_id, STATUS_SUCCESS, 12);
|
||||
iorequest->completion_id, STATUS_SUCCESS, 12);
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i]",
|
||||
__func__, file_id);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||
iorequest->file_id);
|
||||
|
||||
Stream_Write_UINT32(output_stream, 8);
|
||||
Stream_Write_UINT32(output_stream, FILE_DEVICE_DISK); /* DeviceType */
|
||||
Stream_Write_UINT32(output_stream, 0); /* Characteristics */
|
||||
|
||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_fs_process_query_attribute_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_query_attribute_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
int name_len = guac_utf8_strlen(device->device_name);
|
||||
|
||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||
completion_id, STATUS_SUCCESS, 16 + name_len);
|
||||
iorequest->completion_id, STATUS_SUCCESS, 16 + name_len);
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i]",
|
||||
__func__, file_id);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||
iorequest->file_id);
|
||||
|
||||
Stream_Write_UINT32(output_stream, 12 + name_len);
|
||||
Stream_Write_UINT32(output_stream,
|
||||
@ -119,22 +115,22 @@ void guac_rdpdr_fs_process_query_attribute_info(guac_rdpdr_device* device, wStre
|
||||
Stream_Write_UINT32(output_stream, name_len);
|
||||
Stream_Write(output_stream, device->device_name, name_len);
|
||||
|
||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_fs_process_query_full_size_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_query_full_size_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
guac_rdp_fs_info info = {0};
|
||||
guac_rdp_fs_get_info((guac_rdp_fs*) device->data, &info);
|
||||
|
||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||
completion_id, STATUS_SUCCESS, 36);
|
||||
iorequest->completion_id, STATUS_SUCCESS, 36);
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i]",
|
||||
__func__, file_id);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]", __func__,
|
||||
iorequest->file_id);
|
||||
|
||||
Stream_Write_UINT32(output_stream, 32);
|
||||
Stream_Write_UINT64(output_stream, info.blocks_total); /* TotalAllocationUnits */
|
||||
@ -143,7 +139,7 @@ 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, 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GUAC_RDPDR_FS_MESSAGES_VOL_INFO_H
|
||||
#define __GUAC_RDPDR_FS_MESSAGES_VOL_INFO_H
|
||||
#ifndef GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_VOL_INFO_H
|
||||
#define GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_VOL_INFO_H
|
||||
|
||||
/**
|
||||
* Handlers for directory queries received over the RDPDR channel via the
|
||||
* IRP_MJ_DIRECTORY_CONTROL major function and the IRP_MN_QUERY_DIRECTORY minor
|
||||
* function.
|
||||
*
|
||||
* @file rdpdr_fs_messages_vol_info.h
|
||||
* @file rdpdr-fs-messages-vol-info.h
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "channels/rdpdr/rdpdr.h"
|
||||
|
||||
#include "rdpdr_service.h"
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/stream.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Processes a query request for FileFsVolumeInformation. According to the
|
||||
* documentation, this is "used to query information for a volume on which a
|
||||
* file system is mounted."
|
||||
*/
|
||||
void guac_rdpdr_fs_process_query_volume_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_volume_info;
|
||||
|
||||
/**
|
||||
* Processes a query request for FileFsSizeInformation.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_query_size_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_size_info;
|
||||
|
||||
/**
|
||||
* Processes a query request for FileFsAttributeInformation.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_query_attribute_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_attribute_info;
|
||||
|
||||
/**
|
||||
* Processes a query request for FileFsFullSizeInformation.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_query_full_size_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_full_size_info;
|
||||
|
||||
/**
|
||||
* Processes a query request for FileFsDeviceInformation.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_query_device_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_device_info;
|
||||
|
||||
#endif
|
||||
|
@ -17,36 +17,29 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "rdpdr_fs_messages_dir_info.h"
|
||||
#include "rdpdr_fs_messages_file_info.h"
|
||||
#include "rdpdr_fs_messages.h"
|
||||
#include "rdpdr_fs_messages_vol_info.h"
|
||||
#include "rdpdr_messages.h"
|
||||
#include "rdpdr_service.h"
|
||||
#include "rdp_fs.h"
|
||||
#include "rdp_status.h"
|
||||
#include "channels/common-svc.h"
|
||||
#include "channels/rdpdr/rdpdr-fs-messages-dir-info.h"
|
||||
#include "channels/rdpdr/rdpdr-fs-messages-file-info.h"
|
||||
#include "channels/rdpdr/rdpdr-fs-messages-vol-info.h"
|
||||
#include "channels/rdpdr/rdpdr-fs-messages.h"
|
||||
#include "channels/rdpdr/rdpdr.h"
|
||||
#include "download.h"
|
||||
#include "fs.h"
|
||||
#include "unicode.h"
|
||||
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/nt.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/wtypes.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int completion_id) {
|
||||
void guac_rdpdr_fs_process_create(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
wStream* output_stream;
|
||||
int file_id;
|
||||
@ -73,7 +66,7 @@ void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
|
||||
desired_access, file_attributes,
|
||||
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] "
|
||||
"desired_access=0x%x, file_attributes=0x%x, "
|
||||
"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 (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);
|
||||
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
guac_rdp_fs_get_status(file_id), 5);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, guac_rdp_fs_get_status(file_id), 5);
|
||||
Stream_Write_UINT32(output_stream, 0); /* fileId */
|
||||
Stream_Write_UINT8(output_stream, 0); /* information */
|
||||
}
|
||||
@ -97,8 +90,8 @@ void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
|
||||
|
||||
guac_rdp_fs_file* file;
|
||||
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
STATUS_SUCCESS, 5);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, STATUS_SUCCESS, 5);
|
||||
Stream_Write_UINT32(output_stream, file_id); /* fileId */
|
||||
Stream_Write_UINT8(output_stream, 0); /* information */
|
||||
|
||||
@ -107,8 +100,7 @@ void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
|
||||
if (file != NULL && strcmp(file->absolute_path, "\\") == 0) {
|
||||
int download_id =
|
||||
guac_rdp_fs_open((guac_rdp_fs*) device->data, "\\Download",
|
||||
ACCESS_GENERIC_READ, 0,
|
||||
DISP_FILE_OPEN_IF, FILE_DIRECTORY_FILE);
|
||||
GENERIC_READ, 0, FILE_OPEN_IF, FILE_DIRECTORY_FILE);
|
||||
|
||||
if (download_id >= 0)
|
||||
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,
|
||||
wStream* input_stream, int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_read(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
UINT32 length;
|
||||
UINT64 offset;
|
||||
@ -134,9 +127,9 @@ void guac_rdpdr_fs_process_read(guac_rdpdr_device* device,
|
||||
Stream_Read_UINT32(input_stream, length);
|
||||
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,
|
||||
__func__, file_id, length, (uint64_t) offset);
|
||||
__func__, iorequest->file_id, length, (uint64_t) offset);
|
||||
|
||||
/* Ensure buffer size does not exceed a safe maximum */
|
||||
if (length > GUAC_RDP_MAX_READ_BUFFER)
|
||||
@ -146,31 +139,32 @@ void guac_rdpdr_fs_process_read(guac_rdpdr_device* device,
|
||||
buffer = malloc(length);
|
||||
|
||||
/* Attempt read */
|
||||
bytes_read = guac_rdp_fs_read((guac_rdp_fs*) device->data, file_id, offset,
|
||||
buffer, length);
|
||||
bytes_read = guac_rdp_fs_read((guac_rdp_fs*) device->data,
|
||||
iorequest->file_id, offset, buffer, length);
|
||||
|
||||
/* If error, return invalid parameter */
|
||||
if (bytes_read < 0) {
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
guac_rdp_fs_get_status(bytes_read), 4);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, guac_rdp_fs_get_status(bytes_read), 4);
|
||||
Stream_Write_UINT32(output_stream, 0); /* Length */
|
||||
}
|
||||
|
||||
/* Otherwise, send bytes read */
|
||||
else {
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
STATUS_SUCCESS, 4+bytes_read);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, STATUS_SUCCESS, 4+bytes_read);
|
||||
Stream_Write_UINT32(output_stream, bytes_read); /* Length */
|
||||
Stream_Write(output_stream, buffer, bytes_read); /* ReadData */
|
||||
}
|
||||
|
||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
free(buffer);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_fs_process_write(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_write(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
UINT32 length;
|
||||
UINT64 offset;
|
||||
@ -183,69 +177,70 @@ void guac_rdpdr_fs_process_write(guac_rdpdr_device* device,
|
||||
Stream_Read_UINT64(input_stream, offset);
|
||||
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,
|
||||
__func__, file_id, length, (uint64_t) offset);
|
||||
__func__, iorequest->file_id, length, (uint64_t) offset);
|
||||
|
||||
/* Attempt write */
|
||||
bytes_written = guac_rdp_fs_write((guac_rdp_fs*) device->data, file_id,
|
||||
offset, Stream_Pointer(input_stream), length);
|
||||
bytes_written = guac_rdp_fs_write((guac_rdp_fs*) device->data,
|
||||
iorequest->file_id, offset, Stream_Pointer(input_stream), length);
|
||||
|
||||
/* If error, return invalid parameter */
|
||||
if (bytes_written < 0) {
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
guac_rdp_fs_get_status(bytes_written), 5);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, guac_rdp_fs_get_status(bytes_written), 5);
|
||||
Stream_Write_UINT32(output_stream, 0); /* Length */
|
||||
Stream_Write_UINT8(output_stream, 0); /* Padding */
|
||||
}
|
||||
|
||||
/* Otherwise, send success */
|
||||
else {
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
STATUS_SUCCESS, 5);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, STATUS_SUCCESS, 5);
|
||||
Stream_Write_UINT32(output_stream, bytes_written); /* Length */
|
||||
Stream_Write_UINT8(output_stream, 0); /* Padding */
|
||||
}
|
||||
|
||||
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,
|
||||
wStream* input_stream, int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_close(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
wStream* output_stream;
|
||||
guac_rdp_fs_file* file;
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i]",
|
||||
__func__, file_id);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i]",
|
||||
__func__, iorequest->file_id);
|
||||
|
||||
/* 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)
|
||||
return;
|
||||
|
||||
/* If file was written to, and it's in the \Download folder, start stream */
|
||||
if (file->bytes_written > 0 &&
|
||||
strncmp(file->absolute_path, "\\Download\\", 10) == 0) {
|
||||
guac_rdpdr_start_download(device, file->absolute_path);
|
||||
guac_rdp_fs_delete((guac_rdp_fs*) device->data, file_id);
|
||||
guac_client_for_owner(svc->client, guac_rdp_download_to_user, file->absolute_path);
|
||||
guac_rdp_fs_delete((guac_rdp_fs*) device->data, iorequest->file_id);
|
||||
}
|
||||
|
||||
/* Close file */
|
||||
guac_rdp_fs_close((guac_rdp_fs*) device->data, file_id);
|
||||
guac_rdp_fs_close((guac_rdp_fs*) device->data, iorequest->file_id);
|
||||
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
STATUS_SUCCESS, 4);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, STATUS_SUCCESS, 4);
|
||||
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,
|
||||
int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_volume_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
int fs_information_class;
|
||||
|
||||
@ -255,39 +250,35 @@ void guac_rdpdr_fs_process_volume_info(guac_rdpdr_device* device, wStream* input
|
||||
switch (fs_information_class) {
|
||||
|
||||
case FileFsVolumeInformation:
|
||||
guac_rdpdr_fs_process_query_volume_info(device, input_stream,
|
||||
file_id, completion_id);
|
||||
guac_rdpdr_fs_process_query_volume_info(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
case FileFsSizeInformation:
|
||||
guac_rdpdr_fs_process_query_size_info(device, input_stream,
|
||||
file_id, completion_id);
|
||||
guac_rdpdr_fs_process_query_size_info(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
case FileFsDeviceInformation:
|
||||
guac_rdpdr_fs_process_query_device_info(device, input_stream,
|
||||
file_id, completion_id);
|
||||
guac_rdpdr_fs_process_query_device_info(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
case FileFsAttributeInformation:
|
||||
guac_rdpdr_fs_process_query_attribute_info(device, input_stream,
|
||||
file_id, completion_id);
|
||||
guac_rdpdr_fs_process_query_attribute_info(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
case FileFsFullSizeInformation:
|
||||
guac_rdpdr_fs_process_query_full_size_info(device, input_stream,
|
||||
file_id, completion_id);
|
||||
guac_rdpdr_fs_process_query_full_size_info(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_fs_process_file_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_file_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
int fs_information_class;
|
||||
|
||||
@ -297,43 +288,42 @@ void guac_rdpdr_fs_process_file_info(guac_rdpdr_device* device, wStream* input_s
|
||||
switch (fs_information_class) {
|
||||
|
||||
case FileBasicInformation:
|
||||
guac_rdpdr_fs_process_query_basic_info(device, input_stream,
|
||||
file_id, completion_id);
|
||||
guac_rdpdr_fs_process_query_basic_info(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
case FileStandardInformation:
|
||||
guac_rdpdr_fs_process_query_standard_info(device, input_stream,
|
||||
file_id, completion_id);
|
||||
guac_rdpdr_fs_process_query_standard_info(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
case FileAttributeTagInformation:
|
||||
guac_rdpdr_fs_process_query_attribute_tag_info(device, input_stream,
|
||||
file_id, completion_id);
|
||||
guac_rdpdr_fs_process_query_attribute_tag_info(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_fs_process_set_volume_info(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_set_volume_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||
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",
|
||||
__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,
|
||||
wStream* input_stream, int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_set_file_info(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
int fs_information_class;
|
||||
int length;
|
||||
@ -346,66 +336,62 @@ void guac_rdpdr_fs_process_set_file_info(guac_rdpdr_device* device,
|
||||
switch (fs_information_class) {
|
||||
|
||||
case FileBasicInformation:
|
||||
guac_rdpdr_fs_process_set_basic_info(device, input_stream,
|
||||
file_id, completion_id, length);
|
||||
guac_rdpdr_fs_process_set_basic_info(svc, device, iorequest, length, input_stream);
|
||||
break;
|
||||
|
||||
case FileEndOfFileInformation:
|
||||
guac_rdpdr_fs_process_set_end_of_file_info(device, input_stream,
|
||||
file_id, completion_id, length);
|
||||
guac_rdpdr_fs_process_set_end_of_file_info(svc, device, iorequest, length, input_stream);
|
||||
break;
|
||||
|
||||
case FileDispositionInformation:
|
||||
guac_rdpdr_fs_process_set_disposition_info(device, input_stream,
|
||||
file_id, completion_id, length);
|
||||
guac_rdpdr_fs_process_set_disposition_info(svc, device, iorequest, length, input_stream);
|
||||
break;
|
||||
|
||||
case FileRenameInformation:
|
||||
guac_rdpdr_fs_process_set_rename_info(device, input_stream,
|
||||
file_id, completion_id, length);
|
||||
guac_rdpdr_fs_process_set_rename_info(svc, device, iorequest, length, input_stream);
|
||||
break;
|
||||
|
||||
case FileAllocationInformation:
|
||||
guac_rdpdr_fs_process_set_allocation_info(device, input_stream,
|
||||
file_id, completion_id, length);
|
||||
guac_rdpdr_fs_process_set_allocation_info(svc, device, iorequest, length, input_stream);
|
||||
break;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_fs_process_device_control(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_device_control(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||
completion_id, STATUS_INVALID_PARAMETER, 4);
|
||||
iorequest->completion_id, STATUS_INVALID_PARAMETER, 4);
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i] IGNORED",
|
||||
__func__, file_id);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] IGNORED",
|
||||
__func__, iorequest->file_id);
|
||||
|
||||
/* No content for now */
|
||||
Stream_Write_UINT32(output_stream, 0);
|
||||
|
||||
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,
|
||||
wStream* input_stream, int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_notify_change_directory(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: [file_id=%i] Not implemented",
|
||||
__func__, file_id);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] Not "
|
||||
"implemented", __func__, iorequest->file_id);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_query_directory(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
wStream* output_stream;
|
||||
|
||||
@ -416,7 +402,7 @@ void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* i
|
||||
const char* entry_name;
|
||||
|
||||
/* Get file */
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
|
||||
file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, iorequest->file_id);
|
||||
if (file == NULL)
|
||||
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,
|
||||
"%s: [file_id=%i] initial_query=%i, dir_pattern=\"%s\"",
|
||||
__func__, file_id, initial_query, file->dir_pattern);
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG, "%s: [file_id=%i] "
|
||||
"initial_query=%i, dir_pattern=\"%s\"", __func__,
|
||||
iorequest->file_id, initial_query, file->dir_pattern);
|
||||
|
||||
/* Find first matching entry in directory */
|
||||
while ((entry_name = guac_rdp_fs_read_dir((guac_rdp_fs*) device->data,
|
||||
file_id)) != NULL) {
|
||||
iorequest->file_id)) != NULL) {
|
||||
|
||||
/* Convert to absolute 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 */
|
||||
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) {
|
||||
|
||||
@ -465,27 +451,27 @@ void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* i
|
||||
switch (fs_information_class) {
|
||||
|
||||
case FileDirectoryInformation:
|
||||
guac_rdpdr_fs_process_query_directory_info(device,
|
||||
entry_name, entry_file_id, completion_id);
|
||||
guac_rdpdr_fs_process_query_directory_info(svc, device,
|
||||
iorequest, entry_name, entry_file_id);
|
||||
break;
|
||||
|
||||
case FileFullDirectoryInformation:
|
||||
guac_rdpdr_fs_process_query_full_directory_info(device,
|
||||
entry_name, entry_file_id, completion_id);
|
||||
guac_rdpdr_fs_process_query_full_directory_info(svc,
|
||||
device, iorequest, entry_name, entry_file_id);
|
||||
break;
|
||||
|
||||
case FileBothDirectoryInformation:
|
||||
guac_rdpdr_fs_process_query_both_directory_info(device,
|
||||
entry_name, entry_file_id, completion_id);
|
||||
guac_rdpdr_fs_process_query_both_directory_info(svc,
|
||||
device, iorequest, entry_name, entry_file_id);
|
||||
break;
|
||||
|
||||
case FileNamesInformation:
|
||||
guac_rdpdr_fs_process_query_names_info(device,
|
||||
entry_name, entry_file_id, completion_id);
|
||||
guac_rdpdr_fs_process_query_names_info(svc, device,
|
||||
iorequest, entry_name, entry_file_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||
"Unknown dir information class: 0x%x",
|
||||
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.
|
||||
*/
|
||||
|
||||
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
||||
STATUS_NO_MORE_FILES, 5);
|
||||
output_stream = guac_rdpdr_new_io_completion(device,
|
||||
iorequest->completion_id, STATUS_NO_MORE_FILES, 5);
|
||||
|
||||
Stream_Write_UINT32(output_stream, 0); /* Length */
|
||||
Stream_Write_UINT8(output_stream, 0); /* Padding */
|
||||
|
||||
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,
|
||||
int file_id, int completion_id) {
|
||||
void guac_rdpdr_fs_process_lock_control(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||
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",
|
||||
__func__, file_id);
|
||||
__func__, iorequest->file_id);
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GUAC_RDPDR_FS_MESSAGES_H
|
||||
#define __GUAC_RDPDR_FS_MESSAGES_H
|
||||
#ifndef GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_H
|
||||
#define GUAC_RDP_CHANNELS_RDPDR_FS_MESSAGES_H
|
||||
|
||||
/**
|
||||
* Handlers for core drive I/O requests. Requests handled here may be simple
|
||||
* messages handled directly, or more complex multi-type messages handled
|
||||
* elsewhere.
|
||||
*
|
||||
* @file rdpdr_fs_messages.h
|
||||
* @file rdpdr-fs-messages.h
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "channels/rdpdr/rdpdr.h"
|
||||
|
||||
#include "rdpdr_service.h"
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/stream.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Handles a Server Create Drive Request. Despite its name, this request opens
|
||||
* a file.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_create;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
wStream* input_stream, int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_close;
|
||||
|
||||
/**
|
||||
* Handles a Server Drive Read Request. This request reads from a file.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_read(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_read;
|
||||
|
||||
/**
|
||||
* Handles a Server Drive Write Request. This request writes to a file.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_write(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_write;
|
||||
|
||||
/**
|
||||
* Handles a Server Drive Control Request. This request handles one of any
|
||||
* number of Windows FSCTL_* control functions.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_device_control(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_device_control;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* separate file.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_volume_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_volume_info;
|
||||
|
||||
/**
|
||||
* Handles a Server Drive Set Volume Information Request. Currently, this
|
||||
* RDPDR implementation does not support setting of volume information.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_set_volume_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_set_volume_info;
|
||||
|
||||
/**
|
||||
* Handles a Server Drive Query Information Request. This request queries
|
||||
* information about a specific file. This request has several query types
|
||||
* which have their own handlers defined in a separate file.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_file_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_file_info;
|
||||
|
||||
/**
|
||||
* Handles a Server Drive Set Information Request. This request sets
|
||||
* information about a specific file. Currently, this RDPDR implementation does
|
||||
* not support setting of file information.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_set_file_info(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_set_file_info;
|
||||
|
||||
/**
|
||||
* Handles a Server Drive Query Directory Request. This request queries
|
||||
* information about a specific directory. This request has several query types
|
||||
* which have their own handlers defined in a separate file.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_query_directory;
|
||||
|
||||
/**
|
||||
* Handles a Server Drive NotifyChange Directory Request. This request requests
|
||||
* directory change notification.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_notify_change_directory(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_notify_change_directory;
|
||||
|
||||
/**
|
||||
* Handles a Server Drive Lock Control Request. This request locks or unlocks
|
||||
* portions of a file.
|
||||
*/
|
||||
void guac_rdpdr_fs_process_lock_control(guac_rdpdr_device* device, wStream* input_stream,
|
||||
int file_id, int completion_id);
|
||||
guac_rdpdr_device_iorequest_handler guac_rdpdr_fs_process_lock_control;
|
||||
|
||||
#endif
|
||||
|
@ -17,119 +17,116 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "channels/rdpdr/rdpdr-fs.h"
|
||||
#include "channels/rdpdr/rdpdr-fs-messages.h"
|
||||
#include "channels/rdpdr/rdpdr.h"
|
||||
#include "rdp.h"
|
||||
#include "rdpdr_fs_messages.h"
|
||||
#include "rdpdr_messages.h"
|
||||
#include "rdpdr_service.h"
|
||||
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
#include <freerdp/settings.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/unicode.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/stream.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#endif
|
||||
|
||||
static void guac_rdpdr_device_fs_iorequest_handler(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id, int major_func, int minor_func) {
|
||||
#include <stddef.h>
|
||||
|
||||
switch (major_func) {
|
||||
void guac_rdpdr_device_fs_iorequest_handler(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
switch (iorequest->major_func) {
|
||||
|
||||
/* File open */
|
||||
case IRP_MJ_CREATE:
|
||||
guac_rdpdr_fs_process_create(device, input_stream, completion_id);
|
||||
guac_rdpdr_fs_process_create(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
/* File close */
|
||||
case IRP_MJ_CLOSE:
|
||||
guac_rdpdr_fs_process_close(device, input_stream, file_id, completion_id);
|
||||
guac_rdpdr_fs_process_close(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
/* File read */
|
||||
case IRP_MJ_READ:
|
||||
guac_rdpdr_fs_process_read(device, input_stream, file_id, completion_id);
|
||||
guac_rdpdr_fs_process_read(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
/* File write */
|
||||
case IRP_MJ_WRITE:
|
||||
guac_rdpdr_fs_process_write(device, input_stream, file_id, completion_id);
|
||||
guac_rdpdr_fs_process_write(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
/* Device control request (Windows FSCTL_ control codes) */
|
||||
case IRP_MJ_DEVICE_CONTROL:
|
||||
guac_rdpdr_fs_process_device_control(device, input_stream, file_id, completion_id);
|
||||
guac_rdpdr_fs_process_device_control(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
/* Query volume (drive) information */
|
||||
case IRP_MJ_QUERY_VOLUME_INFORMATION:
|
||||
guac_rdpdr_fs_process_volume_info(device, input_stream, file_id, completion_id);
|
||||
guac_rdpdr_fs_process_volume_info(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
/* Set volume (drive) information */
|
||||
case IRP_MJ_SET_VOLUME_INFORMATION:
|
||||
guac_rdpdr_fs_process_set_volume_info(device, input_stream, file_id, completion_id);
|
||||
guac_rdpdr_fs_process_set_volume_info(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
/* Query file information */
|
||||
case IRP_MJ_QUERY_INFORMATION:
|
||||
guac_rdpdr_fs_process_file_info(device, input_stream, file_id, completion_id);
|
||||
guac_rdpdr_fs_process_file_info(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
/* Set file information */
|
||||
case IRP_MJ_SET_INFORMATION:
|
||||
guac_rdpdr_fs_process_set_file_info(device, input_stream, file_id, completion_id);
|
||||
guac_rdpdr_fs_process_set_file_info(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
case IRP_MJ_DIRECTORY_CONTROL:
|
||||
|
||||
/* Enumerate directory contents */
|
||||
if (minor_func == IRP_MN_QUERY_DIRECTORY)
|
||||
guac_rdpdr_fs_process_query_directory(device, input_stream, file_id, completion_id);
|
||||
if (iorequest->minor_func == IRP_MN_QUERY_DIRECTORY)
|
||||
guac_rdpdr_fs_process_query_directory(svc, device, iorequest,
|
||||
input_stream);
|
||||
|
||||
/* Request notification of changes to directory */
|
||||
else if (minor_func == IRP_MN_NOTIFY_CHANGE_DIRECTORY)
|
||||
guac_rdpdr_fs_process_notify_change_directory(device, input_stream,
|
||||
file_id, completion_id);
|
||||
else if (iorequest->minor_func == IRP_MN_NOTIFY_CHANGE_DIRECTORY)
|
||||
guac_rdpdr_fs_process_notify_change_directory(svc, device,
|
||||
iorequest, input_stream);
|
||||
|
||||
break;
|
||||
|
||||
/* Lock/unlock portions of a file */
|
||||
case IRP_MJ_LOCK_CONTROL:
|
||||
guac_rdpdr_fs_process_lock_control(device, input_stream, file_id, completion_id);
|
||||
guac_rdpdr_fs_process_lock_control(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
default:
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
|
||||
guac_client_log(svc->client, GUAC_LOG_DEBUG,
|
||||
"Unknown filesystem I/O request function: 0x%x/0x%x",
|
||||
major_func, minor_func);
|
||||
iorequest->major_func, iorequest->minor_func);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void guac_rdpdr_device_fs_free_handler(guac_rdpdr_device* device) {
|
||||
void guac_rdpdr_device_fs_free_handler(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device) {
|
||||
|
||||
Stream_Free(device->device_announce, 1);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_register_fs(guac_rdpdrPlugin* rdpdr, char* drive_name) {
|
||||
void guac_rdpdr_register_fs(guac_rdp_common_svc* svc, char* drive_name) {
|
||||
|
||||
guac_client* client = rdpdr->client;
|
||||
guac_client* client = svc->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data;
|
||||
int id = rdpdr->devices_registered++;
|
||||
|
||||
/* Get new device */
|
||||
guac_rdpdr_device* device = &(rdpdr->devices[id]);
|
||||
|
||||
/* Init device */
|
||||
device->rdpdr = rdpdr;
|
||||
device->device_id = id;
|
||||
device->device_name = drive_name;
|
||||
int device_name_len = guac_utf8_strlen(device->device_name);
|
@ -17,38 +17,46 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GUAC_RDPDR_FS_H
|
||||
#define __GUAC_RDPDR_FS_H
|
||||
#ifndef GUAC_RDP_RDPDR_FS_H
|
||||
#define GUAC_RDP_RDPDR_FS_H
|
||||
|
||||
/**
|
||||
* Functions and macros specific to filesystem handling and initialization
|
||||
* independent of RDP. The functions here may deal with the RDPDR device
|
||||
* independent of RDP. The functions here may deal with the RDPDR device
|
||||
* directly, but their semantics must not deal with RDP protocol messaging.
|
||||
* Functions here represent a virtual Windows-style filesystem on top of UNIX
|
||||
* system calls and structures, using the guac_rdpdr_device structure as a home
|
||||
* for common data.
|
||||
*
|
||||
* @file rdpdr_fs.h
|
||||
* @file rdpdr-fs.h
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "rdpdr_service.h"
|
||||
#include "channels/common-svc.h"
|
||||
#include "channels/rdpdr/rdpdr.h"
|
||||
|
||||
#include <guacamole/pool.h>
|
||||
|
||||
/**
|
||||
* The UTF-16 string that should be sent as the label of the filesystem.
|
||||
*/
|
||||
#define GUAC_FILESYSTEM_LABEL "G\0U\0A\0C\0F\0I\0L\0E\0"
|
||||
|
||||
/**
|
||||
* The size of GUAC_FILESYSTEM_LABEL in bytes.
|
||||
*/
|
||||
#define GUAC_FILESYSTEM_LABEL_LENGTH 16
|
||||
|
||||
/**
|
||||
* Registers a new filesystem device within the RDPDR plugin. This must be done
|
||||
* before RDPDR connection finishes.
|
||||
*
|
||||
* @param rdpdr
|
||||
* The RDP device redirection plugin with which to register the device.
|
||||
* @param svc
|
||||
* The static virtual channel instance being used for RDPDR.
|
||||
*
|
||||
* @param drive_name
|
||||
* The name of the redirected drive to display in the RDP connection.
|
||||
*/
|
||||
void guac_rdpdr_register_fs(guac_rdpdrPlugin* rdpdr, char* drive_name);
|
||||
void guac_rdpdr_register_fs(guac_rdp_common_svc* svc, char* drive_name);
|
||||
|
||||
#endif
|
||||
|
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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "rdpdr_messages.h"
|
||||
#include "rdpdr_printer.h"
|
||||
#include "rdpdr_service.h"
|
||||
#include "channels/rdpdr/rdpdr-printer.h"
|
||||
#include "channels/rdpdr/rdpdr.h"
|
||||
#include "print-job.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_print_job.h"
|
||||
#include "rdp_status.h"
|
||||
#include "unicode.h"
|
||||
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
#include <freerdp/settings.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/stream.h>
|
||||
#include <guacamole/unicode.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/nt.h>
|
||||
#include <winpr/stream.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void guac_rdpdr_process_print_job_create(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int completion_id) {
|
||||
void guac_rdpdr_process_print_job_create(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
guac_client* client = device->rdpdr->client;
|
||||
guac_client* client = svc->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Log creation of print job */
|
||||
@ -63,17 +48,18 @@ void guac_rdpdr_process_print_job_create(guac_rdpdr_device* device,
|
||||
|
||||
/* Respond with success */
|
||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||
completion_id, STATUS_SUCCESS, 4);
|
||||
iorequest->completion_id, STATUS_SUCCESS, 4);
|
||||
|
||||
Stream_Write_UINT32(output_stream, 0); /* fileId */
|
||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int completion_id) {
|
||||
void guac_rdpdr_process_print_job_write(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
guac_client* client = device->rdpdr->client;
|
||||
guac_client* client = svc->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
|
||||
|
||||
@ -100,19 +86,20 @@ void guac_rdpdr_process_print_job_write(guac_rdpdr_device* 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_UINT8(output_stream, 0); /* Padding */
|
||||
|
||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_process_print_job_close(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int completion_id) {
|
||||
void guac_rdpdr_process_print_job_close(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
guac_client* client = device->rdpdr->client;
|
||||
guac_client* client = svc->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
|
||||
|
||||
@ -123,61 +110,63 @@ void guac_rdpdr_process_print_job_close(guac_rdpdr_device* 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 */
|
||||
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
/* Log end of print job */
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Print job closed");
|
||||
|
||||
}
|
||||
|
||||
static void guac_rdpdr_device_printer_iorequest_handler(guac_rdpdr_device* device,
|
||||
wStream* input_stream, int file_id, int completion_id, int major_func, int minor_func) {
|
||||
void guac_rdpdr_device_printer_iorequest_handler(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
|
||||
wStream* input_stream) {
|
||||
|
||||
switch (major_func) {
|
||||
switch (iorequest->major_func) {
|
||||
|
||||
/* Print job create */
|
||||
case IRP_MJ_CREATE:
|
||||
guac_rdpdr_process_print_job_create(device, input_stream, completion_id);
|
||||
guac_rdpdr_process_print_job_create(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
/* Printer job write */
|
||||
case IRP_MJ_WRITE:
|
||||
guac_rdpdr_process_print_job_write(device, input_stream, completion_id);
|
||||
guac_rdpdr_process_print_job_write(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
/* Printer job close */
|
||||
case IRP_MJ_CLOSE:
|
||||
guac_rdpdr_process_print_job_close(device, input_stream, completion_id);
|
||||
guac_rdpdr_process_print_job_close(svc, device, iorequest, input_stream);
|
||||
break;
|
||||
|
||||
/* Log unknown */
|
||||
default:
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
|
||||
"Unknown printer I/O request function: 0x%x/0x%x",
|
||||
major_func, minor_func);
|
||||
guac_client_log(svc->client, GUAC_LOG_ERROR, "Unknown printer "
|
||||
"I/O request function: 0x%x/0x%x", iorequest->major_func,
|
||||
iorequest->minor_func);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void guac_rdpdr_device_printer_free_handler(guac_rdpdr_device* device) {
|
||||
void guac_rdpdr_device_printer_free_handler(guac_rdp_common_svc* svc,
|
||||
guac_rdpdr_device* device) {
|
||||
|
||||
Stream_Free(device->device_announce, 1);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr, char* printer_name) {
|
||||
void guac_rdpdr_register_printer(guac_rdp_common_svc* svc, char* printer_name) {
|
||||
|
||||
guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data;
|
||||
int id = rdpdr->devices_registered++;
|
||||
|
||||
/* Get new device */
|
||||
guac_rdpdr_device* device = &(rdpdr->devices[id]);
|
||||
|
||||
/* Init device */
|
||||
device->rdpdr = rdpdr;
|
||||
device->device_id = id;
|
||||
device->device_name = printer_name;
|
||||
int device_name_len = guac_utf8_strlen(device->device_name);
|
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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "channels/rdpsnd/rdpsnd-messages.h"
|
||||
#include "channels/rdpsnd/rdpsnd.h"
|
||||
#include "rdp.h"
|
||||
#include "rdpsnd_messages.h"
|
||||
#include "rdpsnd_service.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
#include <freerdp/codec/audio.h>
|
||||
#include <guacamole/audio.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/wtypes.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
/* MESSAGE HANDLERS */
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc,
|
||||
wStream* input_stream, guac_rdpsnd_pdu_header* header) {
|
||||
|
||||
int server_format_count;
|
||||
@ -51,11 +41,10 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
int output_body_size;
|
||||
unsigned char* output_stream_end;
|
||||
|
||||
/* Get associated client data */
|
||||
guac_client* client = rdpsnd->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_client* client = svc->client;
|
||||
guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
|
||||
|
||||
/* Get audio stream from client data */
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_audio_stream* audio = rdp_client->audio;
|
||||
|
||||
/* Reset own format count */
|
||||
@ -188,8 +177,7 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
Stream_SetPointer(output_stream, output_stream_end);
|
||||
|
||||
/* Send accepted formats */
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
svc_plugin_send((rdpSvcPlugin*)rdpsnd, output_stream);
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
/* If version greater than 6, must send Quality Mode PDU */
|
||||
if (server_version >= 6) {
|
||||
@ -202,23 +190,20 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
Stream_Write_UINT16(output_stream, HIGH_QUALITY);
|
||||
Stream_Write_UINT16(output_stream, 0);
|
||||
|
||||
svc_plugin_send((rdpSvcPlugin*)rdpsnd, output_stream);
|
||||
}
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* server is getting a feel of the round trip time */
|
||||
void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
void guac_rdpsnd_training_handler(guac_rdp_common_svc* svc,
|
||||
wStream* input_stream, guac_rdpsnd_pdu_header* header) {
|
||||
|
||||
int data_size;
|
||||
wStream* output_stream;
|
||||
|
||||
/* Get associated client data */
|
||||
guac_client* client = rdpsnd->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
|
||||
|
||||
/* Read timestamp and data size */
|
||||
Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);
|
||||
@ -232,22 +217,19 @@ void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp);
|
||||
Stream_Write_UINT16(output_stream, data_size);
|
||||
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
svc_plugin_send((rdpSvcPlugin*) rdpsnd, output_stream);
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc,
|
||||
wStream* input_stream, guac_rdpsnd_pdu_header* header) {
|
||||
|
||||
int format;
|
||||
|
||||
/* Get associated client data */
|
||||
guac_client* client = rdpsnd->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_client* client = svc->client;
|
||||
guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
|
||||
|
||||
/* Get audio stream from client data */
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_audio_stream* audio = rdp_client->audio;
|
||||
|
||||
/* Read wave information */
|
||||
@ -276,16 +258,13 @@ void guac_rdpsnd_wave_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) {
|
||||
|
||||
rdpSvcPlugin* plugin = (rdpSvcPlugin*)rdpsnd;
|
||||
guac_client* client = svc->client;
|
||||
guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
|
||||
|
||||
/* Get associated client data */
|
||||
guac_client* client = rdpsnd->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Get audio stream from client data */
|
||||
guac_audio_stream* audio = rdp_client->audio;
|
||||
|
||||
/* Wave Confirmation PDU */
|
||||
@ -313,16 +292,14 @@ void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
Stream_Write_UINT8(output_stream, 0);
|
||||
|
||||
/* Send Wave Confirmation PDU */
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
svc_plugin_send(plugin, output_stream);
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
guac_rdp_common_svc_write(svc, output_stream);
|
||||
|
||||
/* We no longer expect to receive wave data */
|
||||
rdpsnd->next_pdu_is_wave = FALSE;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpsnd_close_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
void guac_rdpsnd_close_handler(guac_rdp_common_svc* svc,
|
||||
wStream* input_stream, guac_rdpsnd_pdu_header* header) {
|
||||
|
||||
/* Do nothing */
|
@ -17,87 +17,12 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_CHANNELS_RDPSND_MESSAGES_H
|
||||
#define GUAC_RDP_CHANNELS_RDPSND_MESSAGES_H
|
||||
|
||||
#ifndef __GUAC_RDPSND_MESSAGES_H
|
||||
#define __GUAC_RDPSND_MESSAGES_H
|
||||
#include "channels/common-svc.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "rdpsnd_service.h"
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/stream.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* PDU Message Types
|
||||
*/
|
||||
|
||||
/**
|
||||
* Close PDU
|
||||
*/
|
||||
#define SNDC_CLOSE 1
|
||||
|
||||
/**
|
||||
* WaveInfo PDU. This PDU is sent just before wave data is sent.
|
||||
*/
|
||||
#define SNDC_WAVE 2
|
||||
|
||||
/**
|
||||
* Wave Confirm PDU. This PDU is sent in response to the WaveInfo PDU,
|
||||
* confirming it has been received and played.
|
||||
*/
|
||||
#define SNDC_WAVECONFIRM 5
|
||||
|
||||
/**
|
||||
* Training PDU. This PDU is sent by the server occasionally and must be
|
||||
* responded to with another training PDU, similar to Guac's sync message.
|
||||
*/
|
||||
#define SNDC_TRAINING 6
|
||||
|
||||
/**
|
||||
* Server Audio Formats and Version PDU. This PDU is sent by the server to
|
||||
* advertise to the client which audio formats are supported.
|
||||
*/
|
||||
#define SNDC_FORMATS 7
|
||||
|
||||
/**
|
||||
* Quality Mode PDU. This PDU must be sent by the client to select an audio
|
||||
* quality mode if the server is at least version 6.
|
||||
*/
|
||||
#define SNDC_QUALITYMODE 12
|
||||
|
||||
/*
|
||||
* Quality Modes
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dynamic Quality. The server will choose the audio quality based on its
|
||||
* perception of latency.
|
||||
*/
|
||||
#define DYNAMIC_QUALITY 0x0000
|
||||
|
||||
/**
|
||||
* Medium Quality. The server prioritizes bandwidth over quality.
|
||||
*/
|
||||
#define MEDIUM_QUALITY 0x0001
|
||||
|
||||
/**
|
||||
* High Quality. The server prioritizes quality over bandwidth.
|
||||
*/
|
||||
#define HIGH_QUALITY 0x0002
|
||||
|
||||
/*
|
||||
* Capabilities
|
||||
*/
|
||||
#define TSSNDCAPS_ALIVE 1
|
||||
|
||||
/*
|
||||
* Sound Formats
|
||||
*/
|
||||
#define WAVE_FORMAT_PCM 1
|
||||
|
||||
/**
|
||||
* The header common to all RDPSND PDUs.
|
||||
@ -121,8 +46,8 @@ typedef struct guac_rdpsnd_pdu_header {
|
||||
* SNDC_FORMATS PDU describes all audio formats supported by the RDP server, as
|
||||
* well as the version of RDPSND implemented.
|
||||
*
|
||||
* @param rdpsnd
|
||||
* The Guacamole RDPSND plugin receiving the SNDC_FORMATS PDU.
|
||||
* @param svc
|
||||
* The RDPSND channel receiving the SNDC_FORMATS PDU.
|
||||
*
|
||||
* @param input_stream
|
||||
* The FreeRDP input stream containing the remaining raw bytes (after the
|
||||
@ -132,7 +57,7 @@ typedef struct guac_rdpsnd_pdu_header {
|
||||
* The header content of the SNDC_FORMATS PDU. All RDPSND messages contain
|
||||
* the same header information.
|
||||
*/
|
||||
void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc,
|
||||
wStream* input_stream, guac_rdpsnd_pdu_header* header);
|
||||
|
||||
/**
|
||||
@ -142,8 +67,8 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
*
|
||||
* https://msdn.microsoft.com/en-us/library/cc240961.aspx
|
||||
*
|
||||
* @param rdpsnd
|
||||
* The Guacamole RDPSND plugin receiving the SNDC_TRAINING PDU.
|
||||
* @param svc
|
||||
* The RDPSND channel receiving the SNDC_TRAINING PDU.
|
||||
*
|
||||
* @param input_stream
|
||||
* The FreeRDP input stream containing the remaining raw bytes (after the
|
||||
@ -153,7 +78,7 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
* The header content of the SNDC_TRAINING PDU. All RDPSND messages contain
|
||||
* the same header information.
|
||||
*/
|
||||
void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
void guac_rdpsnd_training_handler(guac_rdp_common_svc* svc,
|
||||
wStream* input_stream, guac_rdpsnd_pdu_header* header);
|
||||
|
||||
/**
|
||||
@ -165,8 +90,8 @@ void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
*
|
||||
* https://msdn.microsoft.com/en-us/library/cc240963.aspx
|
||||
*
|
||||
* @param rdpsnd
|
||||
* The Guacamole RDPSND plugin receiving the SNDC_WAVE PDU.
|
||||
* @param svc
|
||||
* The RDPSND channel receiving the SNDC_WAVE PDU.
|
||||
*
|
||||
* @param input_stream
|
||||
* The FreeRDP input stream containing the remaining raw bytes (after the
|
||||
@ -176,7 +101,7 @@ void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
* The header content of the SNDC_WAVE PDU. All RDPSND messages contain
|
||||
* the same header information.
|
||||
*/
|
||||
void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc,
|
||||
wStream* input_stream, guac_rdpsnd_pdu_header* header);
|
||||
|
||||
/**
|
||||
@ -184,8 +109,8 @@ void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
* PDU contains the actual audio data, less the four bytes of audio data
|
||||
* included in the SNDC_WAVE PDU.
|
||||
*
|
||||
* @param rdpsnd
|
||||
* The Guacamole RDPSND plugin receiving the SNDWAV PDU.
|
||||
* @param svc
|
||||
* The RDPSND channel receiving the SNDWAV PDU.
|
||||
*
|
||||
* @param input_stream
|
||||
* The FreeRDP input stream containing the remaining raw bytes (after the
|
||||
@ -195,7 +120,7 @@ void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
* The header content of the SNDWAV PDU. All RDPSND messages contain
|
||||
* the same header information.
|
||||
*/
|
||||
void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
void guac_rdpsnd_wave_handler(guac_rdp_common_svc* svc,
|
||||
wStream* input_stream, guac_rdpsnd_pdu_header* header);
|
||||
|
||||
/**
|
||||
@ -204,8 +129,8 @@ void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
*
|
||||
* https://msdn.microsoft.com/en-us/library/cc240970.aspx
|
||||
*
|
||||
* @param rdpsnd
|
||||
* The Guacamole RDPSND plugin receiving the SNDC_CLOSE PDU.
|
||||
* @param svc
|
||||
* The RDPSND channel receiving the SNDC_CLOSE PDU.
|
||||
*
|
||||
* @param input_stream
|
||||
* The FreeRDP input stream containing the remaining raw bytes (after the
|
||||
@ -215,7 +140,7 @@ void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
* The header content of the SNDC_CLOSE PDU. All RDPSND messages contain
|
||||
* the same header information.
|
||||
*/
|
||||
void guac_rdpsnd_close_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
void guac_rdpsnd_close_handler(guac_rdp_common_svc* svc,
|
||||
wStream* input_stream, guac_rdpsnd_pdu_header* header);
|
||||
|
||||
#endif
|
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.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_CHANNELS_RDPSND_H
|
||||
#define GUAC_RDP_CHANNELS_RDPSND_H
|
||||
|
||||
#ifndef __GUAC_RDPSND_SERVICE_H
|
||||
#define __GUAC_RDPSND_SERVICE_H
|
||||
#include "channels/common-svc.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/stream.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The maximum number of PCM formats to accept during the initial RDPSND
|
||||
* handshake with the RDP server.
|
||||
@ -42,7 +35,7 @@
|
||||
* Abstract representation of a PCM format, including the sample rate, number
|
||||
* of channels, and bits per sample.
|
||||
*/
|
||||
typedef struct guac_pcm_format {
|
||||
typedef struct guac_rdpsnd_pcm_format {
|
||||
|
||||
/**
|
||||
* The sample rate of this PCM format.
|
||||
@ -61,26 +54,13 @@ typedef struct guac_pcm_format {
|
||||
*/
|
||||
int bps;
|
||||
|
||||
} guac_pcm_format;
|
||||
} guac_rdpsnd_pcm_format;
|
||||
|
||||
/**
|
||||
* Structure representing the current state of the Guacamole RDPSND plugin for
|
||||
* FreeRDP.
|
||||
*/
|
||||
typedef struct guac_rdpsndPlugin {
|
||||
|
||||
/**
|
||||
* The FreeRDP parts of this plugin. This absolutely MUST be first.
|
||||
* FreeRDP depends on accessing this structure as if it were an instance
|
||||
* of rdpSvcPlugin.
|
||||
*/
|
||||
rdpSvcPlugin plugin;
|
||||
|
||||
/**
|
||||
* The Guacamole client associated with the guac_audio_stream that this
|
||||
* plugin should use to stream received audio packets.
|
||||
*/
|
||||
guac_client* client;
|
||||
typedef struct guac_rdpsnd {
|
||||
|
||||
/**
|
||||
* The block number of the last SNDC_WAVE (WaveInfo) PDU received.
|
||||
@ -116,36 +96,45 @@ typedef struct guac_rdpsndPlugin {
|
||||
* exchange. All of these formats will be PCM, which is the only format
|
||||
* guaranteed to be supported (based on the official RDP documentation).
|
||||
*/
|
||||
guac_pcm_format formats[GUAC_RDP_MAX_FORMATS];
|
||||
guac_rdpsnd_pcm_format formats[GUAC_RDP_MAX_FORMATS];
|
||||
|
||||
/**
|
||||
* The total number of formats.
|
||||
*/
|
||||
int format_count;
|
||||
|
||||
} guac_rdpsndPlugin;
|
||||
} guac_rdpsnd;
|
||||
|
||||
/**
|
||||
* Handler called when this plugin is loaded by FreeRDP.
|
||||
* Initializes audio output support for RDP and handling of the RDPSND channel.
|
||||
* If failures occur, messages noting the specifics of those failures will be
|
||||
* logged, and the RDP side of audio output support will not be functional.
|
||||
*
|
||||
* This MUST be called within the PreConnect callback of the freerdp instance
|
||||
* for RDPSND support to be loaded.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the FreeRDP side of the RDP connection.
|
||||
*/
|
||||
void guac_rdpsnd_process_connect(rdpSvcPlugin* plugin);
|
||||
void guac_rdpsnd_load_plugin(rdpContext* context);
|
||||
|
||||
/**
|
||||
* Handler called when this plugin receives data along its designated channel.
|
||||
* Handler which is invoked when the RDPSND channel is connected to the RDP
|
||||
* server.
|
||||
*/
|
||||
void guac_rdpsnd_process_receive(rdpSvcPlugin* plugin,
|
||||
wStream* input_stream);
|
||||
guac_rdp_common_svc_connect_handler guac_rdpsnd_process_connect;
|
||||
|
||||
/**
|
||||
* Handler called when this plugin is being unloaded.
|
||||
* Handler which is invoked when the RDPSND channel has received data from the
|
||||
* RDP server.
|
||||
*/
|
||||
void guac_rdpsnd_process_terminate(rdpSvcPlugin* plugin);
|
||||
guac_rdp_common_svc_receive_handler guac_rdpsnd_process_receive;
|
||||
|
||||
/**
|
||||
* Handler called when this plugin receives an event. For the sake of RDPSND,
|
||||
* all events will be ignored and simply free'd.
|
||||
* Handler which is invoked when the RDPSND channel has disconnected and is
|
||||
* about to be freed.
|
||||
*/
|
||||
void guac_rdpsnd_process_event(rdpSvcPlugin* plugin, wMessage* event);
|
||||
guac_rdp_common_svc_terminate_handler guac_rdpsnd_process_terminate;
|
||||
|
||||
#endif
|
||||
|
@ -17,14 +17,16 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "audio_input.h"
|
||||
#include "common/recording.h"
|
||||
#include "client.h"
|
||||
#include "channels/audio-input/audio-buffer.h"
|
||||
#include "channels/cliprdr.h"
|
||||
#include "channels/disp.h"
|
||||
#include "common/recording.h"
|
||||
#include "config.h"
|
||||
#include "fs.h"
|
||||
#include "log.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_disp.h"
|
||||
#include "rdp_fs.h"
|
||||
#include "settings.h"
|
||||
#include "user.h"
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
@ -33,26 +35,11 @@
|
||||
#include "common-ssh/user.h"
|
||||
#endif
|
||||
|
||||
#include <freerdp/cache/cache.h>
|
||||
#include <freerdp/channels/channels.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <guacamole/audio.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/socket.h>
|
||||
|
||||
#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
|
||||
#include <freerdp/client/cliprdr.h>
|
||||
#else
|
||||
#include "compat/client-cliprdr.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_CLIENT_CHANNELS_H
|
||||
#include <freerdp/client/channels.h>
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
|
||||
@ -64,19 +51,19 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
client->data = rdp_client;
|
||||
|
||||
/* Init clipboard */
|
||||
rdp_client->clipboard = guac_common_clipboard_alloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
|
||||
rdp_client->clipboard = guac_rdp_clipboard_alloc(client);
|
||||
|
||||
/* Init display update module */
|
||||
rdp_client->disp = guac_rdp_disp_alloc();
|
||||
|
||||
/* Redirect FreeRDP log messages to guac_client_log() */
|
||||
guac_rdp_redirect_wlog(client);
|
||||
|
||||
/* Recursive attribute for locks */
|
||||
pthread_mutexattr_init(&(rdp_client->attributes));
|
||||
pthread_mutexattr_settype(&(rdp_client->attributes),
|
||||
PTHREAD_MUTEX_RECURSIVE);
|
||||
|
||||
/* Init RDP lock */
|
||||
pthread_mutex_init(&(rdp_client->rdp_lock), &(rdp_client->attributes));
|
||||
|
||||
/* Set handlers */
|
||||
client->join_handler = guac_rdp_user_join_handler;
|
||||
client->free_handler = guac_rdp_client_free_handler;
|
||||
@ -100,6 +87,9 @@ int guac_rdp_client_free_handler(guac_client* client) {
|
||||
if (rdp_client->settings != NULL)
|
||||
guac_rdp_settings_free(rdp_client->settings);
|
||||
|
||||
/* Clean up clipboard */
|
||||
guac_rdp_clipboard_free(rdp_client->clipboard);
|
||||
|
||||
/* Free display update module */
|
||||
guac_rdp_disp_free(rdp_client->disp);
|
||||
|
||||
@ -136,7 +126,6 @@ int guac_rdp_client_free_handler(guac_client* client) {
|
||||
guac_rdp_audio_buffer_free(rdp_client->audio_input);
|
||||
|
||||
/* Free client data */
|
||||
guac_common_clipboard_free(rdp_client->clipboard);
|
||||
free(rdp_client);
|
||||
|
||||
return 0;
|
||||
|
@ -20,8 +20,6 @@
|
||||
#ifndef GUAC_RDP_CLIENT_H
|
||||
#define GUAC_RDP_CLIENT_H
|
||||
|
||||
#include "config.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
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/wtypes.h>
|
||||
#else
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the FreeRDP pixel format ID corresponding to the 32-bit RGB format
|
||||
* used by the Cairo library's image surfaces. Cairo handles colors in terms of
|
||||
* integers in native endianness, with CAIRO_FORMAT_ARGB32 representing a color
|
||||
* format where the alpha channel is stored in the most significant byte,
|
||||
* followed by red, green, and blue. FreeRDP handles colors in terms of
|
||||
* absolute byte order, with PIXEL_FORMAT_ARGB32 representing a color format
|
||||
* where the alpha channel is in byte 0, followed by red at byte 1, etc.
|
||||
*
|
||||
* @param alpha
|
||||
* TRUE if the returned FreeRDP pixel format should correspond to Cairo's
|
||||
* CAIRO_FORMAT_ARGB32, FALSE if the returned format should correspond to
|
||||
* Cairo's CAIRO_FORMAT_RGB24.
|
||||
*
|
||||
* @return
|
||||
* The FreeRDP pixel format ID that corresponds to the 32-bit RGB format
|
||||
* used by the Cairo library.
|
||||
*/
|
||||
UINT32 guac_rdp_get_native_pixel_format(BOOL alpha);
|
||||
|
||||
/**
|
||||
* Converts the given color to ARGB32. The color given may be an index
|
@ -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.
|
||||
*/
|
||||
|
||||
#include "config.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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "error.h"
|
||||
#include "rdp.h"
|
||||
|
||||
|
@ -17,11 +17,19 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "fs.h"
|
||||
#include "download.h"
|
||||
#include "upload.h"
|
||||
|
||||
#include "rdp_fs.h"
|
||||
#include "rdp_status.h"
|
||||
#include "rdp_stream.h"
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/object.h>
|
||||
#include <guacamole/pool.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/string.h>
|
||||
#include <guacamole/user.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/nt.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
@ -32,16 +40,8 @@
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/object.h>
|
||||
#include <guacamole/pool.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/string.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path,
|
||||
int create_drive_path) {
|
||||
|
||||
@ -244,16 +244,16 @@ int guac_rdp_fs_open(guac_rdp_fs* fs, const char* path,
|
||||
}
|
||||
|
||||
/* Translate access into flags */
|
||||
if (access & ACCESS_GENERIC_ALL)
|
||||
if (access & GENERIC_ALL)
|
||||
flags = O_RDWR;
|
||||
else if ((access & ( ACCESS_GENERIC_WRITE
|
||||
| ACCESS_FILE_WRITE_DATA
|
||||
| ACCESS_FILE_APPEND_DATA))
|
||||
&& (access & (ACCESS_GENERIC_READ | ACCESS_FILE_READ_DATA)))
|
||||
else if ((access & ( GENERIC_WRITE
|
||||
| FILE_WRITE_DATA
|
||||
| FILE_APPEND_DATA))
|
||||
&& (access & (GENERIC_READ | FILE_READ_DATA)))
|
||||
flags = O_RDWR;
|
||||
else if (access & ( ACCESS_GENERIC_WRITE
|
||||
| ACCESS_FILE_WRITE_DATA
|
||||
| ACCESS_FILE_APPEND_DATA))
|
||||
else if (access & ( GENERIC_WRITE
|
||||
| FILE_WRITE_DATA
|
||||
| FILE_APPEND_DATA))
|
||||
flags = O_WRONLY;
|
||||
else
|
||||
flags = O_RDONLY;
|
||||
@ -279,32 +279,32 @@ int guac_rdp_fs_open(guac_rdp_fs* fs, const char* path,
|
||||
switch (create_disposition) {
|
||||
|
||||
/* Create if not exist, fail otherwise */
|
||||
case DISP_FILE_CREATE:
|
||||
case FILE_CREATE:
|
||||
flags |= O_CREAT | O_EXCL;
|
||||
break;
|
||||
|
||||
/* Open file if exists and do not overwrite, fail otherwise */
|
||||
case DISP_FILE_OPEN:
|
||||
case FILE_OPEN:
|
||||
/* No flag necessary - default functionality of open */
|
||||
break;
|
||||
|
||||
/* Open if exists, create otherwise */
|
||||
case DISP_FILE_OPEN_IF:
|
||||
case FILE_OPEN_IF:
|
||||
flags |= O_CREAT;
|
||||
break;
|
||||
|
||||
/* Overwrite if exists, fail otherwise */
|
||||
case DISP_FILE_OVERWRITE:
|
||||
case FILE_OVERWRITE:
|
||||
flags |= O_TRUNC;
|
||||
break;
|
||||
|
||||
/* Overwrite if exists, create otherwise */
|
||||
case DISP_FILE_OVERWRITE_IF:
|
||||
case FILE_OVERWRITE_IF:
|
||||
flags |= O_CREAT | O_TRUNC;
|
||||
break;
|
||||
|
||||
/* Supersede (replace) if exists, otherwise create */
|
||||
case DISP_FILE_SUPERSEDE:
|
||||
case FILE_SUPERSEDE:
|
||||
unlink(real_path);
|
||||
flags |= O_CREAT | O_TRUNC;
|
||||
break;
|
||||
@ -605,6 +605,21 @@ const char* guac_rdp_fs_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 path_depth = 0;
|
@ -17,25 +17,24 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GUAC_RDP_FS_H
|
||||
#define __GUAC_RDP_FS_H
|
||||
#ifndef GUAC_RDP_FS_H
|
||||
#define GUAC_RDP_FS_H
|
||||
|
||||
/**
|
||||
* Functions and macros specific to filesystem handling and initialization
|
||||
* independent of RDP. The functions here may deal with the filesystem device
|
||||
* independent of RDP. The functions here may deal with the filesystem device
|
||||
* directly, but their semantics must not deal with RDP protocol messaging.
|
||||
* Functions here represent a virtual Windows-style filesystem on top of UNIX
|
||||
* system calls and structures, using the guac_rdp_fs structure as a home
|
||||
* for common data.
|
||||
*
|
||||
* @file rdp_fs.h
|
||||
* @file fs.h
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/object.h>
|
||||
#include <guacamole/pool.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdint.h>
|
||||
@ -109,73 +108,6 @@
|
||||
*/
|
||||
#define GUAC_RDP_FS_ENOTSUP -10
|
||||
|
||||
/*
|
||||
* Access constants.
|
||||
*/
|
||||
#define ACCESS_GENERIC_READ 0x80000000
|
||||
#define ACCESS_GENERIC_WRITE 0x40000000
|
||||
#define ACCESS_GENERIC_ALL 0x10000000
|
||||
#define ACCESS_FILE_READ_DATA 0x00000001
|
||||
#define ACCESS_FILE_WRITE_DATA 0x00000002
|
||||
#define ACCESS_FILE_APPEND_DATA 0x00000004
|
||||
#define ACCESS_DELETE 0x00010000
|
||||
|
||||
/*
|
||||
* Create disposition constants.
|
||||
*/
|
||||
|
||||
#define DISP_FILE_SUPERSEDE 0x00000000
|
||||
#define DISP_FILE_OPEN 0x00000001
|
||||
#define DISP_FILE_CREATE 0x00000002
|
||||
#define DISP_FILE_OPEN_IF 0x00000003
|
||||
#define DISP_FILE_OVERWRITE 0x00000004
|
||||
#define DISP_FILE_OVERWRITE_IF 0x00000005
|
||||
|
||||
/*
|
||||
* Information constants.
|
||||
* FreeRDP 1.1+ already defines those constants
|
||||
*/
|
||||
#ifdef LEGACY_FREERDP
|
||||
|
||||
#define FILE_SUPERSEDED 0x00000000
|
||||
#define FILE_OPENED 0x00000001
|
||||
#define FILE_OVERWRITTEN 0x00000003
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* File attributes.
|
||||
*/
|
||||
|
||||
#define FILE_ATTRIBUTE_READONLY 0x00000001
|
||||
#define FILE_ATTRIBUTE_HIDDEN 0x00000002
|
||||
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
|
||||
#define FILE_ATTRIBUTE_ARCHIVE 0x00000020
|
||||
#define FILE_ATTRIBUTE_NORMAL 0x00000080
|
||||
|
||||
/*
|
||||
* Filesystem attributes.
|
||||
*/
|
||||
|
||||
#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
|
||||
#define FILE_CASE_PRESERVED_NAMES 0x00000002
|
||||
#define FILE_UNICODE_ON_DISK 0x00000004
|
||||
|
||||
/*
|
||||
* File create options.
|
||||
*/
|
||||
|
||||
#define FILE_DIRECTORY_FILE 0x00000001
|
||||
#define FILE_NON_DIRECTORY_FILE 0x00000040
|
||||
|
||||
/*
|
||||
* File device types.
|
||||
*/
|
||||
|
||||
#define FILE_DEVICE_DISK 0x00000007
|
||||
|
||||
#define SEC_TO_UNIX_EPOCH 11644473600
|
||||
|
||||
/**
|
||||
* Converts a Windows timestamp (100 nanosecond intervals since Jan 1, 1601
|
||||
* UTC) to UNIX timestamp (seconds since Jan 1, 1970 UTC).
|
||||
@ -438,10 +370,10 @@ int guac_rdp_fs_get_status(int err);
|
||||
* The absolute path to the file within the simulated filesystem.
|
||||
*
|
||||
* @param access
|
||||
* A bitwise-OR of various RDPDR access flags, such as ACCESS_GENERIC_ALL
|
||||
* or ACCESS_GENERIC_WRITE. This value will ultimately be translated to a
|
||||
* standard O_RDWR, O_WRONLY, etc. value when opening the real file on the
|
||||
* local filesystem.
|
||||
* A bitwise-OR of various RDPDR access flags, such as GENERIC_ALL or
|
||||
* GENERIC_WRITE. This value will ultimately be translated to a standard
|
||||
* O_RDWR, O_WRONLY, etc. value when opening the real file on the local
|
||||
* filesystem.
|
||||
*
|
||||
* @param file_attributes
|
||||
* The attributes to apply to the file, if created. This parameter is
|
||||
@ -449,9 +381,9 @@ int guac_rdp_fs_get_status(int err);
|
||||
*
|
||||
* @param create_disposition
|
||||
* Any one of several RDPDR file creation dispositions, such as
|
||||
* DISP_FILE_CREATE, DISP_FILE_OPEN_IF, etc. The creation disposition
|
||||
* dictates whether a new file should be created, whether the file can
|
||||
* already exist, whether existing contents should be truncated, etc.
|
||||
* FILE_CREATE, FILE_OPEN_IF, etc. The creation disposition dictates
|
||||
* whether a new file should be created, whether the file can already
|
||||
* exist, whether existing contents should be truncated, etc.
|
||||
*
|
||||
* @param create_options
|
||||
* A bitwise-OR of various RDPDR options dictating how a file is to be
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* Given an arbitrary path, returns a pointer to the first character following
|
||||
* the last path separator in the path (the basename of the path). For example,
|
||||
* given "/foo/bar/baz" or "\foo\bar\baz", this function would return a pointer
|
||||
* to "baz".
|
||||
*
|
||||
* @param path
|
||||
* The path to determine the basename of.
|
||||
*
|
||||
* @return
|
||||
* A pointer to the first character of the basename within the path.
|
||||
*/
|
||||
const char* guac_rdp_fs_basename(const char* path);
|
||||
|
||||
/**
|
||||
* Given an arbitrary path, which may contain ".." and ".", creates an
|
||||
* absolute path which does NOT contain ".." or ".". The given path MUST
|
@ -17,26 +17,21 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "bitmap.h"
|
||||
#include "common/display.h"
|
||||
#include "common/surface.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_bitmap.h"
|
||||
#include "rdp_color.h"
|
||||
#include "rdp_settings.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/graphics.h>
|
||||
#include <freerdp/primary.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/wtypes.h>
|
||||
#else
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stddef.h>
|
||||
|
||||
guac_transfer_function guac_rdp_rop3_transfer_function(guac_client* client,
|
||||
@ -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_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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) {
|
||||
|
||||
/*
|
||||
* Note that this is not a full implementation of PATBLT. This is a
|
||||
* fallback implementation which only renders a solid block of background
|
||||
* color using the specified ROP3 operation, ignoring whatever brush
|
||||
* was actually specified.
|
||||
*
|
||||
* As libguac-client-rdp explicitly tells the server not to send PATBLT,
|
||||
* well-behaved RDP servers will not use this operation at all, while
|
||||
* others will at least have a fallback.
|
||||
*/
|
||||
|
||||
/* Get client and current layer */
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_common_surface* current_surface =
|
||||
((guac_rdp_client*) client->data)->current_surface;
|
||||
|
||||
int x = patblt->nLeftRect;
|
||||
int y = patblt->nTopRect;
|
||||
int w = patblt->nWidth;
|
||||
int h = patblt->nHeight;
|
||||
|
||||
/*
|
||||
* Warn that rendering is a fallback, as the server should not be sending
|
||||
* this order.
|
||||
*/
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Using fallback PATBLT (server is ignoring "
|
||||
"negotiated client capabilities)");
|
||||
|
||||
/* Render rectangle based on ROP */
|
||||
switch (patblt->bRop) {
|
||||
|
||||
/* If blackness, send black rectangle */
|
||||
case 0x00:
|
||||
guac_common_surface_set(current_surface, x, y, w, h,
|
||||
0x00, 0x00, 0x00, 0xFF);
|
||||
break;
|
||||
|
||||
/* If NOP, do nothing */
|
||||
case 0xAA:
|
||||
break;
|
||||
|
||||
/* If operation is just a copy, send foreground only */
|
||||
case 0xCC:
|
||||
case 0xF0:
|
||||
guac_common_surface_set(current_surface, x, y, w, h,
|
||||
(patblt->foreColor >> 16) & 0xFF,
|
||||
(patblt->foreColor >> 8 ) & 0xFF,
|
||||
(patblt->foreColor ) & 0xFF,
|
||||
0xFF);
|
||||
break;
|
||||
|
||||
/* If whiteness, send white rectangle */
|
||||
case 0xFF:
|
||||
guac_common_surface_set(current_surface, x, y, w, h,
|
||||
0xFF, 0xFF, 0xFF, 0xFF);
|
||||
break;
|
||||
|
||||
/* Otherwise, invert entire rect */
|
||||
default:
|
||||
guac_common_surface_transfer(current_surface, x, y, w, h,
|
||||
GUAC_TRANSFER_BINARY_NDEST, current_surface, x, y);
|
||||
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt) {
|
||||
BOOL guac_rdp_gdi_scrblt(rdpContext* context, const SCRBLT_ORDER* scrblt) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
|
||||
@ -229,9 +158,11 @@ void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt) {
|
||||
guac_common_surface_copy(rdp_client->display->default_surface,
|
||||
x_src, y_src, w, h, current_surface, x, y);
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
|
||||
BOOL guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
|
||||
@ -248,7 +179,7 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
|
||||
/* Make sure that the recieved bitmap is not NULL before processing */
|
||||
if (bitmap == NULL) {
|
||||
guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in memblt instruction.");
|
||||
return;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
switch (memblt->bRop) {
|
||||
@ -321,98 +252,11 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect) {
|
||||
|
||||
/* Get client data */
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
|
||||
UINT32 color = guac_rdp_convert_color(context, opaque_rect->color);
|
||||
|
||||
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
|
||||
|
||||
int x = opaque_rect->nLeftRect;
|
||||
int y = opaque_rect->nTopRect;
|
||||
int w = opaque_rect->nWidth;
|
||||
int h = opaque_rect->nHeight;
|
||||
|
||||
guac_common_surface_set(current_surface, x, y, w, h,
|
||||
(color >> 16) & 0xFF,
|
||||
(color >> 8 ) & 0xFF,
|
||||
(color ) & 0xFF,
|
||||
0xFF);
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the palette within a FreeRDP CLRCONV object using the new palette
|
||||
* entries provided by an RDP palette update.
|
||||
*
|
||||
* @param clrconv
|
||||
* The FreeRDP CLRCONV object to update.
|
||||
*
|
||||
* @param palette
|
||||
* An RDP palette update message containing the palette to store within the
|
||||
* given CLRCONV object.
|
||||
*/
|
||||
static void guac_rdp_update_clrconv(CLRCONV* clrconv,
|
||||
PALETTE_UPDATE* palette) {
|
||||
|
||||
clrconv->palette->count = palette->number;
|
||||
#ifdef LEGACY_RDPPALETTE
|
||||
clrconv->palette->entries = palette->entries;
|
||||
#else
|
||||
memcpy(clrconv->palette->entries, palette->entries,
|
||||
sizeof(palette->entries));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a raw ARGB32 palette using the new palette entries provided by an
|
||||
* RDP palette update.
|
||||
*
|
||||
* @param guac_palette
|
||||
* An array of 256 ARGB32 colors, with each entry corresponding to an
|
||||
* entry in the color palette.
|
||||
*
|
||||
* @param palette
|
||||
* An RDP palette update message containing the palette to store within the
|
||||
* given array of ARGB32 colors.
|
||||
*/
|
||||
static void guac_rdp_update_palette(UINT32* guac_palette,
|
||||
PALETTE_UPDATE* palette) {
|
||||
|
||||
PALETTE_ENTRY* entry = palette->entries;
|
||||
int i;
|
||||
|
||||
/* Copy each palette entry as ARGB32 */
|
||||
for (i=0; i < palette->number; i++) {
|
||||
|
||||
*guac_palette = 0xFF000000
|
||||
| (entry->red << 16)
|
||||
| (entry->green << 8)
|
||||
| entry->blue;
|
||||
|
||||
guac_palette++;
|
||||
entry++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette) {
|
||||
|
||||
CLRCONV* clrconv = ((rdp_freerdp_context*) context)->clrconv;
|
||||
UINT32* guac_palette = ((rdp_freerdp_context*) context)->palette;
|
||||
|
||||
/* Update internal palette representations */
|
||||
guac_rdp_update_clrconv(clrconv, palette);
|
||||
guac_rdp_update_palette(guac_palette, palette);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds) {
|
||||
BOOL guac_rdp_gdi_set_bounds(rdpContext* context, const rdpBounds* bounds) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
@ -428,13 +272,16 @@ void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds) {
|
||||
bounds->right - bounds->left + 1,
|
||||
bounds->bottom - bounds->top + 1);
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_gdi_end_paint(rdpContext* context) {
|
||||
BOOL guac_rdp_gdi_end_paint(rdpContext* context) {
|
||||
/* IGNORE */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void guac_rdp_gdi_desktop_resize(rdpContext* context) {
|
||||
BOOL guac_rdp_gdi_desktop_resize(rdpContext* context) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
@ -449,6 +296,8 @@ void guac_rdp_gdi_desktop_resize(rdpContext* context) {
|
||||
guac_rdp_get_width(context->instance),
|
||||
guac_rdp_get_height(context->instance));
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,8 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _GUAC_RDP_RDP_GDI_H
|
||||
#define _GUAC_RDP_RDP_GDI_H
|
||||
#ifndef GUAC_RDP_GDI_H
|
||||
#define GUAC_RDP_GDI_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@ -46,71 +45,58 @@ guac_composite_mode guac_rdp_rop3_transfer_function(guac_client* client,
|
||||
int rop3);
|
||||
|
||||
/**
|
||||
* Handler for RDP DSTBLT update.
|
||||
* Handler for the DstBlt Primary Drawing Order. A DstBlt Primary Drawing Order
|
||||
* paints a rectangle of image data using a raster operation which considers
|
||||
* the destination only. See:
|
||||
*
|
||||
* https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegdi/87ea30df-59d6-438e-a735-83f0225fbf91
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param dstblt
|
||||
* The DSTBLT update to handle.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt);
|
||||
BOOL guac_rdp_gdi_dstblt(rdpContext* context, const DSTBLT_ORDER* dstblt);
|
||||
|
||||
/**
|
||||
* Handler for RDP PATBLT update.
|
||||
* Handler for the ScrBlt Primary Drawing Order. A ScrBlt Primary Drawing Order
|
||||
* paints a rectangle of image data using a raster operation which considers
|
||||
* the source and destination. See:
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param patblt
|
||||
* The PATBLT update to handle.
|
||||
*/
|
||||
void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt);
|
||||
|
||||
/**
|
||||
* Handler for RDP SCRBLT update.
|
||||
* https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegdi/a4e322b0-cd64-4dfc-8e1a-f24dc0edc99d
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param scrblt
|
||||
* The SCRBLT update to handle.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt);
|
||||
BOOL guac_rdp_gdi_scrblt(rdpContext* context, const SCRBLT_ORDER* scrblt);
|
||||
|
||||
/**
|
||||
* Handler for RDP MEMBLT update.
|
||||
* Handler for the MemBlt Primary Drawing Order. A MemBlt Primary Drawing Order
|
||||
* paints a rectangle of cached image data from a cached surface to the screen
|
||||
* using a raster operation which considers the source and destination. See:
|
||||
*
|
||||
* https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegdi/84c2ec2f-f776-405b-9b48-6894a28b1b14
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param memblt
|
||||
* The MEMBLT update to handle.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt);
|
||||
|
||||
/**
|
||||
* Handler for RDP OPAQUE RECT update.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param opaque_rect
|
||||
* The OPAQUE RECT update to handle.
|
||||
*/
|
||||
void guac_rdp_gdi_opaquerect(rdpContext* context,
|
||||
OPAQUE_RECT_ORDER* opaque_rect);
|
||||
|
||||
/**
|
||||
* Handler called when the remote color palette is changing.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param palette
|
||||
* The PALETTE update containing the new palette.
|
||||
*/
|
||||
void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette);
|
||||
BOOL guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt);
|
||||
|
||||
/**
|
||||
* Handler called prior to calling the handlers for specific updates when
|
||||
@ -124,8 +110,11 @@ void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette);
|
||||
* @param bounds
|
||||
* The clipping rectangle to set, or NULL to remove any applied clipping
|
||||
* rectangle.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds);
|
||||
BOOL guac_rdp_gdi_set_bounds(rdpContext* context, const rdpBounds* bounds);
|
||||
|
||||
/**
|
||||
* Handler called when a paint operation is complete. We don't actually
|
||||
@ -133,8 +122,11 @@ void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds);
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_gdi_end_paint(rdpContext* context);
|
||||
BOOL guac_rdp_gdi_end_paint(rdpContext* context);
|
||||
|
||||
/**
|
||||
* Handler called when the desktop dimensions change, either from a
|
||||
@ -147,7 +139,10 @@ void guac_rdp_gdi_end_paint(rdpContext* context);
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_gdi_desktop_resize(rdpContext* context);
|
||||
BOOL guac_rdp_gdi_desktop_resize(rdpContext* context);
|
||||
|
||||
#endif
|
@ -17,25 +17,16 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "color.h"
|
||||
#include "common/surface.h"
|
||||
#include "config.h"
|
||||
#include "glyph.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_color.h"
|
||||
#include "rdp_glyph.h"
|
||||
#include "rdp_settings.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/wtypes.h>
|
||||
#else
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@ -44,7 +35,7 @@
|
||||
#define cairo_format_stride_for_width(format, width) (width*4)
|
||||
#endif
|
||||
|
||||
void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph) {
|
||||
BOOL guac_rdp_glyph_new(rdpContext* context, const rdpGlyph* glyph) {
|
||||
|
||||
int x, y, i;
|
||||
int stride;
|
||||
@ -95,9 +86,15 @@ void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph) {
|
||||
((guac_rdp_glyph*) glyph)->surface = cairo_image_surface_create_for_data(
|
||||
image_buffer, CAIRO_FORMAT_ARGB32, width, height, stride);
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_glyph_draw(rdpContext* context, rdpGlyph* glyph, int x, int y) {
|
||||
BOOL guac_rdp_glyph_draw(rdpContext* context, const rdpGlyph* glyph,
|
||||
GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
|
||||
GLYPH_CALLBACK_INT32 w, GLYPH_CALLBACK_INT32 h,
|
||||
GLYPH_CALLBACK_INT32 sx, GLYPH_CALLBACK_INT32 sy,
|
||||
BOOL redundant) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
@ -110,6 +107,8 @@ void guac_rdp_glyph_draw(rdpContext* context, rdpGlyph* glyph, int x, int y) {
|
||||
(fgcolor & 0x00FF00) >> 8,
|
||||
fgcolor & 0x0000FF);
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
free(image_buffer);
|
||||
|
||||
/* NOTE: FreeRDP-allocated memory for the rdpGlyph will NOT be
|
||||
* automatically released after this free handler is invoked, thus we must
|
||||
* do so manually here */
|
||||
|
||||
free(glyph->aj);
|
||||
free(glyph);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_glyph_begindraw(rdpContext* context,
|
||||
int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor) {
|
||||
BOOL guac_rdp_glyph_begindraw(rdpContext* context,
|
||||
GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
|
||||
GLYPH_CALLBACK_INT32 width, GLYPH_CALLBACK_INT32 height,
|
||||
UINT32 fgcolor, UINT32 bgcolor, BOOL redundant) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_client* rdp_client =
|
||||
(guac_rdp_client*) client->data;
|
||||
|
||||
/* Fill background with color if specified */
|
||||
if (width != 0 && height != 0) {
|
||||
if (width != 0 && height != 0 && !redundant) {
|
||||
|
||||
/* Convert background color */
|
||||
bgcolor = guac_rdp_convert_color(context, bgcolor);
|
||||
@ -148,10 +156,15 @@ void guac_rdp_glyph_begindraw(rdpContext* context,
|
||||
/* Convert foreground color */
|
||||
rdp_client->glyph_color = guac_rdp_convert_color(context, fgcolor);
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_glyph_enddraw(rdpContext* context,
|
||||
int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor) {
|
||||
BOOL guac_rdp_glyph_enddraw(rdpContext* context,
|
||||
GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
|
||||
GLYPH_CALLBACK_INT32 width, GLYPH_CALLBACK_INT32 height,
|
||||
UINT32 fgcolor, UINT32 bgcolor) {
|
||||
/* IGNORE */
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -17,19 +17,28 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _GUAC_RDP_RDP_GLYPH_H
|
||||
#define _GUAC_RDP_RDP_GLYPH_H
|
||||
#ifndef GUAC_RDP_GLYPH_H
|
||||
#define GUAC_RDP_GLYPH_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <freerdp/graphics.h>
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
#ifdef FREERDP_GLYPH_CALLBACKS_ACCEPT_INT32
|
||||
/**
|
||||
* FreeRDP 2.0.0-rc4 and newer requires INT32 for all integer arguments of
|
||||
* glyph callbacks.
|
||||
*/
|
||||
#define GLYPH_CALLBACK_INT32 INT32
|
||||
#else
|
||||
#include "compat/winpr-wtypes.h"
|
||||
/**
|
||||
* FreeRDP 2.0.0-rc3 and older requires UINT32 for all integer arguments of
|
||||
* glyph callbacks.
|
||||
*/
|
||||
#define GLYPH_CALLBACK_INT32 UINT32
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -58,8 +67,11 @@ typedef struct guac_rdp_glyph {
|
||||
*
|
||||
* @param glyph
|
||||
* The glyph to cache.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph);
|
||||
BOOL guac_rdp_glyph_new(rdpContext* context, const rdpGlyph* glyph);
|
||||
|
||||
/**
|
||||
* Draws a previously-cached glyph at the given coordinates within the current
|
||||
@ -76,8 +88,32 @@ void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph);
|
||||
*
|
||||
* @param y
|
||||
* The destination Y coordinate of the upper-left corner of the glyph.
|
||||
*
|
||||
* @param w
|
||||
* The width of the glyph being drawn.
|
||||
*
|
||||
* @param h
|
||||
* The height of the glyph being drawn.
|
||||
*
|
||||
* @param sx
|
||||
* The X coordinare of the upper-left corner of the glyph within the source
|
||||
* cache surface containing the glyph.
|
||||
*
|
||||
* @param sy
|
||||
* The Y coordinare of the upper-left corner of the glyph within the source
|
||||
* cache surface containing the glyph.
|
||||
*
|
||||
* @param redundant
|
||||
* Whether the background rectangle specified is redundant (transparent).
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_glyph_draw(rdpContext* context, rdpGlyph* glyph, int x, int y);
|
||||
BOOL guac_rdp_glyph_draw(rdpContext* context, const rdpGlyph* glyph,
|
||||
GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
|
||||
GLYPH_CALLBACK_INT32 w, GLYPH_CALLBACK_INT32 h,
|
||||
GLYPH_CALLBACK_INT32 sx, GLYPH_CALLBACK_INT32 sy,
|
||||
BOOL redundant);
|
||||
|
||||
/**
|
||||
* Frees any Guacamole-specific data associated with the given glyph, such that
|
||||
@ -125,9 +161,17 @@ void guac_rdp_glyph_free(rdpContext* context, rdpGlyph* glyph);
|
||||
* colorspace of the RDP session, and may even be a palette index, and must
|
||||
* be translated via guac_rdp_convert_color(). If the background is
|
||||
* transparent, this value is undefined.
|
||||
*
|
||||
* @param redundant
|
||||
* Whether the background rectangle specified is redundant (transparent).
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_glyph_begindraw(rdpContext* context,
|
||||
int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor);
|
||||
BOOL guac_rdp_glyph_begindraw(rdpContext* context,
|
||||
GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
|
||||
GLYPH_CALLBACK_INT32 width, GLYPH_CALLBACK_INT32 height,
|
||||
UINT32 fgcolor, UINT32 bgcolor, BOOL redundant);
|
||||
|
||||
/**
|
||||
* Called immediately after rendering a series of glyphs. Unlike
|
||||
@ -162,8 +206,13 @@ void guac_rdp_glyph_begindraw(rdpContext* context,
|
||||
* colorspace of the RDP session, and may even be a palette index, and must
|
||||
* be translated via guac_rdp_convert_color(). If the background is
|
||||
* transparent, this value is undefined.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_glyph_enddraw(rdpContext* context,
|
||||
int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor);
|
||||
BOOL guac_rdp_glyph_enddraw(rdpContext* context,
|
||||
GLYPH_CALLBACK_INT32 x, GLYPH_CALLBACK_INT32 y,
|
||||
GLYPH_CALLBACK_INT32 width, GLYPH_CALLBACK_INT32 height,
|
||||
UINT32 fgcolor, UINT32 bgcolor);
|
||||
|
||||
#endif
|
@ -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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "channels/disp.h"
|
||||
#include "common/cursor.h"
|
||||
#include "common/display.h"
|
||||
#include "common/recording.h"
|
||||
#include "input.h"
|
||||
#include "keyboard.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_disp.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/input.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
|
||||
@ -38,14 +38,10 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* Skip if not yet connected */
|
||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||
if (rdp_inst == NULL) {
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
if (rdp_inst == NULL)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Store current mouse location/state */
|
||||
guac_common_cursor_update(rdp_client->display->cursor, user, x, y, mask);
|
||||
@ -118,8 +114,6 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
|
||||
rdp_client->mouse_button_mask = mask;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
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;
|
||||
|
||||
/* Send display update */
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
guac_rdp_disp_set_size(rdp_client->disp, settings, rdp_inst, width, height);
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -17,19 +17,15 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "decompose.h"
|
||||
#include "keyboard.h"
|
||||
#include "keymap.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_keymap.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/input.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
@ -102,21 +98,15 @@ static void guac_rdp_send_key_event(guac_rdp_client* rdp_client,
|
||||
else
|
||||
pressed_flags = KBD_FLAGS_RELEASE;
|
||||
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* Skip if not yet connected */
|
||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||
if (rdp_inst == NULL) {
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
if (rdp_inst == NULL)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send actual key */
|
||||
rdp_inst->input->KeyboardEvent(rdp_inst->input,
|
||||
flags | pressed_flags, scancode);
|
||||
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -136,22 +126,16 @@ static void guac_rdp_send_key_event(guac_rdp_client* rdp_client,
|
||||
static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client,
|
||||
int codepoint) {
|
||||
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* Skip if not yet connected */
|
||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||
if (rdp_inst == NULL) {
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
if (rdp_inst == NULL)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send Unicode event */
|
||||
rdp_inst->input->UnicodeKeyboardEvent(
|
||||
rdp_inst->input,
|
||||
0, codepoint);
|
||||
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,20 +155,14 @@ static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client,
|
||||
static void guac_rdp_send_synchronize_event(guac_rdp_client* rdp_client,
|
||||
int flags) {
|
||||
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* Skip if not yet connected */
|
||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||
if (rdp_inst == NULL) {
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
if (rdp_inst == NULL)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Synchronize lock key states */
|
||||
rdp_inst->input->SynchronizeEvent(rdp_inst->input, flags);
|
||||
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,7 @@
|
||||
#ifndef GUAC_RDP_KEYBOARD_H
|
||||
#define GUAC_RDP_KEYBOARD_H
|
||||
|
||||
#include "rdp_keymap.h"
|
||||
#include "keymap.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
@ -94,7 +94,7 @@ typedef struct guac_rdp_keyboard {
|
||||
|
||||
/**
|
||||
* The local state of all keys, as well as the necessary information to
|
||||
* translate received keysyms into scancodes or sequences of scancodes for
|
||||
* translate received keysyms into scancodes or sequences of scancodes for
|
||||
* RDP. The state of each key is updated based on received Guacamole key
|
||||
* events, while the information describing the behavior and scancode
|
||||
* mapping of each key is populated based on an associated keymap.
|
||||
@ -180,7 +180,7 @@ int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard,
|
||||
/**
|
||||
* For every keysym in the given NULL-terminated array of keysyms, send the RDP
|
||||
* key events required to update the remote state of those keys as specified,
|
||||
* depending on the current local state of those keysyms. For each key in the
|
||||
* depending on the current local state of those keysyms. For each key in the
|
||||
* "from" state, that key will be updated to the "to" state. The locally-stored
|
||||
* state of each key is remains untouched.
|
||||
*
|
||||
|
@ -17,9 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "rdp_keymap.h"
|
||||
#include "keymap.h"
|
||||
|
||||
#include <string.h>
|
||||
|
@ -17,17 +17,10 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_KEYMAP_H
|
||||
#define GUAC_RDP_KEYMAP_H
|
||||
|
||||
#ifndef _GUAC_RDP_RDP_KEYMAP_H
|
||||
#define _GUAC_RDP_RDP_KEYMAP_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/wtypes.h>
|
||||
#else
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Represents a keysym-to-scancode mapping for RDP, with extra information
|
@ -43,14 +43,9 @@ my @keymaps = ();
|
||||
open OUTPUT, ">", "_generated_keymaps.c";
|
||||
print OUTPUT
|
||||
'#include "config.h"' . "\n"
|
||||
. '#include "rdp_keymap.h"' . "\n"
|
||||
. '#include "keymap.h"' . "\n"
|
||||
. '#include <freerdp/input.h>' . "\n"
|
||||
. "\n"
|
||||
. '#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H' . "\n"
|
||||
. '#include <freerdp/locale/keyboard.h>' . "\n"
|
||||
. '#else' . "\n"
|
||||
. '#include <freerdp/kbd/layouts.h>' . "\n"
|
||||
. '#endif' . "\n"
|
||||
. "\n"
|
||||
. '#include <stddef.h>' . "\n"
|
||||
. "\n";
|
||||
|
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.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_LOG_H
|
||||
#define GUAC_RDP_LOG_H
|
||||
|
||||
#ifndef __GUAC_WINPR_WTYPES_COMPAT_H
|
||||
#define __GUAC_WINPR_WTYPES_COMPAT_H
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <freerdp/types.h>
|
||||
|
||||
typedef uint8 BYTE;
|
||||
typedef uint8 UINT8;
|
||||
typedef uint16 UINT16;
|
||||
typedef uint32 UINT32;
|
||||
typedef uint64 UINT64;
|
||||
typedef boolean BOOL;
|
||||
|
||||
#define TRUE true
|
||||
#define FALSE false
|
||||
/**
|
||||
* Redirects the core FreeRDP logging facility, wLog, such that it logs all
|
||||
* messages at the debug level using guac_client_log().
|
||||
*
|
||||
* @param client
|
||||
* The guac_client that should receive all log messages.
|
||||
*/
|
||||
void guac_rdp_redirect_wlog(guac_client* client);
|
||||
|
||||
#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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ai_messages.h"
|
||||
#include "audio_input.h"
|
||||
#include "channels/audio-input/audio-buffer.h"
|
||||
#include "plugins/guacai/guacai-messages.h"
|
||||
#include "rdp.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/constants.h>
|
||||
#include <freerdp/dvc.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/stream.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Reads AUDIO_FORMAT data from the given stream into the given struct.
|
||||
@ -113,6 +104,19 @@ 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,
|
||||
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;
|
||||
|
@ -17,19 +17,15 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_AI_MESSAGES_H
|
||||
#define GUAC_RDP_AI_MESSAGES_H
|
||||
#ifndef GUAC_RDP_PLUGINS_GUACAI_MESSAGES_H
|
||||
#define GUAC_RDP_PLUGINS_GUACAI_MESSAGES_H
|
||||
|
||||
#include "config.h"
|
||||
#include "channels/audio-input/audio-buffer.h"
|
||||
|
||||
#include <freerdp/dvc.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/stream.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#endif
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
/**
|
||||
* The format tag associated with raw wave audio (WAVE_FORMAT_PCM). This format
|
||||
@ -212,5 +208,14 @@ void guac_rdp_ai_process_open(guac_client* client,
|
||||
void guac_rdp_ai_process_formatchange(guac_client* client,
|
||||
IWTSVirtualChannel* channel, wStream* stream);
|
||||
|
||||
/**
|
||||
* Audio buffer flush handler which sends audio data along the active audio
|
||||
* input channel using a Data Incoming PDU and Data PDU. The arbitrary data
|
||||
* provided to the handler by the audio buffer implementation is in this case
|
||||
* the IWTSVirtualChannel structure representing the active audio input
|
||||
* channel.
|
||||
*/
|
||||
guac_rdp_audio_buffer_flush_handler guac_rdp_ai_flush_packet;
|
||||
|
||||
#endif
|
||||
|
@ -17,27 +17,20 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ai_messages.h"
|
||||
#include "ai_service.h"
|
||||
#include "audio_input.h"
|
||||
#include "ptr_string.h"
|
||||
#include "channels/audio-input/audio-buffer.h"
|
||||
#include "plugins/guacai/guacai.h"
|
||||
#include "plugins/guacai/guacai-messages.h"
|
||||
#include "plugins/ptr-string.h"
|
||||
#include "rdp.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/constants.h>
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/settings.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/stream.h>
|
||||
#else
|
||||
#include "compat/winpr-stream.h"
|
||||
#endif
|
||||
#include <winpr/wtsapi.h>
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Handles the given data received along the AUDIO_INPUT channel of the RDP
|
||||
@ -95,43 +88,9 @@ 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
|
||||
* AUDIO_INPUT plugin. This callback is specific to FreeRDP 1.1 and older.
|
||||
*
|
||||
* @param channel_callback
|
||||
* The IWTSVirtualChannelCallback structure to which this callback was
|
||||
* originally assigned.
|
||||
*
|
||||
* @param size
|
||||
* The number of bytes received.
|
||||
*
|
||||
* @param buffer
|
||||
* A buffer containing all bytes received.
|
||||
*
|
||||
* @return
|
||||
* Always zero.
|
||||
*/
|
||||
static int guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
|
||||
UINT32 size, BYTE* buffer) {
|
||||
|
||||
guac_rdp_ai_channel_callback* ai_channel_callback =
|
||||
(guac_rdp_ai_channel_callback*) channel_callback;
|
||||
IWTSVirtualChannel* channel = ai_channel_callback->channel;
|
||||
|
||||
/* Invoke generalized (API-independent) data handler */
|
||||
wStream* stream = Stream_New(buffer, size);
|
||||
guac_rdp_ai_handle_data(ai_channel_callback->client, channel, stream);
|
||||
Stream_Free(stream, FALSE);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* Callback which is invoked when data is received along a connection to the
|
||||
* AUDIO_INPUT plugin. This callback is specific to FreeRDP 1.2 and newer.
|
||||
* AUDIO_INPUT plugin.
|
||||
*
|
||||
* @param channel_callback
|
||||
* The IWTSVirtualChannelCallback structure to which this callback was
|
||||
@ -143,7 +102,7 @@ static int guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
|
||||
* @return
|
||||
* Always zero.
|
||||
*/
|
||||
static int guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
|
||||
static UINT guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
|
||||
wStream* stream) {
|
||||
|
||||
guac_rdp_ai_channel_callback* ai_channel_callback =
|
||||
@ -153,10 +112,9 @@ static int guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
|
||||
/* Invoke generalized (API-independent) data handler */
|
||||
guac_rdp_ai_handle_data(ai_channel_callback->client, channel, stream);
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Callback which is invoked when a connection to the AUDIO_INPUT plugin is
|
||||
@ -169,7 +127,7 @@ static int guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
|
||||
* @return
|
||||
* Always zero.
|
||||
*/
|
||||
static int guac_rdp_ai_close(IWTSVirtualChannelCallback* channel_callback) {
|
||||
static UINT guac_rdp_ai_close(IWTSVirtualChannelCallback* channel_callback) {
|
||||
|
||||
guac_rdp_ai_channel_callback* ai_channel_callback =
|
||||
(guac_rdp_ai_channel_callback*) channel_callback;
|
||||
@ -184,7 +142,7 @@ static int guac_rdp_ai_close(IWTSVirtualChannelCallback* channel_callback) {
|
||||
|
||||
guac_rdp_audio_buffer_end(audio_buffer);
|
||||
free(ai_channel_callback);
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
}
|
||||
|
||||
@ -219,7 +177,7 @@ static int guac_rdp_ai_close(IWTSVirtualChannelCallback* channel_callback) {
|
||||
* @return
|
||||
* Always zero.
|
||||
*/
|
||||
static int guac_rdp_ai_new_connection(
|
||||
static UINT guac_rdp_ai_new_connection(
|
||||
IWTSListenerCallback* listener_callback, IWTSVirtualChannel* channel,
|
||||
BYTE* data, int* accept,
|
||||
IWTSVirtualChannelCallback** channel_callback) {
|
||||
@ -243,7 +201,8 @@ static int guac_rdp_ai_new_connection(
|
||||
|
||||
/* Return callback through pointer */
|
||||
*channel_callback = (IWTSVirtualChannelCallback*) ai_channel_callback;
|
||||
return 0;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
}
|
||||
|
||||
@ -261,7 +220,7 @@ static int guac_rdp_ai_new_connection(
|
||||
* @return
|
||||
* Always zero.
|
||||
*/
|
||||
static int guac_rdp_ai_initialize(IWTSPlugin* plugin,
|
||||
static UINT guac_rdp_ai_initialize(IWTSPlugin* plugin,
|
||||
IWTSVirtualChannelManager* manager) {
|
||||
|
||||
/* Allocate new listener callback */
|
||||
@ -281,7 +240,7 @@ static int guac_rdp_ai_initialize(IWTSPlugin* plugin,
|
||||
manager->CreateListener(manager, "AUDIO_INPUT", 0,
|
||||
(IWTSListenerCallback*) ai_listener_callback, NULL);
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
}
|
||||
|
||||
@ -295,7 +254,7 @@ static int guac_rdp_ai_initialize(IWTSPlugin* plugin,
|
||||
* @return
|
||||
* Always zero.
|
||||
*/
|
||||
static int guac_rdp_ai_terminated(IWTSPlugin* plugin) {
|
||||
static UINT guac_rdp_ai_terminated(IWTSPlugin* plugin) {
|
||||
|
||||
guac_rdp_ai_plugin* ai_plugin = (guac_rdp_ai_plugin*) plugin;
|
||||
guac_client* client = ai_plugin->client;
|
||||
@ -305,7 +264,7 @@ static int guac_rdp_ai_terminated(IWTSPlugin* plugin) {
|
||||
free(ai_plugin);
|
||||
|
||||
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) {
|
||||
|
||||
/* Pull guac_client from arguments */
|
||||
#ifdef HAVE_ADDIN_ARGV
|
||||
ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);
|
||||
guac_client* client = (guac_client*) guac_rdp_string_to_ptr(args->argv[1]);
|
||||
#else
|
||||
RDP_PLUGIN_DATA* data = pEntryPoints->GetPluginData(pEntryPoints);
|
||||
guac_client* client = (guac_client*) guac_rdp_string_to_ptr(data->data[1]);
|
||||
#endif
|
||||
|
||||
/* Pull previously-allocated plugin */
|
||||
guac_rdp_ai_plugin* ai_plugin = (guac_rdp_ai_plugin*)
|
||||
@ -343,7 +297,7 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) {
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "AUDIO_INPUT plugin loaded.");
|
||||
}
|
||||
|
||||
return 1;
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
}
|
||||
|
@ -17,14 +17,12 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_AI_SERVICE_H
|
||||
#define GUAC_RDP_AI_SERVICE_H
|
||||
#ifndef GUAC_RDP_PLUGINS_GUACAI_H
|
||||
#define GUAC_RDP_PLUGINS_GUACAI_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/constants.h>
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
/**
|
@ -17,13 +17,9 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ptr_string.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include "plugins/ptr-string.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void guac_rdp_ptr_to_string(void* data, char* str) {
|
||||
|
@ -17,10 +17,8 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_PTR_STRING_H
|
||||
#define GUAC_RDP_PTR_STRING_H
|
||||
|
||||
#include "config.h"
|
||||
#ifndef GUAC_RDP_PLUGINS_PTR_STRING_H
|
||||
#define GUAC_RDP_PLUGINS_PTR_STRING_H
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
@ -17,21 +17,21 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "color.h"
|
||||
#include "common/cursor.h"
|
||||
#include "common/display.h"
|
||||
#include "common/surface.h"
|
||||
#include "pointer.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_pointer.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <freerdp/codec/color.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/gdi/gdi.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
|
||||
BOOL guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
@ -41,17 +41,18 @@ void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
|
||||
rdp_client->display, pointer->width, pointer->height);
|
||||
|
||||
/* Allocate data for image */
|
||||
unsigned char* data =
|
||||
(unsigned char*) malloc(pointer->width * pointer->height * 4);
|
||||
unsigned char* data = _aligned_malloc(pointer->width * pointer->height * 4, 16);
|
||||
|
||||
cairo_surface_t* surface;
|
||||
|
||||
/* Convert to alpha cursor if mask data present */
|
||||
if (pointer->andMaskData && pointer->xorMaskData)
|
||||
freerdp_alpha_cursor_convert(data,
|
||||
pointer->xorMaskData, pointer->andMaskData,
|
||||
pointer->width, pointer->height, pointer->xorBpp,
|
||||
((rdp_freerdp_context*) context)->clrconv);
|
||||
freerdp_image_copy_from_pointer_data(data,
|
||||
guac_rdp_get_native_pixel_format(TRUE), 0, 0, 0,
|
||||
pointer->width, pointer->height, pointer->xorMaskData,
|
||||
pointer->lengthXorMask, pointer->andMaskData,
|
||||
pointer->lengthAndMask, pointer->xorBpp,
|
||||
&context->gdi->palette);
|
||||
|
||||
/* Create surface from image data */
|
||||
surface = cairo_image_surface_create_for_data(
|
||||
@ -63,14 +64,16 @@ void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
|
||||
|
||||
/* Free surface */
|
||||
cairo_surface_destroy(surface);
|
||||
free(data);
|
||||
_aligned_free(data);
|
||||
|
||||
/* Remember buffer */
|
||||
((guac_rdp_pointer*) pointer)->layer = buffer;
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_pointer_set(rdpContext* context, rdpPointer* pointer) {
|
||||
BOOL guac_rdp_pointer_set(rdpContext* context, const rdpPointer* pointer) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
@ -80,6 +83,8 @@ void guac_rdp_pointer_set(rdpContext* context, rdpPointer* pointer) {
|
||||
pointer->xPos, pointer->yPos,
|
||||
((guac_rdp_pointer*) pointer)->layer->surface);
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_pointer_free(rdpContext* context, rdpPointer* pointer) {
|
||||
@ -91,13 +96,31 @@ void guac_rdp_pointer_free(rdpContext* context, rdpPointer* pointer) {
|
||||
/* Free buffer */
|
||||
guac_common_display_free_buffer(rdp_client->display, buffer);
|
||||
|
||||
/* NOTE: FreeRDP-allocated memory for the rdpPointer will be automatically
|
||||
* released after this free handler is invoked */
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_pointer_set_null(rdpContext* context) {
|
||||
/* STUB */
|
||||
BOOL guac_rdp_pointer_set_null(rdpContext* context) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Set cursor to empty/blank graphic */
|
||||
guac_common_cursor_set_blank(rdp_client->display->cursor);
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_pointer_set_default(rdpContext* context) {
|
||||
/* STUB */
|
||||
BOOL guac_rdp_pointer_set_default(rdpContext* context) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Set cursor to embedded pointer */
|
||||
guac_common_cursor_set_pointer(rdp_client->display->cursor);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -17,14 +17,14 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_POINTER_H
|
||||
#define GUAC_RDP_POINTER_H
|
||||
|
||||
#ifndef _GUAC_RDP_RDP_POINTER_H
|
||||
#define _GUAC_RDP_RDP_POINTER_H
|
||||
|
||||
#include "config.h"
|
||||
#include "common/display.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/graphics.h>
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
/**
|
||||
* Guacamole-specific rdpPointer data.
|
||||
@ -52,8 +52,11 @@ typedef struct guac_rdp_pointer {
|
||||
*
|
||||
* @param pointer
|
||||
* The pointer to cache.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer);
|
||||
BOOL guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer);
|
||||
|
||||
/**
|
||||
* Sets the given cached pointer as the current pointer. The given pointer must
|
||||
@ -64,8 +67,11 @@ void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer);
|
||||
*
|
||||
* @param pointer
|
||||
* The pointer to set as the current mouse pointer.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_pointer_set(rdpContext* context, rdpPointer* pointer);
|
||||
BOOL guac_rdp_pointer_set(rdpContext* context, const rdpPointer* pointer);
|
||||
|
||||
/**
|
||||
* Frees all Guacamole-related data associated with the given pointer, allowing
|
||||
@ -84,8 +90,11 @@ void guac_rdp_pointer_free(rdpContext* context, rdpPointer* pointer);
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_pointer_set_null(rdpContext* context);
|
||||
BOOL guac_rdp_pointer_set_null(rdpContext* context);
|
||||
|
||||
/**
|
||||
* Sets the system-dependent (as in dependent on the client system) default
|
||||
@ -93,7 +102,10 @@ void guac_rdp_pointer_set_null(rdpContext* context);
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_pointer_set_default(rdpContext* context);
|
||||
BOOL guac_rdp_pointer_set_default(rdpContext* context);
|
||||
|
||||
#endif
|
@ -17,9 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
#include "config.h"
|
||||
#include "rdp_print_job.h"
|
||||
#include "print-job.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
@ -20,8 +20,6 @@
|
||||
#ifndef GUAC_RDP_PRINT_JOB_H
|
||||
#define GUAC_RDP_PRINT_JOB_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/stream.h>
|
||||
#include <guacamole/user.h>
|
||||
@ -79,6 +77,9 @@ typedef enum guac_rdp_print_job_state {
|
||||
*/
|
||||
typedef struct guac_rdp_print_job {
|
||||
|
||||
/**
|
||||
* The Guacamole client associated with the RDP session.
|
||||
*/
|
||||
guac_client* client;
|
||||
|
||||
/**
|
@ -17,28 +17,30 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "audio_input.h"
|
||||
#include "bitmap.h"
|
||||
#include "channels/audio-input/audio-buffer.h"
|
||||
#include "channels/audio-input/audio-input.h"
|
||||
#include "channels/cliprdr.h"
|
||||
#include "channels/disp.h"
|
||||
#include "channels/pipe-svc.h"
|
||||
#include "channels/rail.h"
|
||||
#include "channels/rdpdr/rdpdr.h"
|
||||
#include "channels/rdpsnd/rdpsnd.h"
|
||||
#include "client.h"
|
||||
#include "color.h"
|
||||
#include "common/cursor.h"
|
||||
#include "common/display.h"
|
||||
#include "common/recording.h"
|
||||
#include "dvc.h"
|
||||
#include "config.h"
|
||||
#include "error.h"
|
||||
#include "fs.h"
|
||||
#include "gdi.h"
|
||||
#include "glyph.h"
|
||||
#include "keyboard.h"
|
||||
#include "plugins/channels.h"
|
||||
#include "pointer.h"
|
||||
#include "print-job.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_bitmap.h"
|
||||
#include "rdp_cliprdr.h"
|
||||
#include "rdp_disp.h"
|
||||
#include "rdp_fs.h"
|
||||
#include "rdp_print_job.h"
|
||||
#include "rdp_gdi.h"
|
||||
#include "rdp_glyph.h"
|
||||
#include "rdp_pointer.h"
|
||||
#include "rdp_rail.h"
|
||||
#include "rdp_stream.h"
|
||||
#include "rdp_svc.h"
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
#include "common-ssh/sftp.h"
|
||||
@ -46,6 +48,7 @@
|
||||
#include "common-ssh/user.h"
|
||||
#endif
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/cache/bitmap.h>
|
||||
#include <freerdp/cache/brush.h>
|
||||
#include <freerdp/cache/glyph.h>
|
||||
@ -53,349 +56,130 @@
|
||||
#include <freerdp/cache/palette.h>
|
||||
#include <freerdp/cache/pointer.h>
|
||||
#include <freerdp/channels/channels.h>
|
||||
#include <freerdp/client/channels.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/gdi/gdi.h>
|
||||
#include <freerdp/graphics.h>
|
||||
#include <freerdp/primary.h>
|
||||
#include <freerdp/settings.h>
|
||||
#include <freerdp/update.h>
|
||||
#include <guacamole/audio.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/timestamp.h>
|
||||
|
||||
#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
|
||||
#include <freerdp/client/cliprdr.h>
|
||||
#else
|
||||
#include "compat/client-cliprdr.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_CLIENT_DISP_H
|
||||
#include <freerdp/client/disp.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_EVENT_PUBSUB
|
||||
#include <freerdp/event.h>
|
||||
#endif
|
||||
|
||||
#ifdef LEGACY_FREERDP
|
||||
#include "compat/rail.h"
|
||||
#else
|
||||
#include <freerdp/rail.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/error.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/wtypes.h>
|
||||
#else
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_ADDIN_H
|
||||
#include <freerdp/addin.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_CLIENT_CHANNELS_H
|
||||
#include <freerdp/client/channels.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_VERSION_H
|
||||
#include <freerdp/version.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
/**
|
||||
* Callback invoked by FreeRDP for data received along a channel. This is the
|
||||
* most recent version of the callback and uses a 16-bit unsigned integer for
|
||||
* the channel ID, as well as different type naming for the datatype of the
|
||||
* data itself. This function does nothing more than invoke
|
||||
* freerdp_channels_data() with the given arguments. The prototypes of these
|
||||
* functions are compatible in 1.2 and later, but not necessarily prior to
|
||||
* that, hence the conditional compilation of differing prototypes.
|
||||
*
|
||||
* Beware that the official purpose of these parameters is an undocumented
|
||||
* mystery. The meanings below are derived from looking at how the function is
|
||||
* used within FreeRDP.
|
||||
*
|
||||
* @param rdp_inst
|
||||
* The RDP client instance associated with the channel receiving the data.
|
||||
*
|
||||
* @param channelId
|
||||
* The integer ID of the channel that received the data.
|
||||
*
|
||||
* @param data
|
||||
* A buffer containing the received data.
|
||||
*
|
||||
* @param size
|
||||
* The number of bytes received and contained in the given buffer (the
|
||||
* number of bytes received within the PDU that resulted in this function
|
||||
* being inboked).
|
||||
*
|
||||
* @param flags
|
||||
* Channel control flags, as defined by the CHANNEL_PDU_HEADER in the RDP
|
||||
* specification.
|
||||
*
|
||||
* @param total_size
|
||||
* The total length of the chanel data being received, which may span
|
||||
* multiple PDUs (see the "length" field of CHANNEL_PDU_HEADER).
|
||||
*
|
||||
* @return
|
||||
* Zero if the received channel data was successfully handled, non-zero
|
||||
* otherwise. Note that this return value is discarded in practice.
|
||||
*/
|
||||
#if defined(FREERDP_VERSION_MAJOR) \
|
||||
&& (FREERDP_VERSION_MAJOR > 1 || FREERDP_VERSION_MINOR >= 2)
|
||||
static int __guac_receive_channel_data(freerdp* rdp_inst, UINT16 channelId,
|
||||
BYTE* data, int size, int flags, int total_size) {
|
||||
#else
|
||||
static int __guac_receive_channel_data(freerdp* rdp_inst, int channelId,
|
||||
UINT8* data, int size, int flags, int total_size) {
|
||||
#endif
|
||||
return freerdp_channels_data(rdp_inst, channelId,
|
||||
data, size, flags, total_size);
|
||||
}
|
||||
|
||||
#ifdef HAVE_FREERDP_EVENT_PUBSUB
|
||||
/**
|
||||
* Called whenever a channel connects via the PubSub event system within
|
||||
* FreeRDP.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the active RDP session.
|
||||
*
|
||||
* @param e
|
||||
* Event-specific arguments, mainly the name of the channel, and a
|
||||
* reference to the associated plugin loaded for that channel by FreeRDP.
|
||||
*/
|
||||
static void guac_rdp_channel_connected(rdpContext* context,
|
||||
ChannelConnectedEventArgs* e) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdp_settings* settings = rdp_client->settings;
|
||||
|
||||
if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) {
|
||||
#ifdef HAVE_RDPSETTINGS_SUPPORTDISPLAYCONTROL
|
||||
/* Store reference to the display update plugin once it's connected */
|
||||
if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0) {
|
||||
|
||||
DispClientContext* disp = (DispClientContext*) e->pInterface;
|
||||
|
||||
/* Init module with current display size */
|
||||
guac_rdp_disp_set_size(rdp_client->disp, rdp_client->settings,
|
||||
context->instance, guac_rdp_get_width(context->instance),
|
||||
guac_rdp_get_height(context->instance));
|
||||
|
||||
/* Store connected channel */
|
||||
guac_rdp_disp_connect(rdp_client->disp, disp);
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Display update channel connected.");
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOL rdp_freerdp_pre_connect(freerdp* instance) {
|
||||
|
||||
rdpContext* context = instance->context;
|
||||
rdpChannels* channels = context->channels;
|
||||
rdpGraphics* graphics = context->graphics;
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdp_settings* settings = rdp_client->settings;
|
||||
|
||||
rdpBitmap* bitmap;
|
||||
rdpGlyph* glyph;
|
||||
rdpPointer* pointer;
|
||||
rdpPrimaryUpdate* primary;
|
||||
CLRCONV* clrconv;
|
||||
/* Push desired settings to FreeRDP */
|
||||
guac_rdp_push_settings(client, settings, instance);
|
||||
|
||||
guac_rdp_dvc_list* dvc_list = guac_rdp_dvc_list_alloc();
|
||||
|
||||
#ifdef HAVE_FREERDP_REGISTER_ADDIN_PROVIDER
|
||||
/* Init FreeRDP add-in provider */
|
||||
freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_EVENT_PUBSUB
|
||||
/* Subscribe to and handle channel connected events */
|
||||
PubSub_SubscribeChannelConnected(context->pubSub,
|
||||
(pChannelConnectedEventHandler) guac_rdp_channel_connected);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
/* Load "disp" plugin for display update */
|
||||
if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE)
|
||||
guac_rdp_disp_load_plugin(instance->context, dvc_list);
|
||||
#endif
|
||||
guac_rdp_disp_load_plugin(context);
|
||||
|
||||
/* Load "AUDIO_INPUT" plugin for audio input*/
|
||||
if (settings->enable_audio_input) {
|
||||
rdp_client->audio_input = guac_rdp_audio_buffer_alloc();
|
||||
guac_rdp_audio_load_plugin(instance->context, dvc_list);
|
||||
guac_rdp_audio_load_plugin(instance->context);
|
||||
}
|
||||
|
||||
/* Load clipboard plugin if not disabled */
|
||||
if (!(settings->disable_copy && settings->disable_paste)
|
||||
&& freerdp_channels_load_plugin(channels, instance->settings,
|
||||
"cliprdr", NULL)) {
|
||||
/* Load "cliprdr" service if not disabled */
|
||||
if (!(settings->disable_copy && settings->disable_paste))
|
||||
guac_rdp_clipboard_load_plugin(rdp_client->clipboard, context);
|
||||
else
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Failed to load cliprdr plugin. Clipboard will not work.");
|
||||
}
|
||||
"Copy and paste are both disabled. Clipboard plugin will not be loaded.");
|
||||
|
||||
/* If RDPSND/RDPDR required, load them */
|
||||
if (settings->printing_enabled
|
||||
|| settings->drive_enabled
|
||||
|| settings->audio_enabled) {
|
||||
|
||||
/* Load RDPDR plugin */
|
||||
if (freerdp_channels_load_plugin(channels, instance->settings,
|
||||
"guacdr", client))
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Failed to load guacdr plugin. Drive redirection and "
|
||||
"printing will not work. Sound MAY not work.");
|
||||
|
||||
/* Load RDPSND plugin */
|
||||
if (freerdp_channels_load_plugin(channels, instance->settings,
|
||||
"guacsnd", client))
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Failed to load guacsnd alongside guacdr plugin. Sound "
|
||||
"will not work. Drive redirection and printing MAY not "
|
||||
"work.");
|
||||
|
||||
guac_rdpdr_load_plugin(context);
|
||||
guac_rdpsnd_load_plugin(context);
|
||||
}
|
||||
|
||||
/* Load RAIL plugin if RemoteApp in use */
|
||||
if (settings->remote_app != NULL) {
|
||||
|
||||
#ifdef LEGACY_FREERDP
|
||||
RDP_PLUGIN_DATA* plugin_data = malloc(sizeof(RDP_PLUGIN_DATA) * 2);
|
||||
|
||||
plugin_data[0].size = sizeof(RDP_PLUGIN_DATA);
|
||||
plugin_data[0].data[0] = settings->remote_app;
|
||||
plugin_data[0].data[1] = settings->remote_app_dir;
|
||||
plugin_data[0].data[2] = settings->remote_app_args;
|
||||
plugin_data[0].data[3] = NULL;
|
||||
|
||||
plugin_data[1].size = 0;
|
||||
|
||||
/* Attempt to load rail */
|
||||
if (freerdp_channels_load_plugin(channels, instance->settings,
|
||||
"rail", plugin_data))
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Failed to load rail plugin. RemoteApp will not work.");
|
||||
#else
|
||||
/* Attempt to load rail */
|
||||
if (freerdp_channels_load_plugin(channels, instance->settings,
|
||||
"rail", instance->settings))
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Failed to load rail plugin. RemoteApp will not work.");
|
||||
#endif
|
||||
|
||||
}
|
||||
if (settings->remote_app != NULL)
|
||||
guac_rdp_rail_load_plugin(context);
|
||||
|
||||
/* Load SVC plugin instances for all static channels */
|
||||
if (settings->svc_names != NULL) {
|
||||
|
||||
char** current = settings->svc_names;
|
||||
do {
|
||||
|
||||
guac_rdp_svc* svc = guac_rdp_alloc_svc(client, *current);
|
||||
|
||||
/* Attempt to load guacsvc plugin for new static channel */
|
||||
if (freerdp_channels_load_plugin(channels, instance->settings,
|
||||
"guacsvc", svc)) {
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Cannot create static channel \"%s\": failed to load guacsvc plugin.",
|
||||
svc->name);
|
||||
guac_rdp_free_svc(svc);
|
||||
}
|
||||
|
||||
/* Store and log on success */
|
||||
else {
|
||||
guac_rdp_add_svc(client, svc);
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Created static channel \"%s\"...",
|
||||
svc->name);
|
||||
}
|
||||
|
||||
guac_rdp_pipe_svc_load_plugin(context, *current);
|
||||
} while (*(++current) != NULL);
|
||||
|
||||
}
|
||||
|
||||
/* Load DRDYNVC plugin if required */
|
||||
if (guac_rdp_load_drdynvc(instance->context, dvc_list))
|
||||
/* Load plugin providing Dynamic Virtual Channel support, if required */
|
||||
if (instance->settings->SupportDynamicChannels &&
|
||||
guac_freerdp_channels_load_plugin(context, "drdynvc",
|
||||
instance->settings)) {
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Failed to load drdynvc plugin. Display update and audio "
|
||||
"input support will be disabled.");
|
||||
}
|
||||
|
||||
/* Dynamic virtual channel list is no longer needed */
|
||||
guac_rdp_dvc_list_free(dvc_list);
|
||||
|
||||
/* Init color conversion structure */
|
||||
clrconv = calloc(1, sizeof(CLRCONV));
|
||||
clrconv->alpha = 1;
|
||||
clrconv->invert = 0;
|
||||
clrconv->rgb555 = 0;
|
||||
clrconv->palette = calloc(1, sizeof(rdpPalette));
|
||||
((rdp_freerdp_context*) context)->clrconv = clrconv;
|
||||
|
||||
/* Init FreeRDP cache */
|
||||
instance->context->cache = cache_new(instance->settings);
|
||||
/* Init FreeRDP internal GDI implementation */
|
||||
if (!gdi_init(instance, guac_rdp_get_native_pixel_format(FALSE)))
|
||||
return FALSE;
|
||||
|
||||
/* Set up bitmap handling */
|
||||
bitmap = calloc(1, sizeof(rdpBitmap));
|
||||
bitmap->size = sizeof(guac_rdp_bitmap);
|
||||
bitmap->New = guac_rdp_bitmap_new;
|
||||
bitmap->Free = guac_rdp_bitmap_free;
|
||||
bitmap->Paint = guac_rdp_bitmap_paint;
|
||||
bitmap->Decompress = guac_rdp_bitmap_decompress;
|
||||
bitmap->SetSurface = guac_rdp_bitmap_setsurface;
|
||||
graphics_register_bitmap(context->graphics, bitmap);
|
||||
free(bitmap);
|
||||
rdpBitmap bitmap = *graphics->Bitmap_Prototype;
|
||||
bitmap.size = sizeof(guac_rdp_bitmap);
|
||||
bitmap.New = guac_rdp_bitmap_new;
|
||||
bitmap.Free = guac_rdp_bitmap_free;
|
||||
bitmap.Paint = guac_rdp_bitmap_paint;
|
||||
bitmap.SetSurface = guac_rdp_bitmap_setsurface;
|
||||
graphics_register_bitmap(graphics, &bitmap);
|
||||
|
||||
/* Set up glyph handling */
|
||||
glyph = calloc(1, sizeof(rdpGlyph));
|
||||
glyph->size = sizeof(guac_rdp_glyph);
|
||||
glyph->New = guac_rdp_glyph_new;
|
||||
glyph->Free = guac_rdp_glyph_free;
|
||||
glyph->Draw = guac_rdp_glyph_draw;
|
||||
glyph->BeginDraw = guac_rdp_glyph_begindraw;
|
||||
glyph->EndDraw = guac_rdp_glyph_enddraw;
|
||||
graphics_register_glyph(context->graphics, glyph);
|
||||
free(glyph);
|
||||
rdpGlyph glyph = *graphics->Glyph_Prototype;
|
||||
glyph.size = sizeof(guac_rdp_glyph);
|
||||
glyph.New = guac_rdp_glyph_new;
|
||||
glyph.Free = guac_rdp_glyph_free;
|
||||
glyph.Draw = guac_rdp_glyph_draw;
|
||||
glyph.BeginDraw = guac_rdp_glyph_begindraw;
|
||||
glyph.EndDraw = guac_rdp_glyph_enddraw;
|
||||
graphics_register_glyph(graphics, &glyph);
|
||||
|
||||
/* Set up pointer handling */
|
||||
pointer = calloc(1, sizeof(rdpPointer));
|
||||
pointer->size = sizeof(guac_rdp_pointer);
|
||||
pointer->New = guac_rdp_pointer_new;
|
||||
pointer->Free = guac_rdp_pointer_free;
|
||||
pointer->Set = guac_rdp_pointer_set;
|
||||
#ifdef HAVE_RDPPOINTER_SETNULL
|
||||
pointer->SetNull = guac_rdp_pointer_set_null;
|
||||
#endif
|
||||
#ifdef HAVE_RDPPOINTER_SETDEFAULT
|
||||
pointer->SetDefault = guac_rdp_pointer_set_default;
|
||||
#endif
|
||||
graphics_register_pointer(context->graphics, pointer);
|
||||
free(pointer);
|
||||
rdpPointer pointer = *graphics->Pointer_Prototype;
|
||||
pointer.size = sizeof(guac_rdp_pointer);
|
||||
pointer.New = guac_rdp_pointer_new;
|
||||
pointer.Free = guac_rdp_pointer_free;
|
||||
pointer.Set = guac_rdp_pointer_set;
|
||||
pointer.SetNull = guac_rdp_pointer_set_null;
|
||||
pointer.SetDefault = guac_rdp_pointer_set_default;
|
||||
graphics_register_pointer(graphics, &pointer);
|
||||
|
||||
/* Set up GDI */
|
||||
instance->update->DesktopResize = guac_rdp_gdi_desktop_resize;
|
||||
instance->update->EndPaint = guac_rdp_gdi_end_paint;
|
||||
instance->update->Palette = guac_rdp_gdi_palette_update;
|
||||
instance->update->SetBounds = guac_rdp_gdi_set_bounds;
|
||||
|
||||
primary = instance->update->primary;
|
||||
rdpPrimaryUpdate* primary = instance->update->primary;
|
||||
primary->DstBlt = guac_rdp_gdi_dstblt;
|
||||
primary->PatBlt = guac_rdp_gdi_patblt;
|
||||
primary->ScrBlt = guac_rdp_gdi_scrblt;
|
||||
primary->MemBlt = guac_rdp_gdi_memblt;
|
||||
primary->OpaqueRect = guac_rdp_gdi_opaquerect;
|
||||
|
||||
pointer_cache_register_callbacks(instance->update);
|
||||
glyph_cache_register_callbacks(instance->update);
|
||||
@ -404,39 +188,6 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
|
||||
offscreen_cache_register_callbacks(instance->update);
|
||||
palette_cache_register_callbacks(instance->update);
|
||||
|
||||
/* Init channels (pre-connect) */
|
||||
if (freerdp_channels_pre_connect(channels, instance)) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error initializing RDP client channel manager");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked by FreeRDP just after the connection is established with
|
||||
* the RDP server. Implementations are required to manually invoke
|
||||
* freerdp_channels_post_connect().
|
||||
*
|
||||
* @param instance
|
||||
* The FreeRDP instance that has just connected.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE if an error occurs.
|
||||
*/
|
||||
static BOOL rdp_freerdp_post_connect(freerdp* instance) {
|
||||
|
||||
rdpContext* context = instance->context;
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
rdpChannels* channels = instance->context->channels;
|
||||
|
||||
/* Init channels (post-connect) */
|
||||
if (freerdp_channels_post_connect(channels, instance)) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error initializing RDP client channel manager");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
@ -499,8 +250,9 @@ static BOOL rdp_freerdp_authenticate(freerdp* instance, char** username,
|
||||
* @return
|
||||
* TRUE if the certificate passes verification, FALSE otherwise.
|
||||
*/
|
||||
static BOOL rdp_freerdp_verify_certificate(freerdp* instance, char* subject,
|
||||
char* issuer, char* fingerprint) {
|
||||
static DWORD rdp_freerdp_verify_certificate(freerdp* instance,
|
||||
const char* common_name, const char* subject, const char* issuer,
|
||||
const char* fingerprint, BOOL host_mismatch) {
|
||||
|
||||
rdpContext* context = instance->context;
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
@ -510,44 +262,14 @@ static BOOL rdp_freerdp_verify_certificate(freerdp* instance, char* subject,
|
||||
/* Bypass validation if ignore_certificate given */
|
||||
if (rdp_client->settings->ignore_certificate) {
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Certificate validation bypassed");
|
||||
return TRUE;
|
||||
return 2; /* Accept only for this session */
|
||||
}
|
||||
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Certificate validation failed");
|
||||
return FALSE;
|
||||
return 0; /* Reject certificate */
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked by FreeRDP after a new rdpContext has been allocated and
|
||||
* associated with the current FreeRDP instance. Implementations are required
|
||||
* to manually invoke freerdp_channels_new() at this point.
|
||||
*
|
||||
* @param instance
|
||||
* The FreeRDP instance whose context has just been allocated.
|
||||
*
|
||||
* @param context
|
||||
* The newly-allocated FreeRDP context.
|
||||
*/
|
||||
static void rdp_freerdp_context_new(freerdp* instance, rdpContext* context) {
|
||||
context->channels = freerdp_channels_new();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked by FreeRDP when the rdpContext is being freed. This must be
|
||||
* provided, but there is no Guacamole-specific data associated with the
|
||||
* FreeRDP context, so nothing is done here.
|
||||
*
|
||||
* @param instance
|
||||
* The FreeRDP instance whose context is being freed.
|
||||
*
|
||||
* @param context
|
||||
* The FreeRDP context being freed.
|
||||
*/
|
||||
static void rdp_freerdp_context_free(freerdp* instance, rdpContext* context) {
|
||||
/* EMPTY */
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for messages from the RDP server for the given number of milliseconds.
|
||||
*
|
||||
@ -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;
|
||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||
rdpChannels* channels = rdp_inst->context->channels;
|
||||
|
||||
int result;
|
||||
int index;
|
||||
HANDLE handles[GUAC_RDP_MAX_FILE_DESCRIPTORS];
|
||||
int num_handles = freerdp_get_event_handles(rdp_inst->context, handles,
|
||||
GUAC_RDP_MAX_FILE_DESCRIPTORS);
|
||||
|
||||
/* List of all file descriptors which we may read data from */
|
||||
void* read_fds[GUAC_RDP_MAX_FILE_DESCRIPTORS];
|
||||
int read_count = 0;
|
||||
/* Wait for data and construct a reasonable frame */
|
||||
int result = WaitForMultipleObjects(num_handles, handles, FALSE,
|
||||
timeout_msecs);
|
||||
|
||||
/* List of all file descriptors which data may be written to. These will
|
||||
* ultimately be ignored, but FreeRDP requires that both read and write
|
||||
* file descriptors be retrieved simultaneously. */
|
||||
void* write_fds[GUAC_RDP_MAX_FILE_DESCRIPTORS];
|
||||
int write_count = 0;
|
||||
/* Translate WaitForMultipleObjects() return values */
|
||||
switch (result) {
|
||||
|
||||
struct pollfd fds[GUAC_RDP_MAX_FILE_DESCRIPTORS];
|
||||
|
||||
/* Get RDP file descriptors */
|
||||
if (!freerdp_get_fds(rdp_inst, read_fds, &read_count,
|
||||
write_fds, &write_count)) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
||||
"Unable to read RDP file descriptors.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get RDP channel file descriptors */
|
||||
if (!freerdp_channels_get_fds(channels, rdp_inst, read_fds, &read_count,
|
||||
write_fds, &write_count)) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
||||
"Unable to read RDP channel file descriptors.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If no file descriptors, error */
|
||||
if (read_count == 0) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
||||
"No file descriptors associated with RDP connection.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Populate poll() array of read file descriptors */
|
||||
for (index = 0; index < read_count; index++) {
|
||||
|
||||
struct pollfd* current = &fds[index];
|
||||
|
||||
/* Init poll() array element with RDP file descriptor */
|
||||
current->fd = (int)(long) (read_fds[index]);
|
||||
current->events = POLLIN;
|
||||
current->revents = 0;
|
||||
|
||||
}
|
||||
|
||||
/* Wait until data can be read from RDP file descriptors */
|
||||
result = poll(fds, read_count, timeout_msecs);
|
||||
if (result < 0) {
|
||||
|
||||
/* If error ignorable, pretend timout occurred */
|
||||
if (errno == EAGAIN
|
||||
|| errno == EWOULDBLOCK
|
||||
|| errno == EINPROGRESS
|
||||
|| errno == EINTR)
|
||||
/* Timeout elapsed before wait could complete */
|
||||
case WAIT_TIMEOUT:
|
||||
return 0;
|
||||
|
||||
/* Otherwise, return as error */
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
|
||||
"Error waiting for file descriptor.");
|
||||
return -1;
|
||||
/* Attempt to wait failed due to an error */
|
||||
case WAIT_FAILED:
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/* Return wait result */
|
||||
return result;
|
||||
/* Wait was successful */
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
@ -686,29 +360,16 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
||||
|
||||
rdp_client->current_surface = rdp_client->display->default_surface;
|
||||
|
||||
rdp_client->requested_clipboard_format = CB_FORMAT_TEXT;
|
||||
rdp_client->available_svc = guac_common_list_alloc();
|
||||
|
||||
#ifdef HAVE_FREERDP_CHANNELS_GLOBAL_INIT
|
||||
freerdp_channels_global_init();
|
||||
#endif
|
||||
|
||||
/* Init client */
|
||||
freerdp* rdp_inst = freerdp_new();
|
||||
rdp_inst->PreConnect = rdp_freerdp_pre_connect;
|
||||
rdp_inst->PostConnect = rdp_freerdp_post_connect;
|
||||
rdp_inst->Authenticate = rdp_freerdp_authenticate;
|
||||
rdp_inst->VerifyCertificate = rdp_freerdp_verify_certificate;
|
||||
rdp_inst->ReceiveChannelData = __guac_receive_channel_data;
|
||||
|
||||
/* Allocate FreeRDP context */
|
||||
#ifdef LEGACY_FREERDP
|
||||
rdp_inst->context_size = sizeof(rdp_freerdp_context);
|
||||
#else
|
||||
rdp_inst->ContextSize = sizeof(rdp_freerdp_context);
|
||||
#endif
|
||||
rdp_inst->ContextNew = (pContextNew) rdp_freerdp_context_new;
|
||||
rdp_inst->ContextFree = (pContextFree) rdp_freerdp_context_free;
|
||||
|
||||
freerdp_context_new(rdp_inst);
|
||||
((rdp_freerdp_context*) rdp_inst->context)->client = client;
|
||||
@ -720,9 +381,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
||||
/* Set default pointer */
|
||||
guac_common_cursor_set_pointer(rdp_client->display->cursor);
|
||||
|
||||
/* Push desired settings to FreeRDP */
|
||||
guac_rdp_push_settings(client, settings, rdp_inst);
|
||||
|
||||
/* Connect to RDP server */
|
||||
if (!freerdp_connect(rdp_inst)) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND,
|
||||
@ -732,7 +390,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
||||
|
||||
/* Connection complete */
|
||||
rdp_client->rdp_inst = rdp_inst;
|
||||
rdpChannels* channels = rdp_inst->context->channels;
|
||||
|
||||
guac_timestamp last_frame_end = guac_timestamp_current();
|
||||
|
||||
@ -744,9 +401,7 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
||||
&& !guac_rdp_disp_reconnect_needed(rdp_client->disp)) {
|
||||
|
||||
/* Update remote display size */
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
guac_rdp_disp_update_size(rdp_client->disp, settings, rdp_inst);
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* Wait for data and construct a reasonable frame */
|
||||
int wait_result = rdp_guac_client_wait_for_messages(client,
|
||||
@ -762,42 +417,15 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
||||
guac_timestamp frame_end;
|
||||
int frame_remaining;
|
||||
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* Check the libfreerdp fds */
|
||||
if (!freerdp_check_fds(rdp_inst)
|
||||
|| !freerdp_channels_check_fds(channels, rdp_inst)) {
|
||||
if (!freerdp_check_event_handles(rdp_inst->context)) {
|
||||
|
||||
/* Flag connection failure */
|
||||
wait_result = -1;
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
/* Check for channel events */
|
||||
wMessage* event = freerdp_channels_pop_event(channels);
|
||||
if (event) {
|
||||
|
||||
/* Handle channel events (clipboard and RAIL) */
|
||||
#ifdef LEGACY_EVENT
|
||||
if (event->event_class == CliprdrChannel_Class)
|
||||
guac_rdp_process_cliprdr_event(client, event);
|
||||
else if (event->event_class == RailChannel_Class)
|
||||
guac_rdp_process_rail_event(client, event);
|
||||
#else
|
||||
if (GetMessageClass(event->id) == CliprdrChannel_Class)
|
||||
guac_rdp_process_cliprdr_event(client, event);
|
||||
else if (GetMessageClass(event->id) == RailChannel_Class)
|
||||
guac_rdp_process_rail_event(client, event);
|
||||
#endif
|
||||
|
||||
freerdp_event_free(event);
|
||||
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* Calculate time remaining in frame */
|
||||
frame_end = guac_timestamp_current();
|
||||
frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION
|
||||
@ -830,9 +458,7 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
||||
}
|
||||
|
||||
/* Test whether the RDP server is closing the connection */
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
int connection_closing = freerdp_shall_disconnect(rdp_inst);
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* Close connection cleanly if server is disconnecting */
|
||||
if (connection_closing)
|
||||
@ -858,16 +484,13 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
||||
guac_rdp_print_job_free(rdp_client->active_job);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* Disconnect client and channels */
|
||||
freerdp_channels_close(channels, rdp_inst);
|
||||
freerdp_channels_free(channels);
|
||||
freerdp_disconnect(rdp_inst);
|
||||
|
||||
/* Clean up FreeRDP internal GDI implementation */
|
||||
gdi_free(rdp_inst);
|
||||
|
||||
/* Clean up RDP client context */
|
||||
freerdp_clrconv_free(((rdp_freerdp_context*) rdp_inst->context)->clrconv);
|
||||
cache_free(rdp_inst->context->cache);
|
||||
freerdp_context_free(rdp_inst);
|
||||
|
||||
/* Clean up RDP client */
|
||||
@ -883,8 +506,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
||||
/* Free display */
|
||||
guac_common_display_free(rdp_client->display);
|
||||
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* Client is now disconnected */
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Internal RDP client disconnected");
|
||||
|
||||
|
@ -20,24 +20,19 @@
|
||||
#ifndef GUAC_RDP_H
|
||||
#define GUAC_RDP_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "audio_input.h"
|
||||
#include "channels/audio-input/audio-buffer.h"
|
||||
#include "channels/cliprdr.h"
|
||||
#include "channels/disp.h"
|
||||
#include "common/clipboard.h"
|
||||
#include "common/display.h"
|
||||
#include "common/list.h"
|
||||
#include "common/recording.h"
|
||||
#include "common/surface.h"
|
||||
#include "config.h"
|
||||
#include "fs.h"
|
||||
#include "keyboard.h"
|
||||
#include "rdp_disp.h"
|
||||
#include "rdp_fs.h"
|
||||
#include "rdp_print_job.h"
|
||||
#include "rdp_settings.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/codec/color.h>
|
||||
#include <guacamole/audio.h>
|
||||
#include <guacamole/client.h>
|
||||
#include "print-job.h"
|
||||
#include "settings.h"
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
#include "common-ssh/sftp.h"
|
||||
@ -45,6 +40,12 @@
|
||||
#include "common-ssh/user.h"
|
||||
#endif
|
||||
|
||||
#include <freerdp/codec/color.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <guacamole/audio.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@ -95,17 +96,9 @@ typedef struct guac_rdp_client {
|
||||
guac_rdp_keyboard* keyboard;
|
||||
|
||||
/**
|
||||
* The current clipboard contents.
|
||||
* The current state of the clipboard and the CLIPRDR channel.
|
||||
*/
|
||||
guac_common_clipboard* clipboard;
|
||||
|
||||
/**
|
||||
* The format of the clipboard which was requested. Data received from
|
||||
* the RDP server should conform to this format. This will be one of
|
||||
* several legal clipboard format values defined within FreeRDP, such as
|
||||
* CB_FORMAT_TEXT.
|
||||
*/
|
||||
int requested_clipboard_format;
|
||||
guac_rdp_clipboard* clipboard;
|
||||
|
||||
/**
|
||||
* Audio output, if any.
|
||||
@ -160,13 +153,6 @@ typedef struct guac_rdp_client {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@ -191,11 +177,6 @@ typedef struct rdp_freerdp_context {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
@ -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