summaryrefslogtreecommitdiffstats
path: root/channels/rail
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /channels/rail
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'channels/rail')
-rw-r--r--channels/rail/CMakeLists.txt26
-rw-r--r--channels/rail/ChannelOptions.cmake13
-rw-r--r--channels/rail/client/CMakeLists.txt33
-rw-r--r--channels/rail/client/client_rails.c98
-rw-r--r--channels/rail/client/rail_main.c756
-rw-r--r--channels/rail/client/rail_main.h60
-rw-r--r--channels/rail/client/rail_orders.c1564
-rw-r--r--channels/rail/client/rail_orders.h60
-rw-r--r--channels/rail/rail_common.c591
-rw-r--r--channels/rail/rail_common.h76
-rw-r--r--channels/rail/server/CMakeLists.txt33
-rw-r--r--channels/rail/server/rail_main.c1728
-rw-r--r--channels/rail/server/rail_main.h44
13 files changed, 5082 insertions, 0 deletions
diff --git a/channels/rail/CMakeLists.txt b/channels/rail/CMakeLists.txt
new file mode 100644
index 0000000..d372dda
--- /dev/null
+++ b/channels/rail/CMakeLists.txt
@@ -0,0 +1,26 @@
+# FreeRDP: A Remote Desktop Protocol Implementation
+# FreeRDP cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# Licensed 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.
+
+define_channel("rail")
+
+if(WITH_CLIENT_CHANNELS)
+ add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
+endif()
+
+if(WITH_SERVER_CHANNELS)
+ add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
+endif()
diff --git a/channels/rail/ChannelOptions.cmake b/channels/rail/ChannelOptions.cmake
new file mode 100644
index 0000000..76f8571
--- /dev/null
+++ b/channels/rail/ChannelOptions.cmake
@@ -0,0 +1,13 @@
+
+set(OPTION_DEFAULT OFF)
+set(OPTION_CLIENT_DEFAULT ON)
+set(OPTION_SERVER_DEFAULT OFF)
+
+define_channel_options(NAME "rail" TYPE "static"
+ DESCRIPTION "Remote Programs Virtual Channel Extension"
+ SPECIFICATIONS "[MS-RDPERP]"
+ DEFAULT ${OPTION_DEFAULT})
+
+define_channel_client_options(${OPTION_CLIENT_DEFAULT})
+define_channel_server_options(${OPTION_SERVER_DEFAULT})
+
diff --git a/channels/rail/client/CMakeLists.txt b/channels/rail/client/CMakeLists.txt
new file mode 100644
index 0000000..ad274b0
--- /dev/null
+++ b/channels/rail/client/CMakeLists.txt
@@ -0,0 +1,33 @@
+# FreeRDP: A Remote Desktop Protocol Implementation
+# FreeRDP cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# Licensed 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.
+
+define_channel_client("rail")
+
+set(${MODULE_PREFIX}_SRCS
+ ../rail_common.h
+ ../rail_common.c
+ client_rails.c
+ rail_main.c
+ rail_main.h
+ rail_orders.c
+ rail_orders.h
+)
+
+set(${MODULE_PREFIX}_LIBS
+ freerdp
+)
+add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntryEx")
diff --git a/channels/rail/client/client_rails.c b/channels/rail/client/client_rails.c
new file mode 100644
index 0000000..ee6db44
--- /dev/null
+++ b/channels/rail/client/client_rails.c
@@ -0,0 +1,98 @@
+
+#include <freerdp/freerdp.h>
+#include <freerdp/client/rail.h>
+
+#include "rail_main.h"
+
+UINT client_rail_server_start_cmd(RailClientContext* context)
+{
+ UINT status = 0;
+ char argsAndFile[520] = { 0 };
+ RAIL_EXEC_ORDER exec = { 0 };
+ RAIL_SYSPARAM_ORDER sysparam = { 0 };
+ RAIL_CLIENT_STATUS_ORDER clientStatus = { 0 };
+
+ WINPR_ASSERT(context);
+ railPlugin* rail = context->handle;
+ WINPR_ASSERT(rail);
+ WINPR_ASSERT(rail->rdpcontext);
+
+ const rdpSettings* settings = rail->rdpcontext->settings;
+ WINPR_ASSERT(settings);
+
+ clientStatus.flags = TS_RAIL_CLIENTSTATUS_ALLOWLOCALMOVESIZE;
+
+ if (freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled))
+ clientStatus.flags |= TS_RAIL_CLIENTSTATUS_AUTORECONNECT;
+
+ clientStatus.flags |= TS_RAIL_CLIENTSTATUS_ZORDER_SYNC;
+ clientStatus.flags |= TS_RAIL_CLIENTSTATUS_WINDOW_RESIZE_MARGIN_SUPPORTED;
+ clientStatus.flags |= TS_RAIL_CLIENTSTATUS_APPBAR_REMOTING_SUPPORTED;
+ clientStatus.flags |= TS_RAIL_CLIENTSTATUS_POWER_DISPLAY_REQUEST_SUPPORTED;
+ clientStatus.flags |= TS_RAIL_CLIENTSTATUS_BIDIRECTIONAL_CLOAK_SUPPORTED;
+ status = context->ClientInformation(context, &clientStatus);
+
+ if (status != CHANNEL_RC_OK)
+ return status;
+
+ if (freerdp_settings_get_bool(settings, FreeRDP_RemoteAppLanguageBarSupported))
+ {
+ RAIL_LANGBAR_INFO_ORDER langBarInfo;
+ langBarInfo.languageBarStatus = 0x00000008; /* TF_SFT_HIDDEN */
+ status = context->ClientLanguageBarInfo(context, &langBarInfo);
+
+ /* We want the language bar, but the server might not support it. */
+ switch (status)
+ {
+ case CHANNEL_RC_OK:
+ case ERROR_BAD_CONFIGURATION:
+ break;
+ default:
+ return status;
+ }
+ }
+
+ sysparam.params = 0;
+ sysparam.params |= SPI_MASK_SET_HIGH_CONTRAST;
+ sysparam.highContrast.colorScheme.string = NULL;
+ sysparam.highContrast.colorScheme.length = 0;
+ sysparam.highContrast.flags = 0x7E;
+ sysparam.params |= SPI_MASK_SET_MOUSE_BUTTON_SWAP;
+ sysparam.mouseButtonSwap = FALSE;
+ sysparam.params |= SPI_MASK_SET_KEYBOARD_PREF;
+ sysparam.keyboardPref = FALSE;
+ sysparam.params |= SPI_MASK_SET_DRAG_FULL_WINDOWS;
+ sysparam.dragFullWindows = FALSE;
+ sysparam.params |= SPI_MASK_SET_KEYBOARD_CUES;
+ sysparam.keyboardCues = FALSE;
+ sysparam.params |= SPI_MASK_SET_WORK_AREA;
+ sysparam.workArea.left = 0;
+ sysparam.workArea.top = 0;
+ sysparam.workArea.right = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
+ sysparam.workArea.bottom = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
+ sysparam.dragFullWindows = FALSE;
+ status = context->ClientSystemParam(context, &sysparam);
+
+ if (status != CHANNEL_RC_OK)
+ return status;
+
+ const char* RemoteApplicationFile =
+ freerdp_settings_get_string(settings, FreeRDP_RemoteApplicationFile);
+ const char* RemoteApplicationCmdLine =
+ freerdp_settings_get_string(settings, FreeRDP_RemoteApplicationCmdLine);
+ if (RemoteApplicationFile && RemoteApplicationCmdLine)
+ {
+ _snprintf(argsAndFile, ARRAYSIZE(argsAndFile), "%s %s", RemoteApplicationCmdLine,
+ RemoteApplicationFile);
+ exec.RemoteApplicationArguments = argsAndFile;
+ }
+ else if (RemoteApplicationFile)
+ exec.RemoteApplicationArguments = RemoteApplicationFile;
+ else
+ exec.RemoteApplicationArguments = RemoteApplicationCmdLine;
+ exec.RemoteApplicationProgram =
+ freerdp_settings_get_string(settings, FreeRDP_RemoteApplicationProgram);
+ exec.RemoteApplicationWorkingDir =
+ freerdp_settings_get_string(settings, FreeRDP_ShellWorkingDirectory);
+ return context->ClientExecute(context, &exec);
+}
diff --git a/channels/rail/client/rail_main.c b/channels/rail/client/rail_main.c
new file mode 100644
index 0000000..c5828c1
--- /dev/null
+++ b/channels/rail/client/rail_main.c
@@ -0,0 +1,756 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * RAIL Virtual Channel Plugin
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2011 Roman Barabanov <romanbarabanov@gmail.com>
+ * Copyright 2011 Vic Lee
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
+ * Copyright 2017 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2017 Thincast Technologies GmbH
+ *
+ * Licensed 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 <freerdp/config.h>
+
+#include <winpr/crt.h>
+
+#include <freerdp/types.h>
+#include <freerdp/constants.h>
+#include <freerdp/freerdp.h>
+
+#include "rail_orders.h"
+#include "rail_main.h"
+
+#include "../../../channels/client/addin.h"
+
+RailClientContext* rail_get_client_interface(railPlugin* rail)
+{
+ RailClientContext* pInterface = NULL;
+
+ if (!rail)
+ return NULL;
+
+ pInterface = (RailClientContext*)rail->channelEntryPoints.pInterface;
+ return pInterface;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send(railPlugin* rail, wStream* s)
+{
+ UINT status = 0;
+
+ if (!rail)
+ {
+ Stream_Free(s, TRUE);
+ return CHANNEL_RC_BAD_INIT_HANDLE;
+ }
+
+ status = rail->channelEntryPoints.pVirtualChannelWriteEx(
+ rail->InitHandle, rail->OpenHandle, Stream_Buffer(s), (UINT32)Stream_GetPosition(s), s);
+
+ if (status != CHANNEL_RC_OK)
+ {
+ Stream_Free(s, TRUE);
+ WLog_ERR(TAG, "pVirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
+ WTSErrorToString(status), status);
+ }
+
+ return status;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_send_channel_data(railPlugin* rail, wStream* src)
+{
+ wStream* s = NULL;
+ size_t length = 0;
+
+ if (!rail || !src)
+ return ERROR_INVALID_PARAMETER;
+
+ length = Stream_GetPosition(src);
+ s = Stream_New(NULL, length);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "Stream_New failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write(s, Stream_Buffer(src), length);
+ return rail_send(rail, s);
+}
+
+/**
+ * Callback Interface
+ */
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_client_execute(RailClientContext* context, const RAIL_EXEC_ORDER* exec)
+{
+ char* exeOrFile = NULL;
+ UINT error = 0;
+ railPlugin* rail = NULL;
+ UINT16 flags = 0;
+ RAIL_UNICODE_STRING ruExeOrFile = { 0 };
+ RAIL_UNICODE_STRING ruWorkingDir = { 0 };
+ RAIL_UNICODE_STRING ruArguments = { 0 };
+
+ if (!context || !exec)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ exeOrFile = exec->RemoteApplicationProgram;
+ flags = exec->flags;
+
+ if (!exeOrFile)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!utf8_string_to_rail_string(exec->RemoteApplicationProgram,
+ &ruExeOrFile) || /* RemoteApplicationProgram */
+ !utf8_string_to_rail_string(exec->RemoteApplicationWorkingDir,
+ &ruWorkingDir) || /* ShellWorkingDirectory */
+ !utf8_string_to_rail_string(exec->RemoteApplicationArguments,
+ &ruArguments)) /* RemoteApplicationCmdLine */
+ error = ERROR_INTERNAL_ERROR;
+ else
+ error = rail_send_client_exec_order(rail, flags, &ruExeOrFile, &ruWorkingDir, &ruArguments);
+
+ free(ruExeOrFile.string);
+ free(ruWorkingDir.string);
+ free(ruArguments.string);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_client_activate(RailClientContext* context, const RAIL_ACTIVATE_ORDER* activate)
+{
+ railPlugin* rail = NULL;
+
+ if (!context || !activate)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ return rail_send_client_activate_order(rail, activate);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send_client_sysparam(RailClientContext* context, RAIL_SYSPARAM_ORDER* sysparam)
+{
+ wStream* s = NULL;
+ size_t length = RAIL_SYSPARAM_ORDER_LENGTH;
+ railPlugin* rail = NULL;
+ UINT error = 0;
+ BOOL extendedSpiSupported = 0;
+
+ if (!context || !sysparam)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+
+ switch (sysparam->param)
+ {
+ case SPI_SET_DRAG_FULL_WINDOWS:
+ case SPI_SET_KEYBOARD_CUES:
+ case SPI_SET_KEYBOARD_PREF:
+ case SPI_SET_MOUSE_BUTTON_SWAP:
+ length += 1;
+ break;
+
+ case SPI_SET_WORK_AREA:
+ case SPI_DISPLAY_CHANGE:
+ case SPI_TASKBAR_POS:
+ length += 8;
+ break;
+
+ case SPI_SET_HIGH_CONTRAST:
+ length += sysparam->highContrast.colorSchemeLength + 10;
+ break;
+
+ case SPI_SETFILTERKEYS:
+ length += 20;
+ break;
+
+ case SPI_SETSTICKYKEYS:
+ case SPI_SETCARETWIDTH:
+ case SPI_SETTOGGLEKEYS:
+ length += 4;
+ break;
+
+ default:
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ s = rail_pdu_init(length);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ extendedSpiSupported = rail_is_extended_spi_supported(rail->channelFlags);
+ if ((error = rail_write_sysparam_order(s, sysparam, extendedSpiSupported)))
+ {
+ WLog_ERR(TAG, "rail_write_client_sysparam_order failed with error %" PRIu32 "!", error);
+ Stream_Free(s, TRUE);
+ return error;
+ }
+
+ if ((error = rail_send_pdu(rail, s, TS_RAIL_ORDER_SYSPARAM)))
+ {
+ WLog_ERR(TAG, "rail_send_pdu failed with error %" PRIu32 "!", error);
+ }
+
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_client_system_param(RailClientContext* context,
+ const RAIL_SYSPARAM_ORDER* sysInParam)
+{
+ UINT error = CHANNEL_RC_OK;
+ RAIL_SYSPARAM_ORDER sysparam;
+
+ if (!context || !sysInParam)
+ return ERROR_INVALID_PARAMETER;
+
+ sysparam = *sysInParam;
+
+ if (sysparam.params & SPI_MASK_SET_HIGH_CONTRAST)
+ {
+ sysparam.param = SPI_SET_HIGH_CONTRAST;
+
+ if ((error = rail_send_client_sysparam(context, &sysparam)))
+ {
+ WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
+ return error;
+ }
+ }
+
+ if (sysparam.params & SPI_MASK_TASKBAR_POS)
+ {
+ sysparam.param = SPI_TASKBAR_POS;
+
+ if ((error = rail_send_client_sysparam(context, &sysparam)))
+ {
+ WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
+ return error;
+ }
+ }
+
+ if (sysparam.params & SPI_MASK_SET_MOUSE_BUTTON_SWAP)
+ {
+ sysparam.param = SPI_SET_MOUSE_BUTTON_SWAP;
+
+ if ((error = rail_send_client_sysparam(context, &sysparam)))
+ {
+ WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
+ return error;
+ }
+ }
+
+ if (sysparam.params & SPI_MASK_SET_KEYBOARD_PREF)
+ {
+ sysparam.param = SPI_SET_KEYBOARD_PREF;
+
+ if ((error = rail_send_client_sysparam(context, &sysparam)))
+ {
+ WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
+ return error;
+ }
+ }
+
+ if (sysparam.params & SPI_MASK_SET_DRAG_FULL_WINDOWS)
+ {
+ sysparam.param = SPI_SET_DRAG_FULL_WINDOWS;
+
+ if ((error = rail_send_client_sysparam(context, &sysparam)))
+ {
+ WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
+ return error;
+ }
+ }
+
+ if (sysparam.params & SPI_MASK_SET_KEYBOARD_CUES)
+ {
+ sysparam.param = SPI_SET_KEYBOARD_CUES;
+
+ if ((error = rail_send_client_sysparam(context, &sysparam)))
+ {
+ WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
+ return error;
+ }
+ }
+
+ if (sysparam.params & SPI_MASK_SET_WORK_AREA)
+ {
+ sysparam.param = SPI_SET_WORK_AREA;
+
+ if ((error = rail_send_client_sysparam(context, &sysparam)))
+ {
+ WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
+ return error;
+ }
+ }
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_client_system_command(RailClientContext* context,
+ const RAIL_SYSCOMMAND_ORDER* syscommand)
+{
+ railPlugin* rail = NULL;
+
+ if (!context || !syscommand)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ return rail_send_client_syscommand_order(rail, syscommand);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_client_handshake(RailClientContext* context, const RAIL_HANDSHAKE_ORDER* handshake)
+{
+ railPlugin* rail = NULL;
+
+ if (!context || !handshake)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ return rail_send_handshake_order(rail, handshake);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_client_notify_event(RailClientContext* context,
+ const RAIL_NOTIFY_EVENT_ORDER* notifyEvent)
+{
+ railPlugin* rail = NULL;
+
+ if (!context || !notifyEvent)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ return rail_send_client_notify_event_order(rail, notifyEvent);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_client_window_move(RailClientContext* context,
+ const RAIL_WINDOW_MOVE_ORDER* windowMove)
+{
+ railPlugin* rail = NULL;
+
+ if (!context || !windowMove)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ return rail_send_client_window_move_order(rail, windowMove);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_client_information(RailClientContext* context,
+ const RAIL_CLIENT_STATUS_ORDER* clientStatus)
+{
+ railPlugin* rail = NULL;
+
+ if (!context || !clientStatus)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ return rail_send_client_status_order(rail, clientStatus);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_client_system_menu(RailClientContext* context, const RAIL_SYSMENU_ORDER* sysmenu)
+{
+ railPlugin* rail = NULL;
+
+ if (!context || !sysmenu)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ return rail_send_client_sysmenu_order(rail, sysmenu);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_client_language_bar_info(RailClientContext* context,
+ const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
+{
+ railPlugin* rail = NULL;
+
+ if (!context || !langBarInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ return rail_send_client_langbar_info_order(rail, langBarInfo);
+}
+
+static UINT rail_client_language_ime_info(RailClientContext* context,
+ const RAIL_LANGUAGEIME_INFO_ORDER* langImeInfo)
+{
+ railPlugin* rail = NULL;
+
+ if (!context || !langImeInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ return rail_send_client_languageime_info_order(rail, langImeInfo);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_client_get_appid_request(RailClientContext* context,
+ const RAIL_GET_APPID_REQ_ORDER* getAppIdReq)
+{
+ railPlugin* rail = NULL;
+
+ if (!context || !getAppIdReq || !context->handle)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ return rail_send_client_get_appid_req_order(rail, getAppIdReq);
+}
+
+static UINT rail_client_compartment_info(RailClientContext* context,
+ const RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo)
+{
+ railPlugin* rail = NULL;
+
+ if (!context || !compartmentInfo || !context->handle)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ return rail_send_client_compartment_info_order(rail, compartmentInfo);
+}
+
+static UINT rail_client_cloak(RailClientContext* context, const RAIL_CLOAK* cloak)
+{
+ railPlugin* rail = NULL;
+
+ if (!context || !cloak || !context->handle)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ return rail_send_client_cloak_order(rail, cloak);
+}
+
+static UINT rail_client_snap_arrange(RailClientContext* context, const RAIL_SNAP_ARRANGE* snap)
+{
+ railPlugin* rail = NULL;
+
+ if (!context || !snap || !context->handle)
+ return ERROR_INVALID_PARAMETER;
+
+ rail = (railPlugin*)context->handle;
+ return rail_send_client_snap_arrange_order(rail, snap);
+}
+
+static VOID VCAPITYPE rail_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
+ UINT event, LPVOID pData,
+ UINT32 dataLength, UINT32 totalLength,
+ UINT32 dataFlags)
+{
+ UINT error = CHANNEL_RC_OK;
+ railPlugin* rail = (railPlugin*)lpUserParam;
+
+ switch (event)
+ {
+ case CHANNEL_EVENT_DATA_RECEIVED:
+ if (!rail || (rail->OpenHandle != openHandle))
+ {
+ WLog_ERR(TAG, "error no match");
+ return;
+ }
+
+ if ((error = channel_client_post_message(rail->MsgsHandle, pData, dataLength,
+ totalLength, dataFlags)))
+ {
+ WLog_ERR(TAG,
+ "rail_virtual_channel_event_data_received"
+ " failed with error %" PRIu32 "!",
+ error);
+ }
+
+ break;
+
+ case CHANNEL_EVENT_WRITE_CANCELLED:
+ case CHANNEL_EVENT_WRITE_COMPLETE:
+ {
+ wStream* s = (wStream*)pData;
+ Stream_Free(s, TRUE);
+ }
+ break;
+
+ case CHANNEL_EVENT_USER:
+ break;
+ }
+
+ if (error && rail && rail->rdpcontext)
+ setChannelError(rail->rdpcontext, error,
+ "rail_virtual_channel_open_event reported an error");
+
+ return;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_virtual_channel_event_connected(railPlugin* rail, LPVOID pData, UINT32 dataLength)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ UINT status = CHANNEL_RC_OK;
+
+ WINPR_ASSERT(rail);
+
+ if (context)
+ {
+ IFCALLRET(context->OnOpen, status, context, &rail->sendHandshake);
+
+ if (status != CHANNEL_RC_OK)
+ WLog_ERR(TAG, "context->OnOpen failed with %s [%08" PRIX32 "]",
+ WTSErrorToString(status), status);
+ }
+ rail->MsgsHandle = channel_client_create_handler(rail->rdpcontext, rail, rail_order_recv,
+ RAIL_SVC_CHANNEL_NAME);
+ if (!rail->MsgsHandle)
+ return ERROR_INTERNAL_ERROR;
+
+ return rail->channelEntryPoints.pVirtualChannelOpenEx(rail->InitHandle, &rail->OpenHandle,
+ rail->channelDef.name,
+ rail_virtual_channel_open_event_ex);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_virtual_channel_event_disconnected(railPlugin* rail)
+{
+ UINT rc = 0;
+
+ channel_client_quit_handler(rail->MsgsHandle);
+ if (rail->OpenHandle == 0)
+ return CHANNEL_RC_OK;
+
+ WINPR_ASSERT(rail->channelEntryPoints.pVirtualChannelCloseEx);
+ rc = rail->channelEntryPoints.pVirtualChannelCloseEx(rail->InitHandle, rail->OpenHandle);
+
+ if (CHANNEL_RC_OK != rc)
+ {
+ WLog_ERR(TAG, "pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]", WTSErrorToString(rc),
+ rc);
+ return rc;
+ }
+
+ rail->OpenHandle = 0;
+
+ return CHANNEL_RC_OK;
+}
+
+static void rail_virtual_channel_event_terminated(railPlugin* rail)
+{
+ rail->InitHandle = 0;
+ free(rail->context);
+ free(rail);
+}
+
+static VOID VCAPITYPE rail_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
+ UINT event, LPVOID pData, UINT dataLength)
+{
+ UINT error = CHANNEL_RC_OK;
+ railPlugin* rail = (railPlugin*)lpUserParam;
+
+ if (!rail || (rail->InitHandle != pInitHandle))
+ {
+ WLog_ERR(TAG, "error no match");
+ return;
+ }
+
+ switch (event)
+ {
+ case CHANNEL_EVENT_CONNECTED:
+ if ((error = rail_virtual_channel_event_connected(rail, pData, dataLength)))
+ WLog_ERR(TAG, "rail_virtual_channel_event_connected failed with error %" PRIu32 "!",
+ error);
+
+ break;
+
+ case CHANNEL_EVENT_DISCONNECTED:
+ if ((error = rail_virtual_channel_event_disconnected(rail)))
+ WLog_ERR(TAG,
+ "rail_virtual_channel_event_disconnected failed with error %" PRIu32 "!",
+ error);
+
+ break;
+
+ case CHANNEL_EVENT_TERMINATED:
+ rail_virtual_channel_event_terminated(rail);
+ break;
+
+ case CHANNEL_EVENT_ATTACHED:
+ case CHANNEL_EVENT_DETACHED:
+ default:
+ break;
+ }
+
+ if (error && rail->rdpcontext)
+ setChannelError(rail->rdpcontext, error,
+ "rail_virtual_channel_init_event_ex reported an error");
+}
+
+/* rail is always built-in */
+#define VirtualChannelEntryEx rail_VirtualChannelEntryEx
+
+FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
+ PVOID pInitHandle))
+{
+ UINT rc = 0;
+ railPlugin* rail = NULL;
+ RailClientContext* context = NULL;
+ CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx = NULL;
+ BOOL isFreerdp = FALSE;
+ rail = (railPlugin*)calloc(1, sizeof(railPlugin));
+
+ if (!rail)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ return FALSE;
+ }
+
+ /* Default to automatically replying to server handshakes */
+ rail->sendHandshake = TRUE;
+ rail->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
+ CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL;
+ sprintf_s(rail->channelDef.name, ARRAYSIZE(rail->channelDef.name), RAIL_SVC_CHANNEL_NAME);
+ pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
+
+ if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
+ (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
+ {
+ context = (RailClientContext*)calloc(1, sizeof(RailClientContext));
+
+ if (!context)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ free(rail);
+ return FALSE;
+ }
+
+ context->handle = (void*)rail;
+ context->custom = NULL;
+ context->ClientExecute = rail_client_execute;
+ context->ClientActivate = rail_client_activate;
+ context->ClientSystemParam = rail_client_system_param;
+ context->ClientSystemCommand = rail_client_system_command;
+ context->ClientHandshake = rail_client_handshake;
+ context->ClientNotifyEvent = rail_client_notify_event;
+ context->ClientWindowMove = rail_client_window_move;
+ context->ClientInformation = rail_client_information;
+ context->ClientSystemMenu = rail_client_system_menu;
+ context->ClientLanguageBarInfo = rail_client_language_bar_info;
+ context->ClientLanguageIMEInfo = rail_client_language_ime_info;
+ context->ClientGetAppIdRequest = rail_client_get_appid_request;
+ context->ClientSnapArrange = rail_client_snap_arrange;
+ context->ClientCloak = rail_client_cloak;
+ context->ClientCompartmentInfo = rail_client_compartment_info;
+ rail->rdpcontext = pEntryPointsEx->context;
+ rail->context = context;
+ isFreerdp = TRUE;
+ }
+
+ rail->log = WLog_Get("com.freerdp.channels.rail.client");
+ WLog_Print(rail->log, WLOG_DEBUG, "VirtualChannelEntryEx");
+ CopyMemory(&(rail->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
+ rail->InitHandle = pInitHandle;
+ rc = rail->channelEntryPoints.pVirtualChannelInitEx(
+ rail, context, pInitHandle, &rail->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
+ rail_virtual_channel_init_event_ex);
+
+ if (CHANNEL_RC_OK != rc)
+ {
+ WLog_ERR(TAG, "failed with %s [%08" PRIX32 "]", WTSErrorToString(rc), rc);
+ goto error_out;
+ }
+
+ rail->channelEntryPoints.pInterface = context;
+ return TRUE;
+error_out:
+
+ if (isFreerdp)
+ free(rail->context);
+
+ free(rail);
+ return FALSE;
+}
diff --git a/channels/rail/client/rail_main.h b/channels/rail/client/rail_main.h
new file mode 100644
index 0000000..2cbc6c1
--- /dev/null
+++ b/channels/rail/client/rail_main.h
@@ -0,0 +1,60 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * RAIL Virtual Channel Plugin
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2011 Roman Barabanov <romanbarabanov@gmail.com>
+ * Copyright 2011 Vic Lee
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
+ *
+ * Licensed 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 FREERDP_CHANNEL_RAIL_CLIENT_MAIN_H
+#define FREERDP_CHANNEL_RAIL_CLIENT_MAIN_H
+
+#include <freerdp/rail.h>
+#include <freerdp/svc.h>
+#include <freerdp/addin.h>
+#include <freerdp/settings.h>
+#include <freerdp/client/rail.h>
+
+#include <winpr/crt.h>
+#include <winpr/wlog.h>
+#include <winpr/stream.h>
+
+#include "../rail_common.h"
+
+typedef struct
+{
+ CHANNEL_DEF channelDef;
+ CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
+
+ RailClientContext* context;
+
+ wLog* log;
+ void* InitHandle;
+ DWORD OpenHandle;
+ void* MsgsHandle;
+ rdpContext* rdpcontext;
+ DWORD channelBuildNumber;
+ DWORD channelFlags;
+ RAIL_CLIENT_STATUS_ORDER clientStatus;
+ BOOL sendHandshake;
+} railPlugin;
+
+RailClientContext* rail_get_client_interface(railPlugin* rail);
+UINT rail_send_channel_data(railPlugin* rail, wStream* s);
+
+#endif /* FREERDP_CHANNEL_RAIL_CLIENT_MAIN_H */
diff --git a/channels/rail/client/rail_orders.c b/channels/rail/client/rail_orders.c
new file mode 100644
index 0000000..7ac432e
--- /dev/null
+++ b/channels/rail/client/rail_orders.c
@@ -0,0 +1,1564 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Remote Applications Integrated Locally (RAIL) Orders
+ *
+ * Copyright 2009 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2011 Roman Barabanov <romanbarabanov@gmail.com>
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
+ * Copyright 2017 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2017 Thincast Technologies GmbH
+ *
+ * Licensed 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 <freerdp/config.h>
+
+#include <winpr/crt.h>
+
+#include <freerdp/channels/log.h>
+#include <freerdp/freerdp.h>
+
+#include "rail_orders.h"
+
+static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask);
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType)
+{
+ char buffer[128] = { 0 };
+ UINT16 orderLength = 0;
+
+ if (!rail || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ orderLength = (UINT16)Stream_GetPosition(s);
+ Stream_SetPosition(s, 0);
+ rail_write_pdu_header(s, orderType, orderLength);
+ Stream_SetPosition(s, orderLength);
+ WLog_Print(rail->log, WLOG_DEBUG, "Sending %s PDU, length: %" PRIu16 "",
+ rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
+ return rail_send_channel_data(rail, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_server_exec_result_order(wStream* s, RAIL_EXEC_RESULT_ORDER* execResult)
+{
+ if (!s || !execResult)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_EXEC_RESULT_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT16(s, execResult->flags); /* flags (2 bytes) */
+ Stream_Read_UINT16(s, execResult->execResult); /* execResult (2 bytes) */
+ Stream_Read_UINT32(s, execResult->rawResult); /* rawResult (4 bytes) */
+ Stream_Seek_UINT16(s); /* padding (2 bytes) */
+ return rail_read_unicode_string(s, &execResult->exeOrFile)
+ ? CHANNEL_RC_OK
+ : ERROR_INTERNAL_ERROR; /* exeOrFile */
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_server_minmaxinfo_order(wStream* s, RAIL_MINMAXINFO_ORDER* minmaxinfo)
+{
+ if (!s || !minmaxinfo)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_MINMAXINFO_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, minmaxinfo->windowId); /* windowId (4 bytes) */
+ Stream_Read_INT16(s, minmaxinfo->maxWidth); /* maxWidth (2 bytes) */
+ Stream_Read_INT16(s, minmaxinfo->maxHeight); /* maxHeight (2 bytes) */
+ Stream_Read_INT16(s, minmaxinfo->maxPosX); /* maxPosX (2 bytes) */
+ Stream_Read_INT16(s, minmaxinfo->maxPosY); /* maxPosY (2 bytes) */
+ Stream_Read_INT16(s, minmaxinfo->minTrackWidth); /* minTrackWidth (2 bytes) */
+ Stream_Read_INT16(s, minmaxinfo->minTrackHeight); /* minTrackHeight (2 bytes) */
+ Stream_Read_INT16(s, minmaxinfo->maxTrackWidth); /* maxTrackWidth (2 bytes) */
+ Stream_Read_INT16(s, minmaxinfo->maxTrackHeight); /* maxTrackHeight (2 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_server_localmovesize_order(wStream* s,
+ RAIL_LOCALMOVESIZE_ORDER* localMoveSize)
+{
+ UINT16 isMoveSizeStart = 0;
+
+ if (!s || !localMoveSize)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_LOCALMOVESIZE_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, localMoveSize->windowId); /* windowId (4 bytes) */
+ Stream_Read_UINT16(s, isMoveSizeStart); /* isMoveSizeStart (2 bytes) */
+ localMoveSize->isMoveSizeStart = (isMoveSizeStart != 0) ? TRUE : FALSE;
+ Stream_Read_UINT16(s, localMoveSize->moveSizeType); /* moveSizeType (2 bytes) */
+ Stream_Read_INT16(s, localMoveSize->posX); /* posX (2 bytes) */
+ Stream_Read_INT16(s, localMoveSize->posY); /* posY (2 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_server_get_appid_resp_order(wStream* s,
+ RAIL_GET_APPID_RESP_ORDER* getAppidResp)
+{
+ if (!s || !getAppidResp)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_GET_APPID_RESP_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, getAppidResp->windowId); /* windowId (4 bytes) */
+ Stream_Read_UTF16_String(
+ s, getAppidResp->applicationId,
+ ARRAYSIZE(getAppidResp->applicationId)); /* applicationId (260 UNICODE chars) */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_langbar_info_order(wStream* s, RAIL_LANGBAR_INFO_ORDER* langbarInfo)
+{
+ if (!s || !langbarInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_LANGBAR_INFO_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, langbarInfo->languageBarStatus); /* languageBarStatus (4 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+static UINT rail_write_client_status_order(wStream* s, const RAIL_CLIENT_STATUS_ORDER* clientStatus)
+{
+ if (!s || !clientStatus)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, clientStatus->flags); /* flags (4 bytes) */
+ return ERROR_SUCCESS;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_write_client_exec_order(wStream* s, UINT16 flags,
+ const RAIL_UNICODE_STRING* exeOrFile,
+ const RAIL_UNICODE_STRING* workingDir,
+ const RAIL_UNICODE_STRING* arguments)
+{
+ UINT error = 0;
+
+ if (!s || !exeOrFile || !workingDir || !arguments)
+ return ERROR_INVALID_PARAMETER;
+
+ /* [MS-RDPERP] 2.2.2.3.1 Client Execute PDU (TS_RAIL_ORDER_EXEC)
+ * Check argument limits */
+ if ((exeOrFile->length > 520) || (workingDir->length > 520) || (arguments->length > 16000))
+ {
+ WLog_ERR(TAG,
+ "TS_RAIL_ORDER_EXEC argument limits exceeded: ExeOrFile=%" PRIu16
+ " [max=520], WorkingDir=%" PRIu16 " [max=520], Arguments=%" PRIu16 " [max=16000]",
+ exeOrFile->length, workingDir->length, arguments->length);
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ Stream_Write_UINT16(s, flags); /* flags (2 bytes) */
+ Stream_Write_UINT16(s, exeOrFile->length); /* exeOrFileLength (2 bytes) */
+ Stream_Write_UINT16(s, workingDir->length); /* workingDirLength (2 bytes) */
+ Stream_Write_UINT16(s, arguments->length); /* argumentsLength (2 bytes) */
+
+ if ((error = rail_write_unicode_string_value(s, exeOrFile)))
+ {
+ WLog_ERR(TAG, "rail_write_unicode_string_value failed with error %" PRIu32 "", error);
+ return error;
+ }
+
+ if ((error = rail_write_unicode_string_value(s, workingDir)))
+ {
+ WLog_ERR(TAG, "rail_write_unicode_string_value failed with error %" PRIu32 "", error);
+ return error;
+ }
+
+ if ((error = rail_write_unicode_string_value(s, arguments)))
+ {
+ WLog_ERR(TAG, "rail_write_unicode_string_value failed with error %" PRIu32 "", error);
+ return error;
+ }
+
+ return error;
+}
+
+static UINT rail_write_client_activate_order(wStream* s, const RAIL_ACTIVATE_ORDER* activate)
+{
+ BYTE enabled = 0;
+
+ if (!s || !activate)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, activate->windowId); /* windowId (4 bytes) */
+ enabled = activate->enabled ? 1 : 0;
+ Stream_Write_UINT8(s, enabled); /* enabled (1 byte) */
+ return ERROR_SUCCESS;
+}
+
+static UINT rail_write_client_sysmenu_order(wStream* s, const RAIL_SYSMENU_ORDER* sysmenu)
+{
+ if (!s || !sysmenu)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, sysmenu->windowId); /* windowId (4 bytes) */
+ Stream_Write_INT16(s, sysmenu->left); /* left (2 bytes) */
+ Stream_Write_INT16(s, sysmenu->top); /* top (2 bytes) */
+ return ERROR_SUCCESS;
+}
+
+static UINT rail_write_client_syscommand_order(wStream* s, const RAIL_SYSCOMMAND_ORDER* syscommand)
+{
+ if (!s || !syscommand)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, syscommand->windowId); /* windowId (4 bytes) */
+ Stream_Write_UINT16(s, syscommand->command); /* command (2 bytes) */
+ return ERROR_SUCCESS;
+}
+
+static UINT rail_write_client_notify_event_order(wStream* s,
+ const RAIL_NOTIFY_EVENT_ORDER* notifyEvent)
+{
+ if (!s || !notifyEvent)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, notifyEvent->windowId); /* windowId (4 bytes) */
+ Stream_Write_UINT32(s, notifyEvent->notifyIconId); /* notifyIconId (4 bytes) */
+ Stream_Write_UINT32(s, notifyEvent->message); /* notifyIconId (4 bytes) */
+ return ERROR_SUCCESS;
+}
+
+static UINT rail_write_client_window_move_order(wStream* s,
+ const RAIL_WINDOW_MOVE_ORDER* windowMove)
+{
+ if (!s || !windowMove)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, windowMove->windowId); /* windowId (4 bytes) */
+ Stream_Write_INT16(s, windowMove->left); /* left (2 bytes) */
+ Stream_Write_INT16(s, windowMove->top); /* top (2 bytes) */
+ Stream_Write_INT16(s, windowMove->right); /* right (2 bytes) */
+ Stream_Write_INT16(s, windowMove->bottom); /* bottom (2 bytes) */
+ return ERROR_SUCCESS;
+}
+
+static UINT rail_write_client_get_appid_req_order(wStream* s,
+ const RAIL_GET_APPID_REQ_ORDER* getAppidReq)
+{
+ if (!s || !getAppidReq)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, getAppidReq->windowId); /* windowId (4 bytes) */
+ return ERROR_SUCCESS;
+}
+
+static UINT rail_write_langbar_info_order(wStream* s, const RAIL_LANGBAR_INFO_ORDER* langbarInfo)
+{
+ if (!s || !langbarInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, langbarInfo->languageBarStatus); /* languageBarStatus (4 bytes) */
+ return ERROR_SUCCESS;
+}
+
+static UINT rail_write_languageime_info_order(wStream* s,
+ const RAIL_LANGUAGEIME_INFO_ORDER* langImeInfo)
+{
+ if (!s || !langImeInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, langImeInfo->ProfileType);
+ Stream_Write_UINT16(s, langImeInfo->LanguageID);
+ Stream_Write(s, &langImeInfo->LanguageProfileCLSID, sizeof(langImeInfo->LanguageProfileCLSID));
+ Stream_Write(s, &langImeInfo->ProfileGUID, sizeof(langImeInfo->ProfileGUID));
+ Stream_Write_UINT32(s, langImeInfo->KeyboardLayout);
+ return ERROR_SUCCESS;
+}
+
+static UINT rail_write_compartment_info_order(wStream* s,
+ const RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo)
+{
+ if (!s || !compartmentInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, compartmentInfo->ImeState);
+ Stream_Write_UINT32(s, compartmentInfo->ImeConvMode);
+ Stream_Write_UINT32(s, compartmentInfo->ImeSentenceMode);
+ Stream_Write_UINT32(s, compartmentInfo->KanaMode);
+ return ERROR_SUCCESS;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_handshake_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_HANDSHAKE_ORDER serverHandshake = { 0 };
+ UINT error = 0;
+
+ if (!context || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_handshake_order(s, &serverHandshake)))
+ {
+ WLog_ERR(TAG, "rail_read_handshake_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ rail->channelBuildNumber = serverHandshake.buildNumber;
+
+ if (rail->sendHandshake)
+ {
+ RAIL_HANDSHAKE_ORDER clientHandshake = { 0 };
+ clientHandshake.buildNumber = 0x00001DB0;
+ error = context->ClientHandshake(context, &clientHandshake);
+ }
+
+ if (error != CHANNEL_RC_OK)
+ return error;
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ServerHandshake, error, context, &serverHandshake);
+
+ if (error)
+ WLog_ERR(TAG, "context.ServerHandshake failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+static UINT rail_read_compartment_info_order(wStream* s,
+ RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_COMPARTMENT_INFO_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, compartmentInfo->ImeState); /* ImeState (4 bytes) */
+ Stream_Read_UINT32(s, compartmentInfo->ImeConvMode); /* ImeConvMode (4 bytes) */
+ Stream_Read_UINT32(s, compartmentInfo->ImeSentenceMode); /* ImeSentenceMode (4 bytes) */
+ Stream_Read_UINT32(s, compartmentInfo->KanaMode); /* KANAMode (4 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+static UINT rail_recv_compartmentinfo_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_COMPARTMENT_INFO_ORDER pdu = { 0 };
+ UINT error = 0;
+
+ if (!context || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED))
+ return ERROR_BAD_CONFIGURATION;
+
+ if ((error = rail_read_compartment_info_order(s, &pdu)))
+ return error;
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ClientCompartmentInfo, error, context, &pdu);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientCompartmentInfo failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask)
+{
+ UINT32 supported = 0;
+ UINT32 masked = 0;
+
+ if (!context || !context->settings)
+ return FALSE;
+
+ const UINT32 level =
+ freerdp_settings_get_uint32(context->settings, FreeRDP_RemoteApplicationSupportLevel);
+ const UINT32 mask =
+ freerdp_settings_get_uint32(context->settings, FreeRDP_RemoteApplicationSupportMask);
+ supported = level & mask;
+ masked = (supported & featureMask);
+
+ if (masked != featureMask)
+ {
+ char maskstr[256] = { 0 };
+ char actualstr[256] = { 0 };
+
+ WLog_WARN(TAG, "have %s, require %s",
+ freerdp_rail_support_flags_to_string(supported, actualstr, sizeof(actualstr)),
+ freerdp_rail_support_flags_to_string(featureMask, maskstr, sizeof(maskstr)));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_handshake_ex_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_HANDSHAKE_EX_ORDER serverHandshake = { 0 };
+ UINT error = 0;
+
+ if (!rail || !context || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED))
+ return ERROR_BAD_CONFIGURATION;
+
+ if ((error = rail_read_handshake_ex_order(s, &serverHandshake)))
+ {
+ WLog_ERR(TAG, "rail_read_handshake_ex_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ rail->channelBuildNumber = serverHandshake.buildNumber;
+ rail->channelFlags = serverHandshake.railHandshakeFlags;
+
+ if (rail->sendHandshake)
+ {
+ RAIL_HANDSHAKE_ORDER clientHandshake = { 0 };
+ clientHandshake.buildNumber = 0x00001DB0;
+ /* 2.2.2.2.3 HandshakeEx PDU (TS_RAIL_ORDER_HANDSHAKE_EX)
+ * Client response is really a Handshake PDU */
+ error = context->ClientHandshake(context, &clientHandshake);
+ }
+
+ if (error != CHANNEL_RC_OK)
+ return error;
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ServerHandshakeEx, error, context, &serverHandshake);
+
+ if (error)
+ WLog_ERR(TAG, "context.ServerHandshakeEx failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_exec_result_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_EXEC_RESULT_ORDER execResult = { 0 };
+ UINT error = 0;
+
+ if (!context || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_server_exec_result_order(s, &execResult)))
+ {
+ WLog_ERR(TAG, "rail_read_server_exec_result_order failed with error %" PRIu32 "!", error);
+ goto fail;
+ }
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ServerExecuteResult, error, context, &execResult);
+
+ if (error)
+ WLog_ERR(TAG, "context.ServerExecuteResult failed with error %" PRIu32 "", error);
+ }
+
+fail:
+ free(execResult.exeOrFile.string);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_server_sysparam_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_SYSPARAM_ORDER sysparam;
+ UINT error = 0;
+ BOOL extendedSpiSupported = 0;
+
+ if (!context || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ extendedSpiSupported = rail_is_extended_spi_supported(rail->channelFlags);
+ if ((error = rail_read_sysparam_order(s, &sysparam, extendedSpiSupported)))
+ {
+ WLog_ERR(TAG, "rail_read_sysparam_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ServerSystemParam, error, context, &sysparam);
+
+ if (error)
+ WLog_ERR(TAG, "context.ServerSystemParam failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_server_minmaxinfo_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_MINMAXINFO_ORDER minMaxInfo = { 0 };
+ UINT error = 0;
+
+ if (!context || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_server_minmaxinfo_order(s, &minMaxInfo)))
+ {
+ WLog_ERR(TAG, "rail_read_server_minmaxinfo_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ServerMinMaxInfo, error, context, &minMaxInfo);
+
+ if (error)
+ WLog_ERR(TAG, "context.ServerMinMaxInfo failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_server_localmovesize_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_LOCALMOVESIZE_ORDER localMoveSize = { 0 };
+ UINT error = 0;
+
+ if (!context || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_server_localmovesize_order(s, &localMoveSize)))
+ {
+ WLog_ERR(TAG, "rail_read_server_localmovesize_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ServerLocalMoveSize, error, context, &localMoveSize);
+
+ if (error)
+ WLog_ERR(TAG, "context.ServerLocalMoveSize failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_server_get_appid_resp_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_GET_APPID_RESP_ORDER getAppIdResp = { 0 };
+ UINT error = 0;
+
+ if (!context || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_server_get_appid_resp_order(s, &getAppIdResp)))
+ {
+ WLog_ERR(TAG, "rail_read_server_get_appid_resp_order failed with error %" PRIu32 "!",
+ error);
+ return error;
+ }
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ServerGetAppIdResponse, error, context, &getAppIdResp);
+
+ if (error)
+ WLog_ERR(TAG, "context.ServerGetAppIdResponse failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_langbar_info_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_LANGBAR_INFO_ORDER langBarInfo = { 0 };
+ UINT error = 0;
+
+ if (!context)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED))
+ return ERROR_BAD_CONFIGURATION;
+
+ if ((error = rail_read_langbar_info_order(s, &langBarInfo)))
+ {
+ WLog_ERR(TAG, "rail_read_langbar_info_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ServerLanguageBarInfo, error, context, &langBarInfo);
+
+ if (error)
+ WLog_ERR(TAG, "context.ServerLanguageBarInfo failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+static UINT rail_read_taskbar_info_order(wStream* s, RAIL_TASKBAR_INFO_ORDER* taskbarInfo)
+{
+ if (!s || !taskbarInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_TASKBAR_INFO_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, taskbarInfo->TaskbarMessage);
+ Stream_Read_UINT32(s, taskbarInfo->WindowIdTab);
+ Stream_Read_UINT32(s, taskbarInfo->Body);
+ return CHANNEL_RC_OK;
+}
+
+static UINT rail_recv_taskbar_info_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_TASKBAR_INFO_ORDER taskBarInfo = { 0 };
+ UINT error = 0;
+
+ if (!context)
+ return ERROR_INVALID_PARAMETER;
+
+ /* 2.2.2.14.1 Taskbar Tab Info PDU (TS_RAIL_ORDER_TASKBARINFO)
+ * server -> client message only supported if announced. */
+ if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_SHELL_INTEGRATION_SUPPORTED))
+ return ERROR_BAD_CONFIGURATION;
+
+ if ((error = rail_read_taskbar_info_order(s, &taskBarInfo)))
+ {
+ WLog_ERR(TAG, "rail_read_langbar_info_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ServerTaskBarInfo, error, context, &taskBarInfo);
+
+ if (error)
+ WLog_ERR(TAG, "context.ServerTaskBarInfo failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+static UINT rail_read_zorder_sync_order(wStream* s, RAIL_ZORDER_SYNC* zorder)
+{
+ if (!s || !zorder)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_Z_ORDER_SYNC_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, zorder->windowIdMarker);
+ return CHANNEL_RC_OK;
+}
+
+static UINT rail_recv_zorder_sync_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_ZORDER_SYNC zorder = { 0 };
+ UINT error = 0;
+
+ if (!context)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((rail->clientStatus.flags & TS_RAIL_CLIENTSTATUS_ZORDER_SYNC) == 0)
+ return ERROR_INVALID_DATA;
+
+ if ((error = rail_read_zorder_sync_order(s, &zorder)))
+ {
+ WLog_ERR(TAG, "rail_read_zorder_sync_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ServerZOrderSync, error, context, &zorder);
+
+ if (error)
+ WLog_ERR(TAG, "context.ServerZOrderSync failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+static UINT rail_read_cloak_order(wStream* s, RAIL_CLOAK* cloak)
+{
+ BYTE cloaked = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_CLOAK_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, cloak->windowId); /* WindowId (4 bytes) */
+ Stream_Read_UINT8(s, cloaked); /* Cloaked (1 byte) */
+ cloak->cloak = (cloaked != 0) ? TRUE : FALSE;
+ return CHANNEL_RC_OK;
+}
+
+static UINT rail_recv_cloak_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_CLOAK cloak = { 0 };
+ UINT error = 0;
+
+ if (!context)
+ return ERROR_INVALID_PARAMETER;
+
+ /* 2.2.2.12.1 Window Cloak State Change PDU (TS_RAIL_ORDER_CLOAK)
+ * server -> client message only supported if announced. */
+ if ((rail->clientStatus.flags & TS_RAIL_CLIENTSTATUS_BIDIRECTIONAL_CLOAK_SUPPORTED) == 0)
+ return ERROR_INVALID_DATA;
+
+ if ((error = rail_read_cloak_order(s, &cloak)))
+ {
+ WLog_ERR(TAG, "rail_read_zorder_sync_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ServerCloak, error, context, &cloak);
+
+ if (error)
+ WLog_ERR(TAG, "context.ServerZOrderSync failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+static UINT rail_read_power_display_request_order(wStream* s, RAIL_POWER_DISPLAY_REQUEST* power)
+{
+ UINT32 active = 0;
+
+ if (!s || !power)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_POWER_DISPLAY_REQUEST_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, active);
+ power->active = active != 0;
+ return CHANNEL_RC_OK;
+}
+
+static UINT rail_recv_power_display_request_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_POWER_DISPLAY_REQUEST power = { 0 };
+ UINT error = 0;
+
+ if (!context)
+ return ERROR_INVALID_PARAMETER;
+
+ /* 2.2.2.13.1 Power Display Request PDU(TS_RAIL_ORDER_POWER_DISPLAY_REQUEST)
+ */
+ if ((rail->clientStatus.flags & TS_RAIL_CLIENTSTATUS_POWER_DISPLAY_REQUEST_SUPPORTED) == 0)
+ return ERROR_INVALID_DATA;
+
+ if ((error = rail_read_power_display_request_order(s, &power)))
+ {
+ WLog_ERR(TAG, "rail_read_zorder_sync_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ServerPowerDisplayRequest, error, context, &power);
+
+ if (error)
+ WLog_ERR(TAG, "context.ServerPowerDisplayRequest failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+static UINT rail_read_get_application_id_extended_response_order(wStream* s,
+ RAIL_GET_APPID_RESP_EX* id)
+{
+ if (!s || !id)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, id->windowID);
+
+ if (!Stream_Read_UTF16_String(s, id->applicationID, ARRAYSIZE(id->applicationID)))
+ return ERROR_INVALID_DATA;
+
+ if (_wcsnlen(id->applicationID, ARRAYSIZE(id->applicationID)) >= ARRAYSIZE(id->applicationID))
+ return ERROR_INVALID_DATA;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, id->processId);
+
+ if (!Stream_Read_UTF16_String(s, id->processImageName, ARRAYSIZE(id->processImageName)))
+ return ERROR_INVALID_DATA;
+
+ if (_wcsnlen(id->applicationID, ARRAYSIZE(id->processImageName)) >=
+ ARRAYSIZE(id->processImageName))
+ return ERROR_INVALID_DATA;
+
+ return CHANNEL_RC_OK;
+}
+
+static UINT rail_recv_get_application_id_extended_response_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ RAIL_GET_APPID_RESP_EX id = { 0 };
+ UINT error = 0;
+
+ if (!context)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_get_application_id_extended_response_order(s, &id)))
+ {
+ WLog_ERR(TAG,
+ "rail_read_get_application_id_extended_response_order failed with error %" PRIu32
+ "!",
+ error);
+ return error;
+ }
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ServerGetAppidResponseExtended, error, context, &id);
+
+ if (error)
+ WLog_ERR(TAG, "context.ServerGetAppidResponseExtended failed with error %" PRIu32 "",
+ error);
+ }
+
+ return error;
+}
+
+static UINT rail_read_textscaleinfo_order(wStream* s, UINT32* pTextScaleFactor)
+{
+ WINPR_ASSERT(pTextScaleFactor);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, *pTextScaleFactor);
+ return CHANNEL_RC_OK;
+}
+
+static UINT rail_recv_textscaleinfo_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ UINT32 TextScaleFactor = 0;
+ UINT error = 0;
+
+ if (!context)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_textscaleinfo_order(s, &TextScaleFactor)))
+ return error;
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ClientTextScale, error, context, TextScaleFactor);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientTextScale failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+static UINT rail_read_caretblinkinfo_order(wStream* s, UINT32* pCaretBlinkRate)
+{
+ WINPR_ASSERT(pCaretBlinkRate);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, *pCaretBlinkRate);
+ return CHANNEL_RC_OK;
+}
+
+static UINT rail_recv_caretblinkinfo_order(railPlugin* rail, wStream* s)
+{
+ RailClientContext* context = rail_get_client_interface(rail);
+ UINT32 CaretBlinkRate = 0;
+ UINT error = 0;
+
+ if (!context)
+ return ERROR_INVALID_PARAMETER;
+ if ((error = rail_read_caretblinkinfo_order(s, &CaretBlinkRate)))
+ return error;
+
+ if (context->custom)
+ {
+ IFCALLRET(context->ClientCaretBlinkRate, error, context, CaretBlinkRate);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientCaretBlinkRate failed with error %" PRIu32 "", error);
+ }
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_order_recv(LPVOID userdata, wStream* s)
+{
+ char buffer[128] = { 0 };
+ railPlugin* rail = userdata;
+ UINT16 orderType = 0;
+ UINT16 orderLength = 0;
+ UINT error = CHANNEL_RC_OK;
+
+ if (!rail || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_pdu_header(s, &orderType, &orderLength)))
+ {
+ WLog_ERR(TAG, "rail_read_pdu_header failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ WLog_Print(rail->log, WLOG_DEBUG, "Received %s PDU, length:%" PRIu16 "",
+ rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
+
+ switch (orderType)
+ {
+ case TS_RAIL_ORDER_HANDSHAKE:
+ error = rail_recv_handshake_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_COMPARTMENTINFO:
+ error = rail_recv_compartmentinfo_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_HANDSHAKE_EX:
+ error = rail_recv_handshake_ex_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_EXEC_RESULT:
+ error = rail_recv_exec_result_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_SYSPARAM:
+ error = rail_recv_server_sysparam_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_MINMAXINFO:
+ error = rail_recv_server_minmaxinfo_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_LOCALMOVESIZE:
+ error = rail_recv_server_localmovesize_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_GET_APPID_RESP:
+ error = rail_recv_server_get_appid_resp_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_LANGBARINFO:
+ error = rail_recv_langbar_info_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_TASKBARINFO:
+ error = rail_recv_taskbar_info_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_ZORDER_SYNC:
+ error = rail_recv_zorder_sync_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_CLOAK:
+ error = rail_recv_cloak_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST:
+ error = rail_recv_power_display_request_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_GET_APPID_RESP_EX:
+ error = rail_recv_get_application_id_extended_response_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_TEXTSCALEINFO:
+ error = rail_recv_textscaleinfo_order(rail, s);
+ break;
+
+ case TS_RAIL_ORDER_CARETBLINKINFO:
+ error = rail_recv_caretblinkinfo_order(rail, s);
+ break;
+
+ default:
+ WLog_ERR(TAG, "Unknown RAIL PDU %s received.",
+ rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)));
+ return ERROR_INVALID_DATA;
+ }
+
+ if (error != CHANNEL_RC_OK)
+ {
+ char ebuffer[128] = { 0 };
+ WLog_Print(rail->log, WLOG_ERROR, "Failed to process rail %s PDU, length:%" PRIu16 "",
+ rail_get_order_type_string_full(orderType, ebuffer, sizeof(ebuffer)),
+ orderLength);
+ }
+
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_send_handshake_order(railPlugin* rail, const RAIL_HANDSHAKE_ORDER* handshake)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail || !handshake)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_HANDSHAKE_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_handshake_order(s, handshake);
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_HANDSHAKE);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_send_handshake_ex_order(railPlugin* rail, const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail || !handshakeEx)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_HANDSHAKE_EX_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_handshake_ex_order(s, handshakeEx);
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_HANDSHAKE_EX);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_send_client_status_order(railPlugin* rail, const RAIL_CLIENT_STATUS_ORDER* clientStatus)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail || !clientStatus)
+ return ERROR_INVALID_PARAMETER;
+
+ rail->clientStatus = *clientStatus;
+ s = rail_pdu_init(RAIL_CLIENT_STATUS_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ error = rail_write_client_status_order(s, clientStatus);
+
+ if (error == ERROR_SUCCESS)
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_CLIENTSTATUS);
+
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_send_client_exec_order(railPlugin* rail, UINT16 flags,
+ const RAIL_UNICODE_STRING* exeOrFile,
+ const RAIL_UNICODE_STRING* workingDir,
+ const RAIL_UNICODE_STRING* arguments)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+ size_t length = 0;
+
+ if (!rail || !exeOrFile || !workingDir || !arguments)
+ return ERROR_INVALID_PARAMETER;
+
+ length = RAIL_EXEC_ORDER_LENGTH + exeOrFile->length + workingDir->length + arguments->length;
+ s = rail_pdu_init(length);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ if ((error = rail_write_client_exec_order(s, flags, exeOrFile, workingDir, arguments)))
+ {
+ WLog_ERR(TAG, "rail_write_client_exec_order failed with error %" PRIu32 "!", error);
+ goto out;
+ }
+
+ if ((error = rail_send_pdu(rail, s, TS_RAIL_ORDER_EXEC)))
+ {
+ WLog_ERR(TAG, "rail_send_pdu failed with error %" PRIu32 "!", error);
+ goto out;
+ }
+
+out:
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_send_client_activate_order(railPlugin* rail, const RAIL_ACTIVATE_ORDER* activate)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail || !activate)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_ACTIVATE_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ error = rail_write_client_activate_order(s, activate);
+
+ if (error == ERROR_SUCCESS)
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_ACTIVATE);
+
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_send_client_sysmenu_order(railPlugin* rail, const RAIL_SYSMENU_ORDER* sysmenu)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail || !sysmenu)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_SYSMENU_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ error = rail_write_client_sysmenu_order(s, sysmenu);
+
+ if (error == ERROR_SUCCESS)
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_SYSMENU);
+
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_send_client_syscommand_order(railPlugin* rail, const RAIL_SYSCOMMAND_ORDER* syscommand)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail || !syscommand)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_SYSCOMMAND_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ error = rail_write_client_syscommand_order(s, syscommand);
+
+ if (error == ERROR_SUCCESS)
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_SYSCOMMAND);
+
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_send_client_notify_event_order(railPlugin* rail,
+ const RAIL_NOTIFY_EVENT_ORDER* notifyEvent)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail || !notifyEvent)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_NOTIFY_EVENT_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ error = rail_write_client_notify_event_order(s, notifyEvent);
+
+ if (ERROR_SUCCESS == error)
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_NOTIFY_EVENT);
+
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_send_client_window_move_order(railPlugin* rail, const RAIL_WINDOW_MOVE_ORDER* windowMove)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail || !windowMove)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_WINDOW_MOVE_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ error = rail_write_client_window_move_order(s, windowMove);
+
+ if (error == ERROR_SUCCESS)
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_WINDOWMOVE);
+
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_send_client_get_appid_req_order(railPlugin* rail,
+ const RAIL_GET_APPID_REQ_ORDER* getAppIdReq)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail || !getAppIdReq)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_GET_APPID_REQ_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ error = rail_write_client_get_appid_req_order(s, getAppIdReq);
+
+ if (error == ERROR_SUCCESS)
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_GET_APPID_REQ);
+
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_send_client_langbar_info_order(railPlugin* rail,
+ const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail || !langBarInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED))
+ return ERROR_BAD_CONFIGURATION;
+
+ s = rail_pdu_init(RAIL_LANGBAR_INFO_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ error = rail_write_langbar_info_order(s, langBarInfo);
+
+ if (ERROR_SUCCESS == error)
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_LANGBARINFO);
+
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+UINT rail_send_client_languageime_info_order(railPlugin* rail,
+ const RAIL_LANGUAGEIME_INFO_ORDER* langImeInfo)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail || !langImeInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED))
+ return ERROR_BAD_CONFIGURATION;
+
+ s = rail_pdu_init(RAIL_LANGUAGEIME_INFO_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ error = rail_write_languageime_info_order(s, langImeInfo);
+
+ if (ERROR_SUCCESS == error)
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_LANGUAGEIMEINFO);
+
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+UINT rail_send_client_compartment_info_order(railPlugin* rail,
+ const RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail || !compartmentInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED))
+ return ERROR_BAD_CONFIGURATION;
+
+ s = rail_pdu_init(RAIL_COMPARTMENT_INFO_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ error = rail_write_compartment_info_order(s, compartmentInfo);
+
+ if (ERROR_SUCCESS == error)
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_COMPARTMENTINFO);
+
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+UINT rail_send_client_cloak_order(railPlugin* rail, const RAIL_CLOAK* cloak)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail || !cloak)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(5);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT32(s, cloak->windowId);
+ Stream_Write_UINT8(s, cloak->cloak ? 1 : 0);
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_CLOAK);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+UINT rail_send_client_snap_arrange_order(railPlugin* rail, const RAIL_SNAP_ARRANGE* snap)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!rail)
+ return ERROR_INVALID_PARAMETER;
+
+ /* 2.2.2.7.5 Client Window Snap PDU (TS_RAIL_ORDER_SNAP_ARRANGE) */
+ if ((rail->channelFlags & TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_SNAP_ARRANGE_SUPPORTED) == 0)
+ {
+ RAIL_WINDOW_MOVE_ORDER move = { 0 };
+ move.top = snap->top;
+ move.left = snap->left;
+ move.right = snap->right;
+ move.bottom = snap->bottom;
+ move.windowId = snap->windowId;
+ return rail_send_client_window_move_order(rail, &move);
+ }
+
+ s = rail_pdu_init(12);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT32(s, snap->windowId);
+ Stream_Write_INT16(s, snap->left);
+ Stream_Write_INT16(s, snap->top);
+ Stream_Write_INT16(s, snap->right);
+ Stream_Write_INT16(s, snap->bottom);
+ error = rail_send_pdu(rail, s, TS_RAIL_ORDER_SNAP_ARRANGE);
+ Stream_Free(s, TRUE);
+ return error;
+}
diff --git a/channels/rail/client/rail_orders.h b/channels/rail/client/rail_orders.h
new file mode 100644
index 0000000..1b15aae
--- /dev/null
+++ b/channels/rail/client/rail_orders.h
@@ -0,0 +1,60 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Remote Applications Integrated Locally (RAIL)
+ *
+ * Copyright 2009 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2011 Roman Barabanov <romanbarabanov@gmail.com>
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
+ *
+ * Licensed 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 FREERDP_CHANNEL_RAIL_CLIENT_ORDERS_H
+#define FREERDP_CHANNEL_RAIL_CLIENT_ORDERS_H
+
+#include <freerdp/channels/log.h>
+
+#include "rail_main.h"
+
+#define TAG CHANNELS_TAG("rail.client")
+
+UINT rail_order_recv(LPVOID userdata, wStream* s);
+UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType);
+
+UINT rail_send_handshake_order(railPlugin* rail, const RAIL_HANDSHAKE_ORDER* handshake);
+UINT rail_send_handshake_ex_order(railPlugin* rail, const RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
+UINT rail_send_client_status_order(railPlugin* rail, const RAIL_CLIENT_STATUS_ORDER* clientStatus);
+UINT rail_send_client_exec_order(railPlugin* rail, UINT16 flags,
+ const RAIL_UNICODE_STRING* exeOrFile,
+ const RAIL_UNICODE_STRING* workingDir,
+ const RAIL_UNICODE_STRING* arguments);
+UINT rail_send_client_activate_order(railPlugin* rail, const RAIL_ACTIVATE_ORDER* activate);
+UINT rail_send_client_sysmenu_order(railPlugin* rail, const RAIL_SYSMENU_ORDER* sysmenu);
+UINT rail_send_client_syscommand_order(railPlugin* rail, const RAIL_SYSCOMMAND_ORDER* syscommand);
+
+UINT rail_send_client_notify_event_order(railPlugin* rail,
+ const RAIL_NOTIFY_EVENT_ORDER* notifyEvent);
+UINT rail_send_client_window_move_order(railPlugin* rail, const RAIL_WINDOW_MOVE_ORDER* windowMove);
+UINT rail_send_client_get_appid_req_order(railPlugin* rail,
+ const RAIL_GET_APPID_REQ_ORDER* getAppIdReq);
+UINT rail_send_client_langbar_info_order(railPlugin* rail,
+ const RAIL_LANGBAR_INFO_ORDER* langBarInfo);
+UINT rail_send_client_languageime_info_order(railPlugin* rail,
+ const RAIL_LANGUAGEIME_INFO_ORDER* langImeInfo);
+UINT rail_send_client_cloak_order(railPlugin* rail, const RAIL_CLOAK* cloak);
+UINT rail_send_client_snap_arrange_order(railPlugin* rail, const RAIL_SNAP_ARRANGE* snap);
+UINT rail_send_client_compartment_info_order(railPlugin* rail,
+ const RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo);
+
+#endif /* FREERDP_CHANNEL_RAIL_CLIENT_ORDERS_H */
diff --git a/channels/rail/rail_common.c b/channels/rail/rail_common.c
new file mode 100644
index 0000000..fb6bc80
--- /dev/null
+++ b/channels/rail/rail_common.c
@@ -0,0 +1,591 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * RAIL common functions
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2011 Roman Barabanov <romanbarabanov@gmail.com>
+ * Copyright 2011 Vic Lee
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
+ *
+ * Licensed 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 "rail_common.h"
+
+#include <winpr/crt.h>
+#include <freerdp/channels/log.h>
+
+#define TAG CHANNELS_TAG("rail.common")
+
+const char* rail_get_order_type_string(UINT16 orderType)
+{
+ switch (orderType)
+ {
+ case TS_RAIL_ORDER_EXEC:
+ return "TS_RAIL_ORDER_EXEC";
+ case TS_RAIL_ORDER_ACTIVATE:
+ return "TS_RAIL_ORDER_ACTIVATE";
+ case TS_RAIL_ORDER_SYSPARAM:
+ return "TS_RAIL_ORDER_SYSPARAM";
+ case TS_RAIL_ORDER_SYSCOMMAND:
+ return "TS_RAIL_ORDER_SYSCOMMAND";
+ case TS_RAIL_ORDER_HANDSHAKE:
+ return "TS_RAIL_ORDER_HANDSHAKE";
+ case TS_RAIL_ORDER_NOTIFY_EVENT:
+ return "TS_RAIL_ORDER_NOTIFY_EVENT";
+ case TS_RAIL_ORDER_WINDOWMOVE:
+ return "TS_RAIL_ORDER_WINDOWMOVE";
+ case TS_RAIL_ORDER_LOCALMOVESIZE:
+ return "TS_RAIL_ORDER_LOCALMOVESIZE";
+ case TS_RAIL_ORDER_MINMAXINFO:
+ return "TS_RAIL_ORDER_MINMAXINFO";
+ case TS_RAIL_ORDER_CLIENTSTATUS:
+ return "TS_RAIL_ORDER_CLIENTSTATUS";
+ case TS_RAIL_ORDER_SYSMENU:
+ return "TS_RAIL_ORDER_SYSMENU";
+ case TS_RAIL_ORDER_LANGBARINFO:
+ return "TS_RAIL_ORDER_LANGBARINFO";
+ case TS_RAIL_ORDER_GET_APPID_REQ:
+ return "TS_RAIL_ORDER_GET_APPID_REQ";
+ case TS_RAIL_ORDER_GET_APPID_RESP:
+ return "TS_RAIL_ORDER_GET_APPID_RESP";
+ case TS_RAIL_ORDER_TASKBARINFO:
+ return "TS_RAIL_ORDER_TASKBARINFO";
+ case TS_RAIL_ORDER_LANGUAGEIMEINFO:
+ return "TS_RAIL_ORDER_LANGUAGEIMEINFO";
+ case TS_RAIL_ORDER_COMPARTMENTINFO:
+ return "TS_RAIL_ORDER_COMPARTMENTINFO";
+ case TS_RAIL_ORDER_HANDSHAKE_EX:
+ return "TS_RAIL_ORDER_HANDSHAKE_EX";
+ case TS_RAIL_ORDER_ZORDER_SYNC:
+ return "TS_RAIL_ORDER_ZORDER_SYNC";
+ case TS_RAIL_ORDER_CLOAK:
+ return "TS_RAIL_ORDER_CLOAK";
+ case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST:
+ return "TS_RAIL_ORDER_POWER_DISPLAY_REQUEST";
+ case TS_RAIL_ORDER_SNAP_ARRANGE:
+ return "TS_RAIL_ORDER_SNAP_ARRANGE";
+ case TS_RAIL_ORDER_GET_APPID_RESP_EX:
+ return "TS_RAIL_ORDER_GET_APPID_RESP_EX";
+ case TS_RAIL_ORDER_EXEC_RESULT:
+ return "TS_RAIL_ORDER_EXEC_RESULT";
+ case TS_RAIL_ORDER_TEXTSCALEINFO:
+ return "TS_RAIL_ORDER_TEXTSCALEINFO";
+ case TS_RAIL_ORDER_CARETBLINKINFO:
+ return "TS_RAIL_ORDER_CARETBLINKINFO";
+ default:
+ return "TS_RAIL_ORDER_UNKNOWN";
+ }
+}
+
+const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length)
+{
+ _snprintf(buffer, length, "%s[0x%04" PRIx16 "]", rail_get_order_type_string(orderType),
+ orderType);
+ return buffer;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_read_pdu_header(wStream* s, UINT16* orderType, UINT16* orderLength)
+{
+ if (!s || !orderType || !orderLength)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT16(s, *orderType); /* orderType (2 bytes) */
+ Stream_Read_UINT16(s, *orderLength); /* orderLength (2 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+void rail_write_pdu_header(wStream* s, UINT16 orderType, UINT16 orderLength)
+{
+ Stream_Write_UINT16(s, orderType); /* orderType (2 bytes) */
+ Stream_Write_UINT16(s, orderLength); /* orderLength (2 bytes) */
+}
+
+wStream* rail_pdu_init(size_t length)
+{
+ wStream* s = NULL;
+ s = Stream_New(NULL, length + RAIL_PDU_HEADER_LENGTH);
+
+ if (!s)
+ return NULL;
+
+ Stream_Seek(s, RAIL_PDU_HEADER_LENGTH);
+ return s;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_read_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, handshake->buildNumber); /* buildNumber (4 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+void rail_write_handshake_order(wStream* s, const RAIL_HANDSHAKE_ORDER* handshake)
+{
+ Stream_Write_UINT32(s, handshake->buildNumber); /* buildNumber (4 bytes) */
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_read_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, handshakeEx->buildNumber); /* buildNumber (4 bytes) */
+ Stream_Read_UINT32(s, handshakeEx->railHandshakeFlags); /* railHandshakeFlags (4 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+void rail_write_handshake_ex_order(wStream* s, const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
+{
+ Stream_Write_UINT32(s, handshakeEx->buildNumber); /* buildNumber (4 bytes) */
+ Stream_Write_UINT32(s, handshakeEx->railHandshakeFlags); /* railHandshakeFlags (4 bytes) */
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_write_unicode_string(wStream* s, const RAIL_UNICODE_STRING* unicode_string)
+{
+ if (!s || !unicode_string)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_EnsureRemainingCapacity(s, 2 + unicode_string->length))
+ {
+ WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT16(s, unicode_string->length); /* cbString (2 bytes) */
+ Stream_Write(s, unicode_string->string, unicode_string->length); /* string */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_write_unicode_string_value(wStream* s, const RAIL_UNICODE_STRING* unicode_string)
+{
+ size_t length = 0;
+
+ if (!s || !unicode_string)
+ return ERROR_INVALID_PARAMETER;
+
+ length = unicode_string->length;
+
+ if (length > 0)
+ {
+ if (!Stream_EnsureRemainingCapacity(s, length))
+ {
+ WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write(s, unicode_string->string, length); /* string */
+ }
+
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_high_contrast(wStream* s, RAIL_HIGH_CONTRAST* highContrast)
+{
+ if (!s || !highContrast)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, highContrast->flags); /* flags (4 bytes) */
+ Stream_Read_UINT32(s, highContrast->colorSchemeLength); /* colorSchemeLength (4 bytes) */
+
+ if (!rail_read_unicode_string(s, &highContrast->colorScheme)) /* colorScheme */
+ return ERROR_INTERNAL_ERROR;
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_write_high_contrast(wStream* s, const RAIL_HIGH_CONTRAST* highContrast)
+{
+ UINT32 colorSchemeLength = 0;
+
+ if (!s || !highContrast)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_EnsureRemainingCapacity(s, 8))
+ return CHANNEL_RC_NO_MEMORY;
+
+ colorSchemeLength = highContrast->colorScheme.length + 2;
+ Stream_Write_UINT32(s, highContrast->flags); /* flags (4 bytes) */
+ Stream_Write_UINT32(s, colorSchemeLength); /* colorSchemeLength (4 bytes) */
+ return rail_write_unicode_string(s, &highContrast->colorScheme); /* colorScheme */
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_filterkeys(wStream* s, TS_FILTERKEYS* filterKeys)
+{
+ if (!s || !filterKeys)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, filterKeys->Flags);
+ Stream_Read_UINT32(s, filterKeys->WaitTime);
+ Stream_Read_UINT32(s, filterKeys->DelayTime);
+ Stream_Read_UINT32(s, filterKeys->RepeatTime);
+ Stream_Read_UINT32(s, filterKeys->BounceTime);
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_write_filterkeys(wStream* s, const TS_FILTERKEYS* filterKeys)
+{
+ if (!s || !filterKeys)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_EnsureRemainingCapacity(s, 20))
+ return CHANNEL_RC_NO_MEMORY;
+
+ Stream_Write_UINT32(s, filterKeys->Flags);
+ Stream_Write_UINT32(s, filterKeys->WaitTime);
+ Stream_Write_UINT32(s, filterKeys->DelayTime);
+ Stream_Write_UINT32(s, filterKeys->RepeatTime);
+ Stream_Write_UINT32(s, filterKeys->BounceTime);
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rail_read_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam, BOOL extendedSpiSupported)
+{
+ BYTE body = 0;
+ UINT error = CHANNEL_RC_OK;
+
+ if (!s || !sysparam)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, sysparam->param); /* systemParam (4 bytes) */
+
+ sysparam->params = 0; /* bitflags of received params */
+
+ switch (sysparam->param)
+ {
+ /* Client sysparams */
+ case SPI_SET_DRAG_FULL_WINDOWS:
+ sysparam->params |= SPI_MASK_SET_DRAG_FULL_WINDOWS;
+ Stream_Read_UINT8(s, body); /* body (1 byte) */
+ sysparam->dragFullWindows = body != 0;
+ break;
+
+ case SPI_SET_KEYBOARD_CUES:
+ sysparam->params |= SPI_MASK_SET_KEYBOARD_CUES;
+ Stream_Read_UINT8(s, body); /* body (1 byte) */
+ sysparam->keyboardCues = body != 0;
+ break;
+
+ case SPI_SET_KEYBOARD_PREF:
+ sysparam->params |= SPI_MASK_SET_KEYBOARD_PREF;
+ Stream_Read_UINT8(s, body); /* body (1 byte) */
+ sysparam->keyboardPref = body != 0;
+ break;
+
+ case SPI_SET_MOUSE_BUTTON_SWAP:
+ sysparam->params |= SPI_MASK_SET_MOUSE_BUTTON_SWAP;
+ Stream_Read_UINT8(s, body); /* body (1 byte) */
+ sysparam->mouseButtonSwap = body != 0;
+ break;
+
+ case SPI_SET_WORK_AREA:
+ sysparam->params |= SPI_MASK_SET_WORK_AREA;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT16(s, sysparam->workArea.left); /* left (2 bytes) */
+ Stream_Read_UINT16(s, sysparam->workArea.top); /* top (2 bytes) */
+ Stream_Read_UINT16(s, sysparam->workArea.right); /* right (2 bytes) */
+ Stream_Read_UINT16(s, sysparam->workArea.bottom); /* bottom (2 bytes) */
+ break;
+
+ case SPI_DISPLAY_CHANGE:
+ sysparam->params |= SPI_MASK_DISPLAY_CHANGE;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT16(s, sysparam->displayChange.left); /* left (2 bytes) */
+ Stream_Read_UINT16(s, sysparam->displayChange.top); /* top (2 bytes) */
+ Stream_Read_UINT16(s, sysparam->displayChange.right); /* right (2 bytes) */
+ Stream_Read_UINT16(s, sysparam->displayChange.bottom); /* bottom (2 bytes) */
+ break;
+
+ case SPI_TASKBAR_POS:
+ sysparam->params |= SPI_MASK_TASKBAR_POS;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT16(s, sysparam->taskbarPos.left); /* left (2 bytes) */
+ Stream_Read_UINT16(s, sysparam->taskbarPos.top); /* top (2 bytes) */
+ Stream_Read_UINT16(s, sysparam->taskbarPos.right); /* right (2 bytes) */
+ Stream_Read_UINT16(s, sysparam->taskbarPos.bottom); /* bottom (2 bytes) */
+ break;
+
+ case SPI_SET_HIGH_CONTRAST:
+ sysparam->params |= SPI_MASK_SET_HIGH_CONTRAST;
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return ERROR_INVALID_DATA;
+
+ error = rail_read_high_contrast(s, &sysparam->highContrast);
+ break;
+
+ case SPI_SETCARETWIDTH:
+ sysparam->params |= SPI_MASK_SET_CARET_WIDTH;
+
+ if (!extendedSpiSupported)
+ return ERROR_INVALID_DATA;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, sysparam->caretWidth);
+
+ if (sysparam->caretWidth < 0x0001)
+ return ERROR_INVALID_DATA;
+
+ break;
+
+ case SPI_SETSTICKYKEYS:
+ sysparam->params |= SPI_MASK_SET_STICKY_KEYS;
+
+ if (!extendedSpiSupported)
+ return ERROR_INVALID_DATA;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, sysparam->stickyKeys);
+ break;
+
+ case SPI_SETTOGGLEKEYS:
+ sysparam->params |= SPI_MASK_SET_TOGGLE_KEYS;
+
+ if (!extendedSpiSupported)
+ return ERROR_INVALID_DATA;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, sysparam->toggleKeys);
+ break;
+
+ case SPI_SETFILTERKEYS:
+ sysparam->params |= SPI_MASK_SET_FILTER_KEYS;
+
+ if (!extendedSpiSupported)
+ return ERROR_INVALID_DATA;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
+ return ERROR_INVALID_DATA;
+
+ error = rail_read_filterkeys(s, &sysparam->filterKeys);
+ break;
+
+ /* Server sysparams */
+ case SPI_SETSCREENSAVEACTIVE:
+ sysparam->params |= SPI_MASK_SET_SCREEN_SAVE_ACTIVE;
+
+ Stream_Read_UINT8(s, body); /* body (1 byte) */
+ sysparam->setScreenSaveActive = body != 0;
+ break;
+
+ case SPI_SETSCREENSAVESECURE:
+ sysparam->params |= SPI_MASK_SET_SET_SCREEN_SAVE_SECURE;
+
+ Stream_Read_UINT8(s, body); /* body (1 byte) */
+ sysparam->setScreenSaveSecure = body != 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 err2or code
+ */
+UINT rail_write_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam,
+ BOOL extendedSpiSupported)
+{
+ BYTE body = 0;
+ UINT error = CHANNEL_RC_OK;
+
+ if (!s || !sysparam)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_EnsureRemainingCapacity(s, 12))
+ return CHANNEL_RC_NO_MEMORY;
+
+ Stream_Write_UINT32(s, sysparam->param); /* systemParam (4 bytes) */
+
+ switch (sysparam->param)
+ {
+ /* Client sysparams */
+ case SPI_SET_DRAG_FULL_WINDOWS:
+ body = sysparam->dragFullWindows ? 1 : 0;
+ Stream_Write_UINT8(s, body);
+ break;
+
+ case SPI_SET_KEYBOARD_CUES:
+ body = sysparam->keyboardCues ? 1 : 0;
+ Stream_Write_UINT8(s, body);
+ break;
+
+ case SPI_SET_KEYBOARD_PREF:
+ body = sysparam->keyboardPref ? 1 : 0;
+ Stream_Write_UINT8(s, body);
+ break;
+
+ case SPI_SET_MOUSE_BUTTON_SWAP:
+ body = sysparam->mouseButtonSwap ? 1 : 0;
+ Stream_Write_UINT8(s, body);
+ break;
+
+ case SPI_SET_WORK_AREA:
+ Stream_Write_UINT16(s, sysparam->workArea.left); /* left (2 bytes) */
+ Stream_Write_UINT16(s, sysparam->workArea.top); /* top (2 bytes) */
+ Stream_Write_UINT16(s, sysparam->workArea.right); /* right (2 bytes) */
+ Stream_Write_UINT16(s, sysparam->workArea.bottom); /* bottom (2 bytes) */
+ break;
+
+ case SPI_DISPLAY_CHANGE:
+ Stream_Write_UINT16(s, sysparam->displayChange.left); /* left (2 bytes) */
+ Stream_Write_UINT16(s, sysparam->displayChange.top); /* top (2 bytes) */
+ Stream_Write_UINT16(s, sysparam->displayChange.right); /* right (2 bytes) */
+ Stream_Write_UINT16(s, sysparam->displayChange.bottom); /* bottom (2 bytes) */
+ break;
+
+ case SPI_TASKBAR_POS:
+ Stream_Write_UINT16(s, sysparam->taskbarPos.left); /* left (2 bytes) */
+ Stream_Write_UINT16(s, sysparam->taskbarPos.top); /* top (2 bytes) */
+ Stream_Write_UINT16(s, sysparam->taskbarPos.right); /* right (2 bytes) */
+ Stream_Write_UINT16(s, sysparam->taskbarPos.bottom); /* bottom (2 bytes) */
+ break;
+
+ case SPI_SET_HIGH_CONTRAST:
+ error = rail_write_high_contrast(s, &sysparam->highContrast);
+ break;
+
+ case SPI_SETCARETWIDTH:
+ if (!extendedSpiSupported)
+ return ERROR_INVALID_DATA;
+
+ if (sysparam->caretWidth < 0x0001)
+ return ERROR_INVALID_DATA;
+
+ Stream_Write_UINT32(s, sysparam->caretWidth);
+ break;
+
+ case SPI_SETSTICKYKEYS:
+ if (!extendedSpiSupported)
+ return ERROR_INVALID_DATA;
+
+ Stream_Write_UINT32(s, sysparam->stickyKeys);
+ break;
+
+ case SPI_SETTOGGLEKEYS:
+ if (!extendedSpiSupported)
+ return ERROR_INVALID_DATA;
+
+ Stream_Write_UINT32(s, sysparam->toggleKeys);
+ break;
+
+ case SPI_SETFILTERKEYS:
+ if (!extendedSpiSupported)
+ return ERROR_INVALID_DATA;
+
+ error = rail_write_filterkeys(s, &sysparam->filterKeys);
+ break;
+
+ /* Server sysparams */
+ case SPI_SETSCREENSAVEACTIVE:
+ body = sysparam->setScreenSaveActive ? 1 : 0;
+ Stream_Write_UINT8(s, body);
+ break;
+
+ case SPI_SETSCREENSAVESECURE:
+ body = sysparam->setScreenSaveSecure ? 1 : 0;
+ Stream_Write_UINT8(s, body);
+ break;
+
+ default:
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ return error;
+}
+
+BOOL rail_is_extended_spi_supported(UINT32 channelFlags)
+{
+ return (channelFlags & TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_SUPPORTED) ? TRUE : FALSE;
+}
diff --git a/channels/rail/rail_common.h b/channels/rail/rail_common.h
new file mode 100644
index 0000000..34b6fa0
--- /dev/null
+++ b/channels/rail/rail_common.h
@@ -0,0 +1,76 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * RAIL Virtual Channel Plugin
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2011 Roman Barabanov <romanbarabanov@gmail.com>
+ * Copyright 2011 Vic Lee
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
+ *
+ * Licensed 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 FREERDP_CHANNEL_RAIL_COMMON_H
+#define FREERDP_CHANNEL_RAIL_COMMON_H
+
+#include <freerdp/rail.h>
+
+#define RAIL_PDU_HEADER_LENGTH 4
+
+/* Fixed length of PDUs, excluding variable lengths */
+#define RAIL_HANDSHAKE_ORDER_LENGTH 4 /* fixed */
+#define RAIL_HANDSHAKE_EX_ORDER_LENGTH 8 /* fixed */
+#define RAIL_CLIENT_STATUS_ORDER_LENGTH 4 /* fixed */
+#define RAIL_EXEC_ORDER_LENGTH 8 /* variable */
+#define RAIL_EXEC_RESULT_ORDER_LENGTH 12 /* variable */
+#define RAIL_SYSPARAM_ORDER_LENGTH 4 /* variable */
+#define RAIL_MINMAXINFO_ORDER_LENGTH 20 /* fixed */
+#define RAIL_LOCALMOVESIZE_ORDER_LENGTH 12 /* fixed */
+#define RAIL_ACTIVATE_ORDER_LENGTH 5 /* fixed */
+#define RAIL_SYSMENU_ORDER_LENGTH 8 /* fixed */
+#define RAIL_SYSCOMMAND_ORDER_LENGTH 6 /* fixed */
+#define RAIL_NOTIFY_EVENT_ORDER_LENGTH 12 /* fixed */
+#define RAIL_WINDOW_MOVE_ORDER_LENGTH 12 /* fixed */
+#define RAIL_SNAP_ARRANGE_ORDER_LENGTH 12 /* fixed */
+#define RAIL_GET_APPID_REQ_ORDER_LENGTH 4 /* fixed */
+#define RAIL_LANGBAR_INFO_ORDER_LENGTH 4 /* fixed */
+#define RAIL_LANGUAGEIME_INFO_ORDER_LENGTH 42 /* fixed */
+#define RAIL_COMPARTMENT_INFO_ORDER_LENGTH 16 /* fixed */
+#define RAIL_CLOAK_ORDER_LENGTH 5 /* fixed */
+#define RAIL_TASKBAR_INFO_ORDER_LENGTH 12 /* fixed */
+#define RAIL_Z_ORDER_SYNC_ORDER_LENGTH 4 /* fixed */
+#define RAIL_POWER_DISPLAY_REQUEST_ORDER_LENGTH 4 /* fixed */
+#define RAIL_GET_APPID_RESP_ORDER_LENGTH 524 /* fixed */
+#define RAIL_GET_APPID_RESP_EX_ORDER_LENGTH 1048 /* fixed */
+
+UINT rail_read_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake);
+void rail_write_handshake_order(wStream* s, const RAIL_HANDSHAKE_ORDER* handshake);
+UINT rail_read_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
+void rail_write_handshake_ex_order(wStream* s, const RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
+
+wStream* rail_pdu_init(size_t length);
+UINT rail_read_pdu_header(wStream* s, UINT16* orderType, UINT16* orderLength);
+void rail_write_pdu_header(wStream* s, UINT16 orderType, UINT16 orderLength);
+
+UINT rail_write_unicode_string(wStream* s, const RAIL_UNICODE_STRING* unicode_string);
+UINT rail_write_unicode_string_value(wStream* s, const RAIL_UNICODE_STRING* unicode_string);
+
+UINT rail_read_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam, BOOL extendedSpiSupported);
+UINT rail_write_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam,
+ BOOL extendedSpiSupported);
+BOOL rail_is_extended_spi_supported(UINT32 channelsFlags);
+const char* rail_get_order_type_string(UINT16 orderType);
+const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length);
+
+#endif /* FREERDP_CHANNEL_RAIL_COMMON_H */
diff --git a/channels/rail/server/CMakeLists.txt b/channels/rail/server/CMakeLists.txt
new file mode 100644
index 0000000..f8f64be
--- /dev/null
+++ b/channels/rail/server/CMakeLists.txt
@@ -0,0 +1,33 @@
+# FreeRDP: A Remote Desktop Protocol Implementation
+# FreeRDP cmake build script
+#
+# Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
+#
+# Licensed 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.
+
+define_channel_server("rail")
+
+set(${MODULE_PREFIX}_SRCS
+ ../rail_common.c
+ ../rail_common.h
+ rail_main.c
+ rail_main.h
+)
+
+set(${MODULE_PREFIX}_LIBS
+ freerdp
+)
+
+include_directories(..)
+
+add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntryEx")
diff --git a/channels/rail/server/rail_main.c b/channels/rail/server/rail_main.c
new file mode 100644
index 0000000..8e38c2b
--- /dev/null
+++ b/channels/rail/server/rail_main.c
@@ -0,0 +1,1728 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * RAIL Virtual Channel Plugin
+ *
+ * Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
+ *
+
+ * Licensed 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 <freerdp/types.h>
+#include <freerdp/constants.h>
+
+#include <freerdp/freerdp.h>
+#include <freerdp/channels/log.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+#include <winpr/thread.h>
+#include <winpr/stream.h>
+
+#include "rail_main.h"
+
+#define TAG CHANNELS_TAG("rail.server")
+
+/**
+ * Sends a single rail PDU on the channel
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send(RailServerContext* context, wStream* s, ULONG length)
+{
+ UINT status = CHANNEL_RC_OK;
+ ULONG written = 0;
+
+ if (!context)
+ return CHANNEL_RC_BAD_INIT_HANDLE;
+
+ if (!WTSVirtualChannelWrite(context->priv->rail_channel, (PCHAR)Stream_Buffer(s), length,
+ &written))
+ {
+ WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
+ status = ERROR_INTERNAL_ERROR;
+ }
+
+ return status;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_server_send_pdu(RailServerContext* context, wStream* s, UINT16 orderType)
+{
+ char buffer[128] = { 0 };
+ UINT16 orderLength = 0;
+
+ if (!context || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ orderLength = (UINT16)Stream_GetPosition(s);
+ Stream_SetPosition(s, 0);
+ rail_write_pdu_header(s, orderType, orderLength);
+ Stream_SetPosition(s, orderLength);
+ WLog_DBG(TAG, "Sending %s PDU, length: %" PRIu16 "",
+ rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
+ return rail_send(context, s, orderLength);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_write_local_move_size_order(wStream* s,
+ const RAIL_LOCALMOVESIZE_ORDER* localMoveSize)
+{
+ if (!s || !localMoveSize)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, localMoveSize->windowId); /* WindowId (4 bytes) */
+ Stream_Write_UINT16(s, localMoveSize->isMoveSizeStart ? 1 : 0); /* IsMoveSizeStart (2 bytes) */
+ Stream_Write_UINT16(s, localMoveSize->moveSizeType); /* MoveSizeType (2 bytes) */
+ Stream_Write_UINT16(s, localMoveSize->posX); /* PosX (2 bytes) */
+ Stream_Write_UINT16(s, localMoveSize->posY); /* PosY (2 bytes) */
+ return ERROR_SUCCESS;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_write_min_max_info_order(wStream* s, const RAIL_MINMAXINFO_ORDER* minMaxInfo)
+{
+ if (!s || !minMaxInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, minMaxInfo->windowId); /* WindowId (4 bytes) */
+ Stream_Write_INT16(s, minMaxInfo->maxWidth); /* MaxWidth (2 bytes) */
+ Stream_Write_INT16(s, minMaxInfo->maxHeight); /* MaxHeight (2 bytes) */
+ Stream_Write_INT16(s, minMaxInfo->maxPosX); /* MaxPosX (2 bytes) */
+ Stream_Write_INT16(s, minMaxInfo->maxPosY); /* MaxPosY (2 bytes) */
+ Stream_Write_INT16(s, minMaxInfo->minTrackWidth); /* MinTrackWidth (2 bytes) */
+ Stream_Write_INT16(s, minMaxInfo->minTrackHeight); /* MinTrackHeight (2 bytes) */
+ Stream_Write_INT16(s, minMaxInfo->maxTrackWidth); /* MaxTrackWidth (2 bytes) */
+ Stream_Write_INT16(s, minMaxInfo->maxTrackHeight); /* MaxTrackHeight (2 bytes) */
+ return ERROR_SUCCESS;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_write_taskbar_info_order(wStream* s, const RAIL_TASKBAR_INFO_ORDER* taskbarInfo)
+{
+ if (!s || !taskbarInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, taskbarInfo->TaskbarMessage); /* TaskbarMessage (4 bytes) */
+ Stream_Write_UINT32(s, taskbarInfo->WindowIdTab); /* WindowIdTab (4 bytes) */
+ Stream_Write_UINT32(s, taskbarInfo->Body); /* Body (4 bytes) */
+ return ERROR_SUCCESS;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_write_langbar_info_order(wStream* s, const RAIL_LANGBAR_INFO_ORDER* langbarInfo)
+{
+ if (!s || !langbarInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, langbarInfo->languageBarStatus); /* LanguageBarStatus (4 bytes) */
+ return ERROR_SUCCESS;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_write_exec_result_order(wStream* s, const RAIL_EXEC_RESULT_ORDER* execResult)
+{
+ if (!s || !execResult)
+ return ERROR_INVALID_PARAMETER;
+
+ if (execResult->exeOrFile.length > 520 || execResult->exeOrFile.length < 1)
+ return ERROR_INVALID_DATA;
+
+ Stream_Write_UINT16(s, execResult->flags); /* Flags (2 bytes) */
+ Stream_Write_UINT16(s, execResult->execResult); /* ExecResult (2 bytes) */
+ Stream_Write_UINT32(s, execResult->rawResult); /* RawResult (4 bytes) */
+ Stream_Write_UINT16(s, 0); /* Padding (2 bytes) */
+ Stream_Write_UINT16(s, execResult->exeOrFile.length); /* ExeOrFileLength (2 bytes) */
+ Stream_Write(s, execResult->exeOrFile.string,
+ execResult->exeOrFile.length); /* ExeOrFile (variable) */
+ return ERROR_SUCCESS;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_write_z_order_sync_order(wStream* s, const RAIL_ZORDER_SYNC* zOrderSync)
+{
+ if (!s || !zOrderSync)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, zOrderSync->windowIdMarker); /* WindowIdMarker (4 bytes) */
+ return ERROR_SUCCESS;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_write_cloak_order(wStream* s, const RAIL_CLOAK* cloak)
+{
+ if (!s || !cloak)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, cloak->windowId); /* WindowId (4 bytes) */
+ Stream_Write_UINT8(s, cloak->cloak ? 1 : 0); /* Cloaked (1 byte) */
+ return ERROR_SUCCESS;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT
+rail_write_power_display_request_order(wStream* s,
+ const RAIL_POWER_DISPLAY_REQUEST* powerDisplayRequest)
+{
+ if (!s || !powerDisplayRequest)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, powerDisplayRequest->active ? 1 : 0); /* Active (4 bytes) */
+ return ERROR_SUCCESS;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_write_get_app_id_resp_order(wStream* s,
+ const RAIL_GET_APPID_RESP_ORDER* getAppidResp)
+{
+ if (!s || !getAppidResp)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, getAppidResp->windowId); /* WindowId (4 bytes) */
+ Stream_Write_UTF16_String(
+ s, getAppidResp->applicationId,
+ ARRAYSIZE(getAppidResp->applicationId)); /* ApplicationId (512 bytes) */
+ return ERROR_SUCCESS;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_write_get_appid_resp_ex_order(wStream* s,
+ const RAIL_GET_APPID_RESP_EX* getAppidRespEx)
+{
+ if (!s || !getAppidRespEx)
+ return ERROR_INVALID_PARAMETER;
+
+ Stream_Write_UINT32(s, getAppidRespEx->windowID); /* WindowId (4 bytes) */
+ Stream_Write_UTF16_String(
+ s, getAppidRespEx->applicationID,
+ ARRAYSIZE(getAppidRespEx->applicationID)); /* ApplicationId (520 bytes) */
+ Stream_Write_UINT32(s, getAppidRespEx->processId); /* ProcessId (4 bytes) */
+ Stream_Write_UTF16_String(
+ s, getAppidRespEx->processImageName,
+ ARRAYSIZE(getAppidRespEx->processImageName)); /* ProcessImageName (520 bytes) */
+ return ERROR_SUCCESS;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send_server_handshake(RailServerContext* context,
+ const RAIL_HANDSHAKE_ORDER* handshake)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!context || !handshake)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_HANDSHAKE_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_handshake_order(s, handshake);
+ error = rail_server_send_pdu(context, s, TS_RAIL_ORDER_HANDSHAKE);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send_server_handshake_ex(RailServerContext* context,
+ const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!context || !handshakeEx || !context->priv)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_HANDSHAKE_EX_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_server_set_handshake_ex_flags(context, handshakeEx->railHandshakeFlags);
+
+ rail_write_handshake_ex_order(s, handshakeEx);
+ error = rail_server_send_pdu(context, s, TS_RAIL_ORDER_HANDSHAKE_EX);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send_server_sysparam(RailServerContext* context,
+ const RAIL_SYSPARAM_ORDER* sysparam)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+ RailServerPrivate* priv = NULL;
+ BOOL extendedSpiSupported = 0;
+
+ if (!context || !sysparam)
+ return ERROR_INVALID_PARAMETER;
+
+ priv = context->priv;
+
+ if (!priv)
+ return ERROR_INVALID_PARAMETER;
+
+ extendedSpiSupported = rail_is_extended_spi_supported(context->priv->channelFlags);
+ s = rail_pdu_init(RAIL_SYSPARAM_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_sysparam_order(s, sysparam, extendedSpiSupported);
+ error = rail_server_send_pdu(context, s, TS_RAIL_ORDER_SYSPARAM);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send_server_local_move_size(RailServerContext* context,
+ const RAIL_LOCALMOVESIZE_ORDER* localMoveSize)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!context || !localMoveSize)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_LOCALMOVESIZE_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_local_move_size_order(s, localMoveSize);
+ error = rail_server_send_pdu(context, s, TS_RAIL_ORDER_LOCALMOVESIZE);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send_server_min_max_info(RailServerContext* context,
+ const RAIL_MINMAXINFO_ORDER* minMaxInfo)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!context || !minMaxInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_MINMAXINFO_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_min_max_info_order(s, minMaxInfo);
+ error = rail_server_send_pdu(context, s, TS_RAIL_ORDER_MINMAXINFO);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send_server_taskbar_info(RailServerContext* context,
+ const RAIL_TASKBAR_INFO_ORDER* taskbarInfo)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!context || !taskbarInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_TASKBAR_INFO_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_taskbar_info_order(s, taskbarInfo);
+ error = rail_server_send_pdu(context, s, TS_RAIL_ORDER_TASKBARINFO);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send_server_langbar_info(RailServerContext* context,
+ const RAIL_LANGBAR_INFO_ORDER* langbarInfo)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!context || !langbarInfo)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_LANGBAR_INFO_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_langbar_info_order(s, langbarInfo);
+ error = rail_server_send_pdu(context, s, TS_RAIL_ORDER_LANGBARINFO);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send_server_exec_result(RailServerContext* context,
+ const RAIL_EXEC_RESULT_ORDER* execResult)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!context || !execResult)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_EXEC_RESULT_ORDER_LENGTH + execResult->exeOrFile.length);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_exec_result_order(s, execResult);
+ error = rail_server_send_pdu(context, s, TS_RAIL_ORDER_EXEC_RESULT);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send_server_z_order_sync(RailServerContext* context,
+ const RAIL_ZORDER_SYNC* zOrderSync)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!context || !zOrderSync)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_Z_ORDER_SYNC_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_z_order_sync_order(s, zOrderSync);
+ error = rail_server_send_pdu(context, s, TS_RAIL_ORDER_ZORDER_SYNC);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send_server_cloak(RailServerContext* context, const RAIL_CLOAK* cloak)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!context || !cloak)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_CLOAK_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_cloak_order(s, cloak);
+ error = rail_server_send_pdu(context, s, TS_RAIL_ORDER_CLOAK);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT
+rail_send_server_power_display_request(RailServerContext* context,
+ const RAIL_POWER_DISPLAY_REQUEST* powerDisplayRequest)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!context || !powerDisplayRequest)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_POWER_DISPLAY_REQUEST_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_power_display_request_order(s, powerDisplayRequest);
+ error = rail_server_send_pdu(context, s, TS_RAIL_ORDER_POWER_DISPLAY_REQUEST);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error coie
+ */
+static UINT rail_send_server_get_app_id_resp(RailServerContext* context,
+ const RAIL_GET_APPID_RESP_ORDER* getAppidResp)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!context || !getAppidResp)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_GET_APPID_RESP_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_get_app_id_resp_order(s, getAppidResp);
+ error = rail_server_send_pdu(context, s, TS_RAIL_ORDER_GET_APPID_RESP);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_send_server_get_appid_resp_ex(RailServerContext* context,
+ const RAIL_GET_APPID_RESP_EX* getAppidRespEx)
+{
+ wStream* s = NULL;
+ UINT error = 0;
+
+ if (!context || !getAppidRespEx)
+ return ERROR_INVALID_PARAMETER;
+
+ s = rail_pdu_init(RAIL_GET_APPID_RESP_EX_ORDER_LENGTH);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rail_pdu_init failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ rail_write_get_appid_resp_ex_order(s, getAppidRespEx);
+ error = rail_server_send_pdu(context, s, TS_RAIL_ORDER_GET_APPID_RESP_EX);
+ Stream_Free(s, TRUE);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_client_status_order(wStream* s, RAIL_CLIENT_STATUS_ORDER* clientStatus)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_CLIENT_STATUS_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, clientStatus->flags); /* Flags (4 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_exec_order(wStream* s, RAIL_EXEC_ORDER* exec)
+{
+ RAIL_EXEC_ORDER order = { 0 };
+ UINT16 exeLen = 0;
+ UINT16 workLen = 0;
+ UINT16 argLen = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_EXEC_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT16(s, exec->flags); /* Flags (2 bytes) */
+ Stream_Read_UINT16(s, exeLen); /* ExeOrFileLength (2 bytes) */
+ Stream_Read_UINT16(s, workLen); /* WorkingDirLength (2 bytes) */
+ Stream_Read_UINT16(s, argLen); /* ArgumentsLength (2 bytes) */
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, (size_t)exeLen + workLen + argLen))
+ return ERROR_INVALID_DATA;
+
+ if (exeLen > 0)
+ {
+ const SSIZE_T len = exeLen / sizeof(WCHAR);
+ exec->RemoteApplicationProgram = Stream_Read_UTF16_String_As_UTF8(s, len, NULL);
+ if (!exec->RemoteApplicationProgram)
+ goto fail;
+ }
+ if (workLen > 0)
+ {
+ const SSIZE_T len = workLen / sizeof(WCHAR);
+ exec->RemoteApplicationWorkingDir = Stream_Read_UTF16_String_As_UTF8(s, len, NULL);
+ if (!exec->RemoteApplicationWorkingDir)
+ goto fail;
+ }
+ if (argLen > 0)
+ {
+ const SSIZE_T len = argLen / sizeof(WCHAR);
+ exec->RemoteApplicationArguments = Stream_Read_UTF16_String_As_UTF8(s, len, NULL);
+ if (!exec->RemoteApplicationArguments)
+ goto fail;
+ }
+
+ return CHANNEL_RC_OK;
+fail:
+ free(exec->RemoteApplicationProgram);
+ free(exec->RemoteApplicationArguments);
+ free(exec->RemoteApplicationWorkingDir);
+ *exec = order;
+ return ERROR_INTERNAL_ERROR;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_activate_order(wStream* s, RAIL_ACTIVATE_ORDER* activate)
+{
+ BYTE enabled = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_ACTIVATE_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, activate->windowId); /* WindowId (4 bytes) */
+ Stream_Read_UINT8(s, enabled); /* Enabled (1 byte) */
+ activate->enabled = (enabled != 0) ? TRUE : FALSE;
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_sysmenu_order(wStream* s, RAIL_SYSMENU_ORDER* sysmenu)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_SYSMENU_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, sysmenu->windowId); /* WindowId (4 bytes) */
+ Stream_Read_INT16(s, sysmenu->left); /* Left (2 bytes) */
+ Stream_Read_INT16(s, sysmenu->top); /* Top (2 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_syscommand_order(wStream* s, RAIL_SYSCOMMAND_ORDER* syscommand)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_SYSCOMMAND_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, syscommand->windowId); /* WindowId (4 bytes) */
+ Stream_Read_UINT16(s, syscommand->command); /* Command (2 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_notify_event_order(wStream* s, RAIL_NOTIFY_EVENT_ORDER* notifyEvent)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_NOTIFY_EVENT_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, notifyEvent->windowId); /* WindowId (4 bytes) */
+ Stream_Read_UINT32(s, notifyEvent->notifyIconId); /* NotifyIconId (4 bytes) */
+ Stream_Read_UINT32(s, notifyEvent->message); /* Message (4 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_get_appid_req_order(wStream* s, RAIL_GET_APPID_REQ_ORDER* getAppidReq)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_GET_APPID_REQ_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, getAppidReq->windowId); /* WindowId (4 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_window_move_order(wStream* s, RAIL_WINDOW_MOVE_ORDER* windowMove)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_WINDOW_MOVE_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, windowMove->windowId); /* WindowId (4 bytes) */
+ Stream_Read_INT16(s, windowMove->left); /* Left (2 bytes) */
+ Stream_Read_INT16(s, windowMove->top); /* Top (2 bytes) */
+ Stream_Read_INT16(s, windowMove->right); /* Right (2 bytes) */
+ Stream_Read_INT16(s, windowMove->bottom); /* Bottom (2 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_snap_arange_order(wStream* s, RAIL_SNAP_ARRANGE* snapArrange)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_SNAP_ARRANGE_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, snapArrange->windowId); /* WindowId (4 bytes) */
+ Stream_Read_INT16(s, snapArrange->left); /* Left (2 bytes) */
+ Stream_Read_INT16(s, snapArrange->top); /* Top (2 bytes) */
+ Stream_Read_INT16(s, snapArrange->right); /* Right (2 bytes) */
+ Stream_Read_INT16(s, snapArrange->bottom); /* Bottom (2 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_langbar_info_order(wStream* s, RAIL_LANGBAR_INFO_ORDER* langbarInfo)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_LANGBAR_INFO_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, langbarInfo->languageBarStatus); /* LanguageBarStatus (4 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_language_ime_info_order(wStream* s,
+ RAIL_LANGUAGEIME_INFO_ORDER* languageImeInfo)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_LANGUAGEIME_INFO_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, languageImeInfo->ProfileType); /* ProfileType (4 bytes) */
+ Stream_Read_UINT16(s, languageImeInfo->LanguageID); /* LanguageID (2 bytes) */
+ Stream_Read(
+ s, &languageImeInfo->LanguageProfileCLSID,
+ sizeof(languageImeInfo->LanguageProfileCLSID)); /* LanguageProfileCLSID (16 bytes) */
+ Stream_Read(s, &languageImeInfo->ProfileGUID,
+ sizeof(languageImeInfo->ProfileGUID)); /* ProfileGUID (16 bytes) */
+ Stream_Read_UINT32(s, languageImeInfo->KeyboardLayout); /* KeyboardLayout (4 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_compartment_info_order(wStream* s,
+ RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_COMPARTMENT_INFO_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, compartmentInfo->ImeState); /* ImeState (4 bytes) */
+ Stream_Read_UINT32(s, compartmentInfo->ImeConvMode); /* ImeConvMode (4 bytes) */
+ Stream_Read_UINT32(s, compartmentInfo->ImeSentenceMode); /* ImeSentenceMode (4 bytes) */
+ Stream_Read_UINT32(s, compartmentInfo->KanaMode); /* KANAMode (4 bytes) */
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_read_cloak_order(wStream* s, RAIL_CLOAK* cloak)
+{
+ BYTE cloaked = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_CLOAK_ORDER_LENGTH))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, cloak->windowId); /* WindowId (4 bytes) */
+ Stream_Read_UINT8(s, cloaked); /* Cloaked (1 byte) */
+ cloak->cloak = (cloaked != 0) ? TRUE : FALSE;
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_handshake_order(RailServerContext* context,
+ RAIL_HANDSHAKE_ORDER* handshake, wStream* s)
+{
+ UINT error = 0;
+
+ if (!context || !handshake || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_handshake_order(s, handshake)))
+ {
+ WLog_ERR(TAG, "rail_read_handshake_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientHandshake, error, context, handshake);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientHandshake failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_client_status_order(RailServerContext* context,
+ RAIL_CLIENT_STATUS_ORDER* clientStatus, wStream* s)
+{
+ UINT error = 0;
+
+ if (!context || !clientStatus || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_client_status_order(s, clientStatus)))
+ {
+ WLog_ERR(TAG, "rail_read_client_status_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientClientStatus, error, context, clientStatus);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientClientStatus failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_exec_order(RailServerContext* context, wStream* s)
+{
+ UINT error = 0;
+ RAIL_EXEC_ORDER exec = { 0 };
+
+ if (!context || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_exec_order(s, &exec)))
+ {
+ WLog_ERR(TAG, "rail_read_client_status_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientExec, error, context, &exec);
+
+ if (error)
+ WLog_ERR(TAG, "context.Exec failed with error %" PRIu32 "", error);
+
+ free(exec.RemoteApplicationProgram);
+ free(exec.RemoteApplicationArguments);
+ free(exec.RemoteApplicationWorkingDir);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_sysparam_order(RailServerContext* context,
+ RAIL_SYSPARAM_ORDER* sysparam, wStream* s)
+{
+ UINT error = 0;
+ BOOL extendedSpiSupported = 0;
+
+ if (!context || !sysparam || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ extendedSpiSupported = rail_is_extended_spi_supported(context->priv->channelFlags);
+ if ((error = rail_read_sysparam_order(s, sysparam, extendedSpiSupported)))
+ {
+ WLog_ERR(TAG, "rail_read_sysparam_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientSysparam, error, context, sysparam);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientSysparam failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_activate_order(RailServerContext* context,
+ RAIL_ACTIVATE_ORDER* activate, wStream* s)
+{
+ UINT error = 0;
+
+ if (!context || !activate || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_activate_order(s, activate)))
+ {
+ WLog_ERR(TAG, "rail_read_activate_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientActivate, error, context, activate);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientActivate failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_sysmenu_order(RailServerContext* context, RAIL_SYSMENU_ORDER* sysmenu,
+ wStream* s)
+{
+ UINT error = 0;
+
+ if (!context || !sysmenu || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_sysmenu_order(s, sysmenu)))
+ {
+ WLog_ERR(TAG, "rail_read_sysmenu_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientSysmenu, error, context, sysmenu);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientSysmenu failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_syscommand_order(RailServerContext* context,
+ RAIL_SYSCOMMAND_ORDER* syscommand, wStream* s)
+{
+ UINT error = 0;
+
+ if (!context || !syscommand || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_syscommand_order(s, syscommand)))
+ {
+ WLog_ERR(TAG, "rail_read_syscommand_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientSyscommand, error, context, syscommand);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientSyscommand failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_notify_event_order(RailServerContext* context,
+ RAIL_NOTIFY_EVENT_ORDER* notifyEvent, wStream* s)
+{
+ UINT error = 0;
+
+ if (!context || !notifyEvent || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_notify_event_order(s, notifyEvent)))
+ {
+ WLog_ERR(TAG, "rail_read_notify_event_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientNotifyEvent, error, context, notifyEvent);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientNotifyEvent failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_window_move_order(RailServerContext* context,
+ RAIL_WINDOW_MOVE_ORDER* windowMove, wStream* s)
+{
+ UINT error = 0;
+
+ if (!context || !windowMove || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_window_move_order(s, windowMove)))
+ {
+ WLog_ERR(TAG, "rail_read_window_move_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientWindowMove, error, context, windowMove);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientWindowMove failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_snap_arrange_order(RailServerContext* context,
+ RAIL_SNAP_ARRANGE* snapArrange, wStream* s)
+{
+ UINT error = 0;
+
+ if (!context || !snapArrange || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_snap_arange_order(s, snapArrange)))
+ {
+ WLog_ERR(TAG, "rail_read_snap_arange_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientSnapArrange, error, context, snapArrange);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientSnapArrange failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_get_appid_req_order(RailServerContext* context,
+ RAIL_GET_APPID_REQ_ORDER* getAppidReq, wStream* s)
+{
+ UINT error = 0;
+
+ if (!context || !getAppidReq || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_get_appid_req_order(s, getAppidReq)))
+ {
+ WLog_ERR(TAG, "rail_read_get_appid_req_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientGetAppidReq, error, context, getAppidReq);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientGetAppidReq failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_langbar_info_order(RailServerContext* context,
+ RAIL_LANGBAR_INFO_ORDER* langbarInfo, wStream* s)
+{
+ UINT error = 0;
+
+ if (!context || !langbarInfo || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_langbar_info_order(s, langbarInfo)))
+ {
+ WLog_ERR(TAG, "rail_read_langbar_info_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientLangbarInfo, error, context, langbarInfo);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientLangbarInfo failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_language_ime_info_order(RailServerContext* context,
+ RAIL_LANGUAGEIME_INFO_ORDER* languageImeInfo,
+ wStream* s)
+{
+ UINT error = 0;
+
+ if (!context || !languageImeInfo || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_language_ime_info_order(s, languageImeInfo)))
+ {
+ WLog_ERR(TAG, "rail_read_language_ime_info_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientLanguageImeInfo, error, context, languageImeInfo);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientLanguageImeInfo failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_compartment_info(RailServerContext* context,
+ RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo,
+ wStream* s)
+{
+ UINT error = 0;
+
+ if (!context || !compartmentInfo || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_compartment_info_order(s, compartmentInfo)))
+ {
+ WLog_ERR(TAG, "rail_read_compartment_info_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientCompartmentInfo, error, context, compartmentInfo);
+
+ if (error)
+ WLog_ERR(TAG, "context.ClientCompartmentInfo failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_recv_client_cloak_order(RailServerContext* context, RAIL_CLOAK* cloak, wStream* s)
+{
+ UINT error = 0;
+
+ if (!context || !cloak || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((error = rail_read_cloak_order(s, cloak)))
+ {
+ WLog_ERR(TAG, "rail_read_cloak_order failed with error %" PRIu32 "!", error);
+ return error;
+ }
+
+ IFCALLRET(context->ClientCloak, error, context, cloak);
+
+ if (error)
+ WLog_ERR(TAG, "context.Cloak failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+static UINT rail_recv_client_text_scale_order(RailServerContext* context, wStream* s)
+{
+ UINT error = CHANNEL_RC_OK;
+ UINT32 TextScaleFactor = 0;
+
+ if (!context || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, TextScaleFactor);
+ IFCALLRET(context->ClientTextScale, error, context, TextScaleFactor);
+
+ if (error)
+ WLog_ERR(TAG, "context.TextScale failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+static UINT rail_recv_client_caret_blink(RailServerContext* context, wStream* s)
+{
+ UINT error = CHANNEL_RC_OK;
+ UINT32 CaretBlinkRate = 0;
+
+ if (!context || !s)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, CaretBlinkRate);
+ IFCALLRET(context->ClientCaretBlinkRate, error, context, CaretBlinkRate);
+
+ if (error)
+ WLog_ERR(TAG, "context.CaretBlinkRate failed with error %" PRIu32 "", error);
+
+ return error;
+}
+
+static DWORD WINAPI rail_server_thread(LPVOID arg)
+{
+ RailServerContext* context = (RailServerContext*)arg;
+ RailServerPrivate* priv = context->priv;
+ DWORD status = 0;
+ DWORD nCount = 0;
+ HANDLE events[8];
+ UINT error = CHANNEL_RC_OK;
+ events[nCount++] = priv->channelEvent;
+ events[nCount++] = priv->stopEvent;
+
+ while (TRUE)
+ {
+ status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
+
+ if (status == WAIT_FAILED)
+ {
+ error = GetLastError();
+ WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "!", error);
+ break;
+ }
+
+ status = WaitForSingleObject(context->priv->stopEvent, 0);
+
+ if (status == WAIT_FAILED)
+ {
+ error = GetLastError();
+ WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
+ break;
+ }
+
+ if (status == WAIT_OBJECT_0)
+ break;
+
+ status = WaitForSingleObject(context->priv->channelEvent, 0);
+
+ if (status == WAIT_FAILED)
+ {
+ error = GetLastError();
+ WLog_ERR(
+ TAG,
+ "WaitForSingleObject(context->priv->channelEvent, 0) failed with error %" PRIu32
+ "!",
+ error);
+ break;
+ }
+
+ if (status == WAIT_OBJECT_0)
+ {
+ if ((error = rail_server_handle_messages(context)))
+ {
+ WLog_ERR(TAG, "rail_server_handle_messages failed with error %" PRIu32 "", error);
+ break;
+ }
+ }
+ }
+
+ if (error && context->rdpcontext)
+ setChannelError(context->rdpcontext, error, "rail_server_thread reported an error");
+
+ ExitThread(error);
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rail_server_start(RailServerContext* context)
+{
+ void* buffer = NULL;
+ DWORD bytesReturned = 0;
+ RailServerPrivate* priv = context->priv;
+ UINT error = ERROR_INTERNAL_ERROR;
+ priv->rail_channel =
+ WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, RAIL_SVC_CHANNEL_NAME);
+
+ if (!priv->rail_channel)
+ {
+ WLog_ERR(TAG, "WTSVirtualChannelOpen failed!");
+ return error;
+ }
+
+ if (!WTSVirtualChannelQuery(priv->rail_channel, WTSVirtualEventHandle, &buffer,
+ &bytesReturned) ||
+ (bytesReturned != sizeof(HANDLE)))
+ {
+ WLog_ERR(TAG,
+ "error during WTSVirtualChannelQuery(WTSVirtualEventHandle) or invalid returned "
+ "size(%" PRIu32 ")",
+ bytesReturned);
+
+ if (buffer)
+ WTSFreeMemory(buffer);
+
+ goto out_close;
+ }
+
+ CopyMemory(&priv->channelEvent, buffer, sizeof(HANDLE));
+ WTSFreeMemory(buffer);
+ context->priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (!context->priv->stopEvent)
+ {
+ WLog_ERR(TAG, "CreateEvent failed!");
+ goto out_close;
+ }
+
+ context->priv->thread = CreateThread(NULL, 0, rail_server_thread, (void*)context, 0, NULL);
+
+ if (!context->priv->thread)
+ {
+ WLog_ERR(TAG, "CreateThread failed!");
+ goto out_stop_event;
+ }
+
+ return CHANNEL_RC_OK;
+out_stop_event:
+ CloseHandle(context->priv->stopEvent);
+ context->priv->stopEvent = NULL;
+out_close:
+ WTSVirtualChannelClose(context->priv->rail_channel);
+ context->priv->rail_channel = NULL;
+ return error;
+}
+
+static BOOL rail_server_stop(RailServerContext* context)
+{
+ RailServerPrivate* priv = (RailServerPrivate*)context->priv;
+
+ if (priv->thread)
+ {
+ SetEvent(priv->stopEvent);
+
+ if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
+ {
+ WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", GetLastError());
+ return FALSE;
+ }
+
+ CloseHandle(priv->thread);
+ CloseHandle(priv->stopEvent);
+ priv->thread = NULL;
+ priv->stopEvent = NULL;
+ }
+
+ if (priv->rail_channel)
+ {
+ WTSVirtualChannelClose(priv->rail_channel);
+ priv->rail_channel = NULL;
+ }
+
+ priv->channelEvent = NULL;
+ return TRUE;
+}
+
+RailServerContext* rail_server_context_new(HANDLE vcm)
+{
+ RailServerContext* context = NULL;
+ RailServerPrivate* priv = NULL;
+ context = (RailServerContext*)calloc(1, sizeof(RailServerContext));
+
+ if (!context)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ return NULL;
+ }
+
+ context->vcm = vcm;
+ context->Start = rail_server_start;
+ context->Stop = rail_server_stop;
+ context->ServerHandshake = rail_send_server_handshake;
+ context->ServerHandshakeEx = rail_send_server_handshake_ex;
+ context->ServerSysparam = rail_send_server_sysparam;
+ context->ServerLocalMoveSize = rail_send_server_local_move_size;
+ context->ServerMinMaxInfo = rail_send_server_min_max_info;
+ context->ServerTaskbarInfo = rail_send_server_taskbar_info;
+ context->ServerLangbarInfo = rail_send_server_langbar_info;
+ context->ServerExecResult = rail_send_server_exec_result;
+ context->ServerGetAppidResp = rail_send_server_get_app_id_resp;
+ context->ServerZOrderSync = rail_send_server_z_order_sync;
+ context->ServerCloak = rail_send_server_cloak;
+ context->ServerPowerDisplayRequest = rail_send_server_power_display_request;
+ context->ServerGetAppidRespEx = rail_send_server_get_appid_resp_ex;
+ context->priv = priv = (RailServerPrivate*)calloc(1, sizeof(RailServerPrivate));
+
+ if (!priv)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ goto out_free;
+ }
+
+ /* Create shared input stream */
+ priv->input_stream = Stream_New(NULL, 4096);
+
+ if (!priv->input_stream)
+ {
+ WLog_ERR(TAG, "Stream_New failed!");
+ goto out_free_priv;
+ }
+
+ return context;
+out_free_priv:
+ free(context->priv);
+out_free:
+ free(context);
+ return NULL;
+}
+
+void rail_server_context_free(RailServerContext* context)
+{
+ if (context->priv)
+ Stream_Free(context->priv->input_stream, TRUE);
+
+ free(context->priv);
+ free(context);
+}
+
+void rail_server_set_handshake_ex_flags(RailServerContext* context, DWORD flags)
+{
+ RailServerPrivate* priv = NULL;
+
+ if (!context || !context->priv)
+ return;
+
+ priv = context->priv;
+ priv->channelFlags = flags;
+}
+
+UINT rail_server_handle_messages(RailServerContext* context)
+{
+ char buffer[128] = { 0 };
+ UINT status = CHANNEL_RC_OK;
+ DWORD bytesReturned = 0;
+ UINT16 orderType = 0;
+ UINT16 orderLength = 0;
+ RailServerPrivate* priv = context->priv;
+ wStream* s = priv->input_stream;
+
+ /* Read header */
+ if (!Stream_EnsureRemainingCapacity(s, RAIL_PDU_HEADER_LENGTH))
+ {
+ WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed, RAIL_PDU_HEADER_LENGTH");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ if (!WTSVirtualChannelRead(priv->rail_channel, 0, Stream_Pointer(s), RAIL_PDU_HEADER_LENGTH,
+ &bytesReturned))
+ {
+ if (GetLastError() == ERROR_NO_DATA)
+ return ERROR_NO_DATA;
+
+ WLog_ERR(TAG, "channel connection closed");
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ /* Parse header */
+ if ((status = rail_read_pdu_header(s, &orderType, &orderLength)) != CHANNEL_RC_OK)
+ {
+ WLog_ERR(TAG, "rail_read_pdu_header failed with error %" PRIu32 "!", status);
+ return status;
+ }
+
+ if (!Stream_EnsureRemainingCapacity(s, orderLength - RAIL_PDU_HEADER_LENGTH))
+ {
+ WLog_ERR(TAG,
+ "Stream_EnsureRemainingCapacity failed, orderLength - RAIL_PDU_HEADER_LENGTH");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ /* Read body */
+ if (!WTSVirtualChannelRead(priv->rail_channel, 0, Stream_Pointer(s),
+ orderLength - RAIL_PDU_HEADER_LENGTH, &bytesReturned))
+ {
+ if (GetLastError() == ERROR_NO_DATA)
+ return ERROR_NO_DATA;
+
+ WLog_ERR(TAG, "channel connection closed");
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ WLog_DBG(TAG, "Received %s PDU, length:%" PRIu16 "",
+ rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
+
+ switch (orderType)
+ {
+ case TS_RAIL_ORDER_HANDSHAKE:
+ {
+ RAIL_HANDSHAKE_ORDER handshake;
+ return rail_recv_client_handshake_order(context, &handshake, s);
+ }
+
+ case TS_RAIL_ORDER_CLIENTSTATUS:
+ {
+ RAIL_CLIENT_STATUS_ORDER clientStatus;
+ return rail_recv_client_client_status_order(context, &clientStatus, s);
+ }
+
+ case TS_RAIL_ORDER_EXEC:
+ return rail_recv_client_exec_order(context, s);
+
+ case TS_RAIL_ORDER_SYSPARAM:
+ {
+ RAIL_SYSPARAM_ORDER sysparam = { 0 };
+ return rail_recv_client_sysparam_order(context, &sysparam, s);
+ }
+
+ case TS_RAIL_ORDER_ACTIVATE:
+ {
+ RAIL_ACTIVATE_ORDER activate;
+ return rail_recv_client_activate_order(context, &activate, s);
+ }
+
+ case TS_RAIL_ORDER_SYSMENU:
+ {
+ RAIL_SYSMENU_ORDER sysmenu;
+ return rail_recv_client_sysmenu_order(context, &sysmenu, s);
+ }
+
+ case TS_RAIL_ORDER_SYSCOMMAND:
+ {
+ RAIL_SYSCOMMAND_ORDER syscommand;
+ return rail_recv_client_syscommand_order(context, &syscommand, s);
+ }
+
+ case TS_RAIL_ORDER_NOTIFY_EVENT:
+ {
+ RAIL_NOTIFY_EVENT_ORDER notifyEvent;
+ return rail_recv_client_notify_event_order(context, &notifyEvent, s);
+ }
+
+ case TS_RAIL_ORDER_WINDOWMOVE:
+ {
+ RAIL_WINDOW_MOVE_ORDER windowMove;
+ return rail_recv_client_window_move_order(context, &windowMove, s);
+ }
+
+ case TS_RAIL_ORDER_SNAP_ARRANGE:
+ {
+ RAIL_SNAP_ARRANGE snapArrange;
+ return rail_recv_client_snap_arrange_order(context, &snapArrange, s);
+ }
+
+ case TS_RAIL_ORDER_GET_APPID_REQ:
+ {
+ RAIL_GET_APPID_REQ_ORDER getAppidReq;
+ return rail_recv_client_get_appid_req_order(context, &getAppidReq, s);
+ }
+
+ case TS_RAIL_ORDER_LANGBARINFO:
+ {
+ RAIL_LANGBAR_INFO_ORDER langbarInfo;
+ return rail_recv_client_langbar_info_order(context, &langbarInfo, s);
+ }
+
+ case TS_RAIL_ORDER_LANGUAGEIMEINFO:
+ {
+ RAIL_LANGUAGEIME_INFO_ORDER languageImeInfo;
+ return rail_recv_client_language_ime_info_order(context, &languageImeInfo, s);
+ }
+
+ case TS_RAIL_ORDER_COMPARTMENTINFO:
+ {
+ RAIL_COMPARTMENT_INFO_ORDER compartmentInfo;
+ return rail_recv_client_compartment_info(context, &compartmentInfo, s);
+ }
+
+ case TS_RAIL_ORDER_CLOAK:
+ {
+ RAIL_CLOAK cloak;
+ return rail_recv_client_cloak_order(context, &cloak, s);
+ }
+
+ case TS_RAIL_ORDER_TEXTSCALEINFO:
+ {
+ return rail_recv_client_text_scale_order(context, s);
+ }
+
+ case TS_RAIL_ORDER_CARETBLINKINFO:
+ {
+ return rail_recv_client_caret_blink(context, s);
+ }
+
+ default:
+ WLog_ERR(TAG, "Unknown RAIL PDU order received.");
+ return ERROR_INVALID_DATA;
+ }
+}
diff --git a/channels/rail/server/rail_main.h b/channels/rail/server/rail_main.h
new file mode 100644
index 0000000..f15cf19
--- /dev/null
+++ b/channels/rail/server/rail_main.h
@@ -0,0 +1,44 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * RAIL Virtual Channel Plugin
+ *
+ * Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
+ *
+ * Licensed 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 FREERDP_CHANNEL_RAIL_SERVER_MAIN_H
+#define FREERDP_CHANNEL_RAIL_SERVER_MAIN_H
+
+#include <freerdp/rail.h>
+#include <freerdp/server/rail.h>
+
+#include <winpr/crt.h>
+#include <winpr/wlog.h>
+#include <winpr/stream.h>
+
+#include "../rail_common.h"
+
+struct s_rail_server_private
+{
+ HANDLE thread;
+ HANDLE stopEvent;
+ HANDLE channelEvent;
+ void* rail_channel;
+
+ wStream* input_stream;
+
+ DWORD channelFlags;
+};
+
+#endif /* FREERDP_CHANNEL_RAIL_SERVER_MAIN_H */