From bca442a4774b77bd959189e5d8fc89df9d391dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 16 Aug 2013 21:17:24 -0400 Subject: [PATCH] channels/cliprdr: start exchanging messages with server-side cliprdr --- channels/cliprdr/client/CMakeLists.txt | 1 - channels/cliprdr/client/cliprdr_constants.h | 58 ----- channels/cliprdr/client/cliprdr_format.c | 1 - channels/cliprdr/client/cliprdr_main.c | 1 - channels/cliprdr/client/cliprdr_main.h | 8 - channels/cliprdr/server/cliprdr_main.c | 253 +++++++++++++++++++- channels/cliprdr/server/cliprdr_main.h | 14 ++ channels/server/channels.c | 10 +- include/freerdp/channels/wtsvc.h | 2 + include/freerdp/client/cliprdr.h | 42 ++++ 10 files changed, 316 insertions(+), 74 deletions(-) delete mode 100644 channels/cliprdr/client/cliprdr_constants.h diff --git a/channels/cliprdr/client/CMakeLists.txt b/channels/cliprdr/client/CMakeLists.txt index f1dd07440..00c5605a0 100644 --- a/channels/cliprdr/client/CMakeLists.txt +++ b/channels/cliprdr/client/CMakeLists.txt @@ -18,7 +18,6 @@ define_channel_client("cliprdr") set(${MODULE_PREFIX}_SRCS - cliprdr_constants.h cliprdr_format.c cliprdr_format.h cliprdr_main.c diff --git a/channels/cliprdr/client/cliprdr_constants.h b/channels/cliprdr/client/cliprdr_constants.h deleted file mode 100644 index e3e141986..000000000 --- a/channels/cliprdr/client/cliprdr_constants.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * Clipboard Virtual Channel - * - * Copyright 2009-2011 Jay Sorg - * Copyright 2010-2011 Vic Lee - * - * 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 __CLIPRDR_CONSTANTS -#define __CLIPRDR_CONSTANTS - -/* CLIPRDR_HEADER.msgType */ -#define CB_MONITOR_READY 0x0001 -#define CB_FORMAT_LIST 0x0002 -#define CB_FORMAT_LIST_RESPONSE 0x0003 -#define CB_FORMAT_DATA_REQUEST 0x0004 -#define CB_FORMAT_DATA_RESPONSE 0x0005 -#define CB_TEMP_DIRECTORY 0x0006 -#define CB_CLIP_CAPS 0x0007 -#define CB_FILECONTENTS_REQUEST 0x0008 -#define CB_FILECONTENTS_RESPONSE 0x0009 -#define CB_LOCK_CLIPDATA 0x000A -#define CB_UNLOCK_CLIPDATA 0x000B - -/* CLIPRDR_HEADER.msgFlags */ -#define CB_RESPONSE_OK 0x0001 -#define CB_RESPONSE_FAIL 0x0002 -#define CB_ASCII_NAMES 0x0004 - -/* CLIPRDR_CAPS_SET.capabilitySetType */ -#define CB_CAPSTYPE_GENERAL 0x0001 - -/* CLIPRDR_GENERAL_CAPABILITY.lengthCapability */ -#define CB_CAPSTYPE_GENERAL_LEN 12 - -/* CLIPRDR_GENERAL_CAPABILITY.version */ -#define CB_CAPS_VERSION_1 0x00000001 -#define CB_CAPS_VERSION_2 0x00000002 - -/* CLIPRDR_GENERAL_CAPABILITY.generalFlags */ -#define CB_USE_LONG_FORMAT_NAMES 0x00000002 -#define CB_STREAM_FILECLIP_ENABLED 0x00000004 -#define CB_FILECLIP_NO_FILE_PATHS 0x00000008 -#define CB_CAN_LOCK_CLIPDATA 0x00000010 - -#endif /* __CLIPRDR_CONSTANTS */ diff --git a/channels/cliprdr/client/cliprdr_format.c b/channels/cliprdr/client/cliprdr_format.c index 426475a64..6345cadef 100644 --- a/channels/cliprdr/client/cliprdr_format.c +++ b/channels/cliprdr/client/cliprdr_format.c @@ -34,7 +34,6 @@ #include #include -#include "cliprdr_constants.h" #include "cliprdr_main.h" #include "cliprdr_format.h" diff --git a/channels/cliprdr/client/cliprdr_main.c b/channels/cliprdr/client/cliprdr_main.c index f83abba7a..0ed0d20e9 100644 --- a/channels/cliprdr/client/cliprdr_main.c +++ b/channels/cliprdr/client/cliprdr_main.c @@ -33,7 +33,6 @@ #include #include -#include "cliprdr_constants.h" #include "cliprdr_main.h" #include "cliprdr_format.h" diff --git a/channels/cliprdr/client/cliprdr_main.h b/channels/cliprdr/client/cliprdr_main.h index b2b984d31..38d361031 100644 --- a/channels/cliprdr/client/cliprdr_main.h +++ b/channels/cliprdr/client/cliprdr_main.h @@ -25,14 +25,6 @@ #include -struct _CLIPRDR_FORMAT_NAME -{ - UINT32 id; - char* name; - int length; -}; -typedef struct _CLIPRDR_FORMAT_NAME CLIPRDR_FORMAT_NAME; - struct cliprdr_plugin { rdpSvcPlugin plugin; diff --git a/channels/cliprdr/server/cliprdr_main.c b/channels/cliprdr/server/cliprdr_main.c index 732351304..c8db646a1 100644 --- a/channels/cliprdr/server/cliprdr_main.c +++ b/channels/cliprdr/server/cliprdr_main.c @@ -22,6 +22,7 @@ #endif #include +#include #include #include "cliprdr_main.h" @@ -41,11 +42,69 @@ static int cliprdr_server_send_capabilities(CliprdrServerContext* context) { + wStream* s; + BOOL status; + UINT32 generalFlags; + CLIPRDR_HEADER header; + + printf("CliprdrServerSendCapabilities\n"); + + header.msgType = CB_CLIP_CAPS; + header.msgFlags = 0; + header.dataLen = 24; + + generalFlags = 0; + + if (context->priv->UseLongFormatNames) + generalFlags |= CB_USE_LONG_FORMAT_NAMES; + + s = Stream_New(NULL, header.dataLen); + + Stream_Write_UINT16(s, header.msgType); /* msgType (2 bytes) */ + Stream_Write_UINT16(s, header.msgFlags); /* msgFlags (2 bytes) */ + Stream_Write_UINT32(s, header.dataLen); /* dataLen (4 bytes) */ + + Stream_Write_UINT16(s, 1); /* cCapabilitiesSets (2 bytes) */ + Stream_Write_UINT16(s, 0); /* pad1 (2 bytes) */ + + Stream_Write_UINT16(s, CB_CAPSTYPE_GENERAL); /* capabilitySetType (2 bytes) */ + Stream_Write_UINT16(s, CB_CAPSTYPE_GENERAL_LEN); /* lengthCapability (2 bytes) */ + Stream_Write_UINT32(s, CB_CAPS_VERSION_2); /* version (4 bytes) */ + Stream_Write_UINT32(s, generalFlags); /* generalFlags (4 bytes) */ + + Stream_SealLength(s); + + status = WTSVirtualChannelWrite(context->priv->ChannelHandle, Stream_Buffer(s), Stream_Length(s), NULL); + + Stream_Free(s, TRUE); + return 0; } static int cliprdr_server_send_monitor_ready(CliprdrServerContext* context) { + wStream* s; + BOOL status; + CLIPRDR_HEADER header; + + printf("CliprdrServerSendMonitorReady\n"); + + header.msgType = CB_MONITOR_READY; + header.msgFlags = 0; + header.dataLen = 8; + + s = Stream_New(NULL, header.dataLen); + + Stream_Write_UINT16(s, header.msgType); /* msgType (2 bytes) */ + Stream_Write_UINT16(s, header.msgFlags); /* msgFlags (2 bytes) */ + Stream_Write_UINT32(s, header.dataLen); /* dataLen (4 bytes) */ + + Stream_SealLength(s); + + status = WTSVirtualChannelWrite(context->priv->ChannelHandle, Stream_Buffer(s), Stream_Length(s), NULL); + + Stream_Free(s, TRUE); + return 0; } @@ -54,6 +113,184 @@ static int cliprdr_server_send_format_list_response(CliprdrServerContext* contex return 0; } +static int cliprdr_server_receive_capabilities(CliprdrServerContext* context, wStream* s, CLIPRDR_HEADER* header) +{ + UINT32 version; + UINT32 generalFlags; + UINT16 cCapabilitiesSets; + UINT16 capabilitySetType; + UINT16 lengthCapability; + + Stream_Read_UINT16(s, cCapabilitiesSets); /* cCapabilitiesSets (2 bytes) */ + Stream_Seek_UINT16(s); /* pad1 (2 bytes) */ + + Stream_Read_UINT16(s, capabilitySetType); /* capabilitySetType (2 bytes) */ + Stream_Read_UINT16(s, lengthCapability); /* lengthCapability (2 bytes) */ + + Stream_Read_UINT32(s, version); /* version (4 bytes) */ + Stream_Read_UINT32(s, generalFlags); /* generalFlags (4 bytes) */ + + if (generalFlags & CB_USE_LONG_FORMAT_NAMES) + context->priv->UseLongFormatNames = TRUE; + + if (generalFlags & CB_STREAM_FILECLIP_ENABLED) + context->priv->StreamFileClipEnabled = TRUE; + + if (generalFlags & CB_FILECLIP_NO_FILE_PATHS) + context->priv->FileClipNoFilePaths = TRUE; + + if (generalFlags & CB_CAN_LOCK_CLIPDATA) + context->priv->CanLockClipData = TRUE; + + return 0; +} + +int cliprdr_wcslen(const WCHAR* str, const WCHAR* end) +{ + WCHAR* p = (WCHAR*) str; + + if (!p) + return -1; + + while (*p) + { + if (p == end) + return -1; + + p++; + } + + return (p - str); +} + +static int cliprdr_server_receive_long_format_list(CliprdrServerContext* context, wStream* s, CLIPRDR_HEADER* header) +{ + int i; + WCHAR* end; + int length; + int position; + + position = Stream_GetPosition(s); + end = (WCHAR*) &(Stream_Buffer(s)[Stream_Length(s)]); + + winpr_HexDump(Stream_Buffer(s), Stream_Length(s)); + + context->priv->ClientFormatNameCount = 0; + + while (Stream_GetRemainingLength(s) >= 6) + { + Stream_Seek(s, 4); /* formatId (4 bytes) */ + + length = cliprdr_wcslen((WCHAR*) Stream_Pointer(s), end); + + if (length < 0) + return -1; + + Stream_Seek(s, (length + 1) * 2); /* wszFormatName */ + + context->priv->ClientFormatNameCount = 0; + } + + context->priv->ClientFormatNames = (CLIPRDR_FORMAT_NAME*) + malloc(sizeof(CLIPRDR_FORMAT_NAME) * context->priv->ClientFormatNameCount); + + Stream_SetPosition(s, position); + + for (i = 0; i < context->priv->ClientFormatNameCount; i++) + { + Stream_Read_UINT32(s, context->priv->ClientFormatNames[i].id); /* formatId (4 bytes) */ + + context->priv->ClientFormatNames[i].length = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(s), + -1, &context->priv->ClientFormatNames[i].name, 0, NULL, NULL); + + Stream_Seek(s, (context->priv->ClientFormatNames[i].length + 1) * 2); + } + + for (i = 0; i < context->priv->ClientFormatNameCount; i++) + { + printf("Format %d: Id: 0x%04X Name: %s\n", i, + context->priv->ClientFormatNames[i].id, + context->priv->ClientFormatNames[i].name); + } + + return 0; +} + +static int cliprdr_server_receive_short_format_list(CliprdrServerContext* context, wStream* s, CLIPRDR_HEADER* header) +{ + printf("%s: unimplemented\n", __FUNCTION__); + return 0; +} + +static int cliprdr_server_receive_format_list(CliprdrServerContext* context, wStream* s, CLIPRDR_HEADER* header) +{ + int status; + + if (context->priv->UseLongFormatNames) + { + status = cliprdr_server_receive_long_format_list(context, s, header); + } + else + { + status = cliprdr_server_receive_short_format_list(context, s, header); + } + + return status; +} + +static int cliprdr_server_receive_pdu(CliprdrServerContext* context, wStream* s) +{ + CLIPRDR_HEADER header; + + Stream_Read_UINT16(s, header.msgType); /* msgType (2 bytes) */ + Stream_Read_UINT16(s, header.msgFlags); /* msgFlags (2 bytes) */ + Stream_Read_UINT32(s, header.dataLen); /* dataLen (4 bytes) */ + + printf("CliprdrServerReceivePdu: msgType: %d msgFlags: 0x%08X dataLen: %d\n", + header.msgType, header.msgFlags, header.dataLen); + + switch (header.msgType) + { + case CB_CLIP_CAPS: + cliprdr_server_receive_capabilities(context, s, &header); + break; + + case CB_TEMP_DIRECTORY: + break; + + case CB_FORMAT_LIST: + cliprdr_server_receive_format_list(context, s, &header); + break; + + case CB_FORMAT_LIST_RESPONSE: + break; + + case CB_LOCK_CLIPDATA: + break; + + case CB_UNLOCK_CLIPDATA: + break; + + case CB_FORMAT_DATA_REQUEST: + break; + + case CB_FORMAT_DATA_RESPONSE: + break; + + case CB_FILECONTENTS_REQUEST: + break; + + case CB_FILECONTENTS_RESPONSE: + break; + + default: + printf("Unexpected clipboard PDU type: %d\n", header.msgType); + break; + } + + return 0; +} + static void* cliprdr_server_thread(void* arg) { wStream* s; @@ -85,6 +322,9 @@ static void* cliprdr_server_thread(void* arg) events[nCount++] = ChannelEvent; events[nCount++] = context->priv->StopEvent; + context->SendCapabilities(context); + context->SendMonitorReady(context); + while (1) { status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); @@ -97,17 +337,24 @@ static void* cliprdr_server_thread(void* arg) if (WTSVirtualChannelRead(context->priv->ChannelHandle, 0, Stream_Buffer(s), Stream_Capacity(s), &BytesReturned) == FALSE) { - if (BytesReturned == 0) + if (!BytesReturned) break; - Stream_EnsureRemainingCapacity(s, (int) BytesReturned); + Stream_EnsureRemainingCapacity(s, BytesReturned); if (WTSVirtualChannelRead(context->priv->ChannelHandle, 0, Stream_Buffer(s), Stream_Capacity(s), &BytesReturned) == FALSE) { break; } + + Stream_Seek(s, BytesReturned); } + + Stream_SealLength(s); + Stream_SetPosition(s, 0); + + cliprdr_server_receive_pdu(context, s); } Stream_Free(s, TRUE); @@ -164,6 +411,8 @@ CliprdrServerContext* cliprdr_server_context_new(WTSVirtualChannelManager* vcm) if (context->priv) { ZeroMemory(context->priv, sizeof(CliprdrServerPrivate)); + + context->priv->UseLongFormatNames = TRUE; } } diff --git a/channels/cliprdr/server/cliprdr_main.h b/channels/cliprdr/server/cliprdr_main.h index 540c33397..b72902b64 100644 --- a/channels/cliprdr/server/cliprdr_main.h +++ b/channels/cliprdr/server/cliprdr_main.h @@ -26,11 +26,25 @@ #include +struct _CLIPRDR_HEADER +{ + UINT16 msgType; + UINT16 msgFlags; + UINT32 dataLen; +}; +typedef struct _CLIPRDR_HEADER CLIPRDR_HEADER; + struct _cliprdr_server_private { HANDLE Thread; HANDLE StopEvent; void* ChannelHandle; + BOOL UseLongFormatNames; + BOOL StreamFileClipEnabled; + BOOL FileClipNoFilePaths; + BOOL CanLockClipData; + UINT32 ClientFormatNameCount; + CLIPRDR_FORMAT_NAME* ClientFormatNames; }; #endif /* FREERDP_CHANNEL_SERVER_CLIPRDR_MAIN_H */ diff --git a/channels/server/channels.c b/channels/server/channels.c index 2bef6414b..174df7f48 100644 --- a/channels/server/channels.c +++ b/channels/server/channels.c @@ -474,8 +474,7 @@ void WTSDestroyVirtualChannelManager(WTSVirtualChannelManager* vcm) } } -void WTSVirtualChannelManagerGetFileDescriptor(WTSVirtualChannelManager* vcm, - void** fds, int* fds_count) +void WTSVirtualChannelManagerGetFileDescriptor(WTSVirtualChannelManager* vcm, void** fds, int* fds_count) { void* fd; @@ -543,6 +542,11 @@ BOOL WTSVirtualChannelManagerCheckFileDescriptor(WTSVirtualChannelManager* vcm) return result; } +HANDLE WTSVirtualChannelManagerGetEventHandle(WTSVirtualChannelManager* vcm) +{ + return vcm->send_event; +} + void* WTSVirtualChannelOpenEx( /* __in */ WTSVirtualChannelManager* vcm, /* __in */ const char* pVirtualName, @@ -786,7 +790,7 @@ BOOL WTSVirtualChannelWrite( int first; UINT32 written; - if (channel == NULL) + if (!channel) return FALSE; if (channel->channel_type == RDP_PEER_CHANNEL_TYPE_SVC) diff --git a/include/freerdp/channels/wtsvc.h b/include/freerdp/channels/wtsvc.h index 1b59d8ea2..118cfdaa8 100644 --- a/include/freerdp/channels/wtsvc.h +++ b/include/freerdp/channels/wtsvc.h @@ -60,6 +60,8 @@ FREERDP_API void WTSVirtualChannelManagerGetFileDescriptor(WTSVirtualChannelMana void** fds, int* fds_count); FREERDP_API BOOL WTSVirtualChannelManagerCheckFileDescriptor(WTSVirtualChannelManager* vcm); +FREERDP_API HANDLE WTSVirtualChannelManagerGetEventHandle(WTSVirtualChannelManager* vcm); + /** * Opens a static or dynamic virtual channel and return the handle. If the * operation fails, a NULL handle is returned. diff --git a/include/freerdp/client/cliprdr.h b/include/freerdp/client/cliprdr.h index a0455dcf4..6cf137eab 100644 --- a/include/freerdp/client/cliprdr.h +++ b/include/freerdp/client/cliprdr.h @@ -54,6 +54,48 @@ struct _cliprdr_client_context #define CB_FORMAT_JPEG 0xD012 #define CB_FORMAT_GIF 0xD013 +/* CLIPRDR_HEADER.msgType */ +#define CB_MONITOR_READY 0x0001 +#define CB_FORMAT_LIST 0x0002 +#define CB_FORMAT_LIST_RESPONSE 0x0003 +#define CB_FORMAT_DATA_REQUEST 0x0004 +#define CB_FORMAT_DATA_RESPONSE 0x0005 +#define CB_TEMP_DIRECTORY 0x0006 +#define CB_CLIP_CAPS 0x0007 +#define CB_FILECONTENTS_REQUEST 0x0008 +#define CB_FILECONTENTS_RESPONSE 0x0009 +#define CB_LOCK_CLIPDATA 0x000A +#define CB_UNLOCK_CLIPDATA 0x000B + +/* CLIPRDR_HEADER.msgFlags */ +#define CB_RESPONSE_OK 0x0001 +#define CB_RESPONSE_FAIL 0x0002 +#define CB_ASCII_NAMES 0x0004 + +/* CLIPRDR_CAPS_SET.capabilitySetType */ +#define CB_CAPSTYPE_GENERAL 0x0001 + +/* CLIPRDR_GENERAL_CAPABILITY.lengthCapability */ +#define CB_CAPSTYPE_GENERAL_LEN 12 + +/* CLIPRDR_GENERAL_CAPABILITY.version */ +#define CB_CAPS_VERSION_1 0x00000001 +#define CB_CAPS_VERSION_2 0x00000002 + +/* CLIPRDR_GENERAL_CAPABILITY.generalFlags */ +#define CB_USE_LONG_FORMAT_NAMES 0x00000002 +#define CB_STREAM_FILECLIP_ENABLED 0x00000004 +#define CB_FILECLIP_NO_FILE_PATHS 0x00000008 +#define CB_CAN_LOCK_CLIPDATA 0x00000010 + +struct _CLIPRDR_FORMAT_NAME +{ + UINT32 id; + char* name; + int length; +}; +typedef struct _CLIPRDR_FORMAT_NAME CLIPRDR_FORMAT_NAME; + /** * Clipboard Events */