22#include <freerdp/config.h>
25#include <winpr/assert.h>
26#include <winpr/print.h>
27#include <winpr/stream.h>
29#include <freerdp/freerdp.h>
30#include <freerdp/channels/log.h>
31#include "cliprdr_main.h"
32#include "../cliprdr_common.h"
85 WINPR_ASSERT(cliprdr);
87 const size_t pos = Stream_GetPosition(s);
88 if ((pos < 8) || (pos > UINT32_MAX))
94 const UINT32 dataLen = (UINT32)(pos - 8);
95 if (!Stream_SetPosition(s, 4))
97 Stream_Write_UINT32(s, dataLen);
99 WINPR_ASSERT(pos <= UINT32_MAX);
100 status = WTSVirtualChannelWrite(cliprdr->ChannelHandle, Stream_BufferAs(s,
char), (UINT32)pos,
102 rc = status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
104 Stream_Free(s, TRUE);
113static UINT cliprdr_server_capabilities(CliprdrServerContext* context,
118 WINPR_ASSERT(context);
119 WINPR_ASSERT(capabilities);
123 if (capabilities->common.msgType != CB_CLIP_CAPS)
124 WLog_Print(cliprdr->log, WLOG_WARN,
"called with invalid type %08" PRIx32,
125 capabilities->common.msgType);
127 if (capabilities->cCapabilitiesSets > UINT16_MAX)
129 WLog_Print(cliprdr->log, WLOG_ERROR,
"Invalid number of capability sets in clipboard caps");
130 return ERROR_INVALID_PARAMETER;
133 wStream* s = cliprdr_packet_new(CB_CLIP_CAPS, 0, 4 + CB_CAPSTYPE_GENERAL_LEN);
137 WLog_Print(cliprdr->log, WLOG_ERROR,
"cliprdr_packet_new failed!");
138 return ERROR_INTERNAL_ERROR;
141 if ((capabilities->common.msgFlags & CB_RESPONSE_FAIL) == 0)
144 s, (UINT16)capabilities->cCapabilitiesSets);
145 Stream_Write_UINT16(s, 0);
146 for (UINT32 x = 0; x < capabilities->cCapabilitiesSets; x++)
150 (((
const BYTE*)capabilities->capabilitySets) + offset));
151 offset += cap->capabilitySetLength;
153 switch (cap->capabilitySetType)
155 case CB_CAPSTYPE_GENERAL:
161 generalCapabilitySet->capabilitySetType);
164 generalCapabilitySet->capabilitySetLength);
165 Stream_Write_UINT32(s, generalCapabilitySet->version);
167 s, generalCapabilitySet->generalFlags);
172 WLog_Print(cliprdr->log, WLOG_WARN,
"Unknown capability set type %08" PRIx16,
173 cap->capabilitySetType);
174 if (!Stream_SafeZero(s, cap->capabilitySetLength))
176 WLog_Print(cliprdr->log, WLOG_ERROR,
"short stream");
177 Stream_Free(s, TRUE);
178 return ERROR_NO_DATA;
184 WLog_Print(cliprdr->log, WLOG_DEBUG,
"ServerCapabilities");
185 return cliprdr_server_packet_send(cliprdr, s);
193static UINT cliprdr_server_monitor_ready(CliprdrServerContext* context,
196 WINPR_ASSERT(context);
197 WINPR_ASSERT(monitorReady);
201 if (monitorReady->common.msgType != CB_MONITOR_READY)
202 WLog_Print(cliprdr->log, WLOG_WARN,
"called with invalid type %08" PRIx32,
203 monitorReady->common.msgType);
205 wStream* s = cliprdr_packet_new(CB_MONITOR_READY, monitorReady->common.msgFlags,
206 monitorReady->common.dataLen);
210 WLog_Print(cliprdr->log, WLOG_ERROR,
"cliprdr_packet_new failed!");
211 return ERROR_INTERNAL_ERROR;
214 WLog_Print(cliprdr->log, WLOG_DEBUG,
"ServerMonitorReady");
215 return cliprdr_server_packet_send(cliprdr, s);
223static UINT cliprdr_server_format_list(CliprdrServerContext* context,
226 WINPR_ASSERT(context);
227 WINPR_ASSERT(formatList);
231 wStream* s = cliprdr_packet_format_list_new(formatList, context->useLongFormatNames, FALSE);
234 WLog_Print(cliprdr->log, WLOG_ERROR,
"cliprdr_packet_format_list_new failed!");
235 return ERROR_INTERNAL_ERROR;
238 WLog_Print(cliprdr->log, WLOG_DEBUG,
"ServerFormatList: numFormats: %" PRIu32
"",
239 formatList->numFormats);
240 return cliprdr_server_packet_send(cliprdr, s);
249cliprdr_server_format_list_response(CliprdrServerContext* context,
252 WINPR_ASSERT(context);
253 WINPR_ASSERT(formatListResponse);
256 if (formatListResponse->common.msgType != CB_FORMAT_LIST_RESPONSE)
257 WLog_Print(cliprdr->log, WLOG_WARN,
"called with invalid type %08" PRIx32,
258 formatListResponse->common.msgType);
260 wStream* s = cliprdr_packet_new(CB_FORMAT_LIST_RESPONSE, formatListResponse->common.msgFlags,
261 formatListResponse->common.dataLen);
265 WLog_Print(cliprdr->log, WLOG_ERROR,
"cliprdr_packet_new failed!");
266 return ERROR_INTERNAL_ERROR;
269 WLog_Print(cliprdr->log, WLOG_DEBUG,
"ServerFormatListResponse");
270 return cliprdr_server_packet_send(cliprdr, s);
278static UINT cliprdr_server_lock_clipboard_data(CliprdrServerContext* context,
281 WINPR_ASSERT(context);
282 WINPR_ASSERT(lockClipboardData);
285 if (lockClipboardData->common.msgType != CB_LOCK_CLIPDATA)
286 WLog_Print(cliprdr->log, WLOG_WARN,
"called with invalid type %08" PRIx32,
287 lockClipboardData->common.msgType);
289 wStream* s = cliprdr_packet_lock_clipdata_new(lockClipboardData);
292 WLog_Print(cliprdr->log, WLOG_ERROR,
"cliprdr_packet_lock_clipdata_new failed!");
293 return ERROR_INTERNAL_ERROR;
296 WLog_Print(cliprdr->log, WLOG_DEBUG,
"ServerLockClipboardData: clipDataId: 0x%08" PRIX32
"",
297 lockClipboardData->clipDataId);
298 return cliprdr_server_packet_send(cliprdr, s);
307cliprdr_server_unlock_clipboard_data(CliprdrServerContext* context,
310 WINPR_ASSERT(context);
311 WINPR_ASSERT(unlockClipboardData);
314 if (unlockClipboardData->common.msgType != CB_UNLOCK_CLIPDATA)
315 WLog_Print(cliprdr->log, WLOG_WARN,
"called with invalid type %08" PRIx32,
316 unlockClipboardData->common.msgType);
318 wStream* s = cliprdr_packet_unlock_clipdata_new(unlockClipboardData);
322 WLog_Print(cliprdr->log, WLOG_ERROR,
"cliprdr_packet_unlock_clipdata_new failed!");
323 return ERROR_INTERNAL_ERROR;
326 WLog_Print(cliprdr->log, WLOG_DEBUG,
"ServerUnlockClipboardData: clipDataId: 0x%08" PRIX32
"",
327 unlockClipboardData->clipDataId);
328 return cliprdr_server_packet_send(cliprdr, s);
336static UINT cliprdr_server_format_data_request(CliprdrServerContext* context,
339 WINPR_ASSERT(context);
340 WINPR_ASSERT(formatDataRequest);
343 if (formatDataRequest->common.msgType != CB_FORMAT_DATA_REQUEST)
344 WLog_Print(cliprdr->log, WLOG_WARN,
"called with invalid type %08" PRIx32,
345 formatDataRequest->common.msgType);
348 wStream* s = cliprdr_packet_new(CB_FORMAT_DATA_REQUEST, 0, 4);
352 WLog_Print(cliprdr->log, WLOG_ERROR,
"cliprdr_packet_new failed!");
353 return ERROR_INTERNAL_ERROR;
356 if ((formatDataRequest->common.msgFlags & CB_RESPONSE_FAIL) == 0)
357 Stream_Write_UINT32(s,
358 formatDataRequest->requestedFormatId);
359 WLog_Print(cliprdr->log, WLOG_DEBUG,
"ClientFormatDataRequest");
360 return cliprdr_server_packet_send(cliprdr, s);
369cliprdr_server_format_data_response(CliprdrServerContext* context,
372 WINPR_ASSERT(context);
373 WINPR_ASSERT(formatDataResponse);
377 if (formatDataResponse->common.msgType != CB_FORMAT_DATA_RESPONSE)
378 WLog_Print(cliprdr->log, WLOG_WARN,
"called with invalid type %08" PRIx32,
379 formatDataResponse->common.msgType);
381 wStream* s = cliprdr_packet_new(CB_FORMAT_DATA_RESPONSE, formatDataResponse->common.msgFlags,
382 formatDataResponse->common.dataLen);
386 WLog_Print(cliprdr->log, WLOG_ERROR,
"cliprdr_packet_new failed!");
387 return ERROR_INTERNAL_ERROR;
390 if ((formatDataResponse->common.msgFlags & CB_RESPONSE_FAIL) == 0)
391 Stream_Write(s, formatDataResponse->requestedFormatData,
392 formatDataResponse->common.dataLen);
393 WLog_Print(cliprdr->log, WLOG_DEBUG,
"ServerFormatDataResponse");
394 return cliprdr_server_packet_send(cliprdr, s);
403cliprdr_server_file_contents_request(CliprdrServerContext* context,
406 WINPR_ASSERT(context);
407 WINPR_ASSERT(fileContentsRequest);
411 if (fileContentsRequest->common.msgType != CB_FILECONTENTS_REQUEST)
412 WLog_Print(cliprdr->log, WLOG_WARN,
"called with invalid type %08" PRIx32,
413 fileContentsRequest->common.msgType);
415 wStream* s = cliprdr_packet_file_contents_request_new(fileContentsRequest);
418 WLog_Print(cliprdr->log, WLOG_ERROR,
"cliprdr_packet_file_contents_request_new failed!");
419 return ERROR_INTERNAL_ERROR;
422 WLog_Print(cliprdr->log, WLOG_DEBUG,
"ServerFileContentsRequest: streamId: 0x%08" PRIX32
"",
423 fileContentsRequest->streamId);
424 return cliprdr_server_packet_send(cliprdr, s);
433cliprdr_server_file_contents_response(CliprdrServerContext* context,
436 WINPR_ASSERT(context);
437 WINPR_ASSERT(fileContentsResponse);
440 WINPR_ASSERT(cliprdr);
442 if (fileContentsResponse->common.msgType != CB_FILECONTENTS_RESPONSE)
443 WLog_Print(cliprdr->log, WLOG_WARN,
"called with invalid type %08" PRIx32,
444 fileContentsResponse->common.msgType);
446 wStream* s = cliprdr_packet_file_contents_response_new(fileContentsResponse);
449 WLog_Print(cliprdr->log, WLOG_ERROR,
"cliprdr_packet_file_contents_response_new failed!");
450 return ERROR_INTERNAL_ERROR;
453 WLog_Print(cliprdr->log, WLOG_DEBUG,
"ServerFileContentsResponse: streamId: 0x%08" PRIX32
"",
454 fileContentsResponse->streamId);
455 return cliprdr_server_packet_send(cliprdr, s);
463static UINT cliprdr_server_receive_general_capability(CliprdrServerContext* context,
wStream* s,
466 WINPR_ASSERT(context);
467 WINPR_ASSERT(cap_set);
470 WINPR_ASSERT(cliprdr);
474 WLog_Print(cliprdr->log, WLOG_ERROR,
475 "invalid capabilitySetLength %" PRIu16
" != %" PRIuz
476 " (capabilitySetType=CB_CAPSTYPE_GENERAL)",
478 return ERROR_INVALID_DATA;
481 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
482 return ERROR_INVALID_DATA;
484 Stream_Read_UINT32(s, cap_set->version);
485 Stream_Read_UINT32(s, cap_set->generalFlags);
487 if (context->useLongFormatNames)
488 context->useLongFormatNames = (cap_set->generalFlags & CB_USE_LONG_FORMAT_NAMES) != 0;
490 if (context->streamFileClipEnabled)
491 context->streamFileClipEnabled = (cap_set->generalFlags & CB_STREAM_FILECLIP_ENABLED) != 0;
493 if (context->fileClipNoFilePaths)
494 context->fileClipNoFilePaths = (cap_set->generalFlags & CB_FILECLIP_NO_FILE_PATHS) != 0;
496 if (context->canLockClipData)
497 context->canLockClipData = (cap_set->generalFlags & CB_CAN_LOCK_CLIPDATA) != 0;
499 if (context->hasHugeFileSupport)
500 context->hasHugeFileSupport = (cap_set->generalFlags & CB_HUGE_FILE_SUPPORT_ENABLED) != 0;
502 return CHANNEL_RC_OK;
506static BOOL cliprdr_capabilities_contain_capset(wLog* log,
const CLIPRDR_CAPABILITIES* capabilities,
507 size_t capabilitiesSize,
510 WINPR_ASSERT(capabilities);
512 const BYTE* offset = (
const BYTE*)capabilities->capabilitySets;
513 size_t byteOffset = 0;
514 for (UINT32 x = 0; x < capabilities->cCapabilitiesSets; x++)
518 byteOffset += cur->capabilitySetLength;
519 if (byteOffset > capabilitiesSize)
522 offset += cur->capabilitySetLength;
524 if (cur->capabilitySetType == capset->capabilitySetType)
526 WLog_Print(log, WLOG_ERROR,
"duplicate CLIPRDR_CAPABILITY_SET %" PRIu16,
527 cur->capabilitySetType);
539static UINT cliprdr_server_receive_capabilities(CliprdrServerContext* context,
wStream* s,
542 UINT error = ERROR_INVALID_DATA;
543 size_t cap_sets_size = 0;
546 WINPR_ASSERT(context);
547 WINPR_UNUSED(header);
550 WINPR_ASSERT(cliprdr);
552 WLog_Print(cliprdr->log, WLOG_DEBUG,
"CliprdrClientCapabilities");
553 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
554 return ERROR_INVALID_DATA;
556 const UINT16 cCapabilitiesSets = Stream_Get_UINT16(s);
557 Stream_Seek_UINT16(s);
559 for (
size_t index = 0; index < cCapabilitiesSets; index++)
561 const size_t cap_set_offset = cap_sets_size;
564 const UINT16 capabilitySetType = Stream_Get_UINT16(s);
565 const UINT16 capabilitySetLength = Stream_Get_UINT16(s);
569 WLog_Print(cliprdr->log, WLOG_ERROR,
570 "invalid capabilitySetLength %" PRIu16
" < %" PRIuz
571 " (capabilitySetType=%" PRIu16
")",
576 if (!Stream_CheckAndLogRequiredLength(
580 cap_sets_size += capabilitySetLength;
585 WLog_Print(cliprdr->log, WLOG_ERROR,
"capabilities.capabilitySets realloc failed!");
586 free(capabilities.capabilitySets);
587 return CHANNEL_RC_NO_MEMORY;
590 capabilities.capabilitySets = tmp;
595 capSet->capabilitySetType = capabilitySetType;
596 capSet->capabilitySetLength = capabilitySetLength;
598 if (cliprdr_capabilities_contain_capset(cliprdr->log, &capabilities, cap_sets_size, capSet))
601 capabilities.cCapabilitiesSets++;
603 switch (capSet->capabilitySetType)
605 case CB_CAPSTYPE_GENERAL:
606 error = cliprdr_server_receive_general_capability(
611 cliprdr->log, WLOG_ERROR,
612 "cliprdr_server_receive_general_capability failed with error %" PRIu32
"",
619 WLog_Print(cliprdr->log, WLOG_ERROR,
"unknown cliprdr capability set: %" PRIu16
"",
620 capSet->capabilitySetType);
625 error = CHANNEL_RC_OK;
626 IFCALLRET(context->ClientCapabilities, error, context, &capabilities);
628 free(capabilities.capabilitySets);
637static UINT cliprdr_server_receive_temporary_directory(CliprdrServerContext* context,
wStream* s,
640 UINT error = CHANNEL_RC_OK;
642 WINPR_ASSERT(context);
643 WINPR_UNUSED(header);
646 WINPR_ASSERT(cliprdr);
648 if (!Stream_CheckAndLogRequiredLength(TAG, s,
649 ARRAYSIZE(cliprdr->temporaryDirectory) *
sizeof(WCHAR)))
650 return CHANNEL_RC_NO_MEMORY;
652 const WCHAR* wszTempDir = Stream_ConstPointer(s);
654 if (wszTempDir[ARRAYSIZE(cliprdr->temporaryDirectory) - 1] != 0)
656 WLog_Print(cliprdr->log, WLOG_ERROR,
"wszTempDir[259] was not 0");
657 return ERROR_INVALID_DATA;
660 if (ConvertWCharNToUtf8(wszTempDir, ARRAYSIZE(cliprdr->temporaryDirectory),
661 cliprdr->temporaryDirectory,
662 ARRAYSIZE(cliprdr->temporaryDirectory)) < 0)
664 WLog_Print(cliprdr->log, WLOG_ERROR,
"failed to convert temporary directory name");
665 return ERROR_INVALID_DATA;
668 size_t length = strnlen(cliprdr->temporaryDirectory, ARRAYSIZE(cliprdr->temporaryDirectory));
670 if (length >= ARRAYSIZE(cliprdr->temporaryDirectory))
671 length = ARRAYSIZE(cliprdr->temporaryDirectory) - 1;
674 CopyMemory(tempDirectory.szTempDir, cliprdr->temporaryDirectory, length);
675 tempDirectory.szTempDir[length] =
'\0';
676 WLog_Print(cliprdr->log, WLOG_DEBUG,
"CliprdrTemporaryDirectory: %s",
677 cliprdr->temporaryDirectory);
678 IFCALLRET(context->TempDirectory, error, context, &tempDirectory);
681 WLog_Print(cliprdr->log, WLOG_ERROR,
"TempDirectory failed with error %" PRIu32
"!", error);
691static UINT cliprdr_server_receive_format_list(CliprdrServerContext* context,
wStream* s,
694 UINT error = CHANNEL_RC_OK;
696 WINPR_ASSERT(context);
697 WINPR_ASSERT(header);
700 .msgFlags = header->msgFlags,
701 .dataLen = header->dataLen },
703 .formats =
nullptr };
704 wLog* log = WLog_Get(TAG);
705 if ((error = cliprdr_read_format_list(log, s, &formatList, context->useLongFormatNames)))
708 WLog_Print(log, WLOG_DEBUG,
"ClientFormatList: numFormats: %" PRIu32
"", formatList.numFormats);
709 IFCALLRET(context->ClientFormatList, error, context, &formatList);
712 WLog_Print(log, WLOG_ERROR,
"ClientFormatList failed with error %" PRIu32
"!", error);
715 cliprdr_free_format_list(&formatList);
724static UINT cliprdr_server_receive_format_list_response(CliprdrServerContext* context,
wStream* s,
727 UINT error = CHANNEL_RC_OK;
729 WINPR_ASSERT(context);
730 WINPR_ASSERT(header);
733 WINPR_ASSERT(cliprdr);
736 WLog_Print(cliprdr->log, WLOG_DEBUG,
"CliprdrClientFormatListResponse");
739 CB_FORMAT_LIST_RESPONSE,
740 .msgFlags = header->msgFlags,
741 .dataLen = header->dataLen } };
742 IFCALLRET(context->ClientFormatListResponse, error, context, &formatListResponse);
745 WLog_Print(cliprdr->log, WLOG_ERROR,
746 "ClientFormatListResponse failed with error %" PRIu32
"!", error);
756static UINT cliprdr_server_receive_lock_clipdata(CliprdrServerContext* context,
wStream* s,
759 UINT error = CHANNEL_RC_OK;
761 WINPR_ASSERT(context);
762 WINPR_ASSERT(header);
765 WINPR_ASSERT(cliprdr);
767 WLog_Print(cliprdr->log, WLOG_DEBUG,
"CliprdrClientLockClipData");
769 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
770 return ERROR_INVALID_DATA;
773 .msgFlags = header->msgFlags,
774 .dataLen = header->dataLen },
777 Stream_Read_UINT32(s, lockClipboardData.clipDataId);
778 IFCALLRET(context->ClientLockClipboardData, error, context, &lockClipboardData);
781 WLog_Print(cliprdr->log, WLOG_ERROR,
782 "ClientLockClipboardData failed with error %" PRIu32
"!", error);
792static UINT cliprdr_server_receive_unlock_clipdata(CliprdrServerContext* context,
wStream* s,
795 WINPR_ASSERT(context);
796 WINPR_ASSERT(header);
799 WINPR_ASSERT(cliprdr);
801 WLog_Print(cliprdr->log, WLOG_DEBUG,
"CliprdrClientUnlockClipData");
804 .msgFlags = header->msgFlags,
805 .dataLen = header->dataLen },
808 UINT error = cliprdr_read_unlock_clipdata(s, &unlockClipboardData);
812 IFCALLRET(context->ClientUnlockClipboardData, error, context, &unlockClipboardData);
815 WLog_Print(cliprdr->log, WLOG_ERROR,
816 "ClientUnlockClipboardData failed with error %" PRIu32
"!", error);
826static UINT cliprdr_server_receive_format_data_request(CliprdrServerContext* context,
wStream* s,
829 WINPR_ASSERT(context);
830 WINPR_ASSERT(header);
833 WINPR_ASSERT(cliprdr);
835 WLog_Print(cliprdr->log, WLOG_DEBUG,
"CliprdrClientFormatDataRequest");
838 .msgFlags = header->msgFlags,
839 .dataLen = header->dataLen },
840 .requestedFormatId = 0 };
842 UINT error = cliprdr_read_format_data_request(s, &formatDataRequest);
846 context->lastRequestedFormatId = formatDataRequest.requestedFormatId;
847 IFCALLRET(context->ClientFormatDataRequest, error, context, &formatDataRequest);
850 WLog_Print(cliprdr->log, WLOG_ERROR,
851 "ClientFormatDataRequest failed with error %" PRIu32
"!", error);
861static UINT cliprdr_server_receive_format_data_response(CliprdrServerContext* context,
wStream* s,
864 WINPR_ASSERT(context);
865 WINPR_ASSERT(header);
868 WINPR_ASSERT(cliprdr);
870 WLog_Print(cliprdr->log, WLOG_DEBUG,
"CliprdrClientFormatDataResponse");
873 CB_FORMAT_DATA_RESPONSE,
874 .msgFlags = header->msgFlags,
875 .dataLen = header->dataLen },
876 .requestedFormatData =
nullptr };
878 UINT error = cliprdr_read_format_data_response(s, &formatDataResponse);
882 IFCALLRET(context->ClientFormatDataResponse, error, context, &formatDataResponse);
885 WLog_Print(cliprdr->log, WLOG_ERROR,
886 "ClientFormatDataResponse failed with error %" PRIu32
"!", error);
896static UINT cliprdr_server_receive_filecontents_request(CliprdrServerContext* context,
wStream* s,
899 WINPR_ASSERT(context);
900 WINPR_ASSERT(header);
903 WINPR_ASSERT(cliprdr);
905 WLog_Print(cliprdr->log, WLOG_DEBUG,
"CliprdrClientFileContentsRequest");
908 .msgFlags = header->msgFlags,
909 .dataLen = header->dataLen },
916 .haveClipDataId = FALSE,
919 UINT error = cliprdr_read_file_contents_request(s, &request);
923 if (!context->hasHugeFileSupport)
925 if (request.nPositionHigh > 0)
926 return ERROR_INVALID_DATA;
927 if ((UINT64)request.nPositionLow + request.cbRequested > UINT32_MAX)
928 return ERROR_INVALID_DATA;
930 IFCALLRET(context->ClientFileContentsRequest, error, context, &request);
933 WLog_Print(cliprdr->log, WLOG_ERROR,
934 "ClientFileContentsRequest failed with error %" PRIu32
"!", error);
944static UINT cliprdr_server_receive_filecontents_response(CliprdrServerContext* context,
wStream* s,
947 UINT error = CHANNEL_RC_OK;
949 WINPR_ASSERT(context);
950 WINPR_ASSERT(header);
953 WINPR_ASSERT(cliprdr);
955 WLog_Print(cliprdr->log, WLOG_DEBUG,
"CliprdrClientFileContentsResponse");
958 .msgFlags = header->msgFlags,
959 .dataLen = header->dataLen },
962 .requestedData =
nullptr };
964 if ((error = cliprdr_read_file_contents_response(s, &response)))
967 IFCALLRET(context->ClientFileContentsResponse, error, context, &response);
970 WLog_Print(cliprdr->log, WLOG_ERROR,
971 "ClientFileContentsResponse failed with error %" PRIu32
"!", error);
981static UINT cliprdr_server_receive_pdu(CliprdrServerContext* context,
wStream* s,
986 WINPR_ASSERT(context);
987 WINPR_ASSERT(header);
990 WINPR_ASSERT(cliprdr);
993 char buffer1[64] = WINPR_C_ARRAY_INIT;
994 char buffer2[64] = WINPR_C_ARRAY_INIT;
995 WLog_Print(cliprdr->log, WLOG_DEBUG,
996 "CliprdrServerReceivePdu: msgType: %s, msgFlags: %s dataLen: %" PRIu32
"",
997 CB_MSG_TYPE_STRING(header->msgType, buffer1,
sizeof(buffer1)),
998 CB_MSG_FLAGS_STRING(header->msgFlags, buffer2,
sizeof(buffer2)),
1002 const size_t start = Stream_GetPosition(s);
1003 switch (header->msgType)
1006 error = cliprdr_server_receive_capabilities(context, s, header);
1009 case CB_TEMP_DIRECTORY:
1010 error = cliprdr_server_receive_temporary_directory(context, s, header);
1013 case CB_FORMAT_LIST:
1014 error = cliprdr_server_receive_format_list(context, s, header);
1017 case CB_FORMAT_LIST_RESPONSE:
1018 error = cliprdr_server_receive_format_list_response(context, s, header);
1021 case CB_LOCK_CLIPDATA:
1022 error = cliprdr_server_receive_lock_clipdata(context, s, header);
1025 case CB_UNLOCK_CLIPDATA:
1026 error = cliprdr_server_receive_unlock_clipdata(context, s, header);
1029 case CB_FORMAT_DATA_REQUEST:
1030 error = cliprdr_server_receive_format_data_request(context, s, header);
1033 case CB_FORMAT_DATA_RESPONSE:
1034 error = cliprdr_server_receive_format_data_response(context, s, header);
1037 case CB_FILECONTENTS_REQUEST:
1038 error = cliprdr_server_receive_filecontents_request(context, s, header);
1041 case CB_FILECONTENTS_RESPONSE:
1042 error = cliprdr_server_receive_filecontents_response(context, s, header);
1046 error = ERROR_INVALID_DATA;
1047 WLog_Print(cliprdr->log, WLOG_ERROR,
"Unexpected clipboard PDU type: %" PRIu16
"",
1052 const size_t end = Stream_GetPosition(s);
1053 const size_t diff = end - start;
1054 if (diff != header->dataLen)
1056 char buffer1[64] = WINPR_C_ARRAY_INIT;
1058 TAG,
"[%s] Mismatch: header told length is %" PRIu32
", but actually read %" PRIuz,
1059 CB_MSG_TYPE_STRING(header->msgType, buffer1,
sizeof(buffer1)), header->dataLen, diff);
1062 const size_t rem = Stream_GetRemainingLength(s);
1065 char buffer1[64] = WINPR_C_ARRAY_INIT;
1066 char buffer2[64] = WINPR_C_ARRAY_INIT;
1068 TAG,
"msgType: %s, msgFlags: %s dataLen: %" PRIu32
": %" PRIuz
" bytes not parsed",
1069 CB_MSG_TYPE_STRING(header->msgType, buffer1,
sizeof(buffer1)),
1070 CB_MSG_FLAGS_STRING(header->msgFlags, buffer2,
sizeof(buffer2)), header->dataLen, rem);
1080static UINT cliprdr_server_init(CliprdrServerContext* context)
1082 UINT32 generalFlags = 0;
1086 WINPR_ASSERT(context);
1089 WINPR_ASSERT(cliprdr);
1091 monitorReady.common.msgType = CB_MONITOR_READY;
1093 if (context->useLongFormatNames)
1094 generalFlags |= CB_USE_LONG_FORMAT_NAMES;
1096 if (context->streamFileClipEnabled)
1097 generalFlags |= CB_STREAM_FILECLIP_ENABLED;
1099 if (context->fileClipNoFilePaths)
1100 generalFlags |= CB_FILECLIP_NO_FILE_PATHS;
1102 if (context->canLockClipData)
1103 generalFlags |= CB_CAN_LOCK_CLIPDATA;
1105 if (context->hasHugeFileSupport)
1106 generalFlags |= CB_HUGE_FILE_SUPPORT_ENABLED;
1109 CB_CAPSTYPE_GENERAL,
1110 .capabilitySetLength =
1111 CB_CAPSTYPE_GENERAL_LEN,
1112 .version = CB_CAPS_VERSION_2,
1113 .generalFlags = generalFlags };
1117 .dataLen = 4 + CB_CAPSTYPE_GENERAL_LEN },
1118 .cCapabilitiesSets = 1,
1122 if ((error = context->ServerCapabilities(context, &capabilities)))
1124 WLog_Print(cliprdr->log, WLOG_ERROR,
"ServerCapabilities failed with error %" PRIu32
"!",
1129 if ((error = context->MonitorReady(context, &monitorReady)))
1131 WLog_Print(cliprdr->log, WLOG_ERROR,
"MonitorReady failed with error %" PRIu32
"!", error);
1143static UINT cliprdr_server_read(CliprdrServerContext* context)
1145 WINPR_ASSERT(context);
1148 WINPR_ASSERT(cliprdr);
1151 wStream* s = ChannelPduTracker_poll(cliprdr->channelPduTracker, &ok);
1154 return ERROR_INVALID_DATA;
1157 return CHANNEL_RC_OK;
1159 UINT ret = ERROR_INVALID_DATA;
1160 if (!Stream_CheckAndLogRequiredLengthWLog(cliprdr->log, s, 8))
1164 .msgType = Stream_Get_UINT16(s),
1165 .msgFlags = Stream_Get_UINT16(s),
1166 .dataLen = Stream_Get_UINT32(s)
1169 if (!Stream_CheckAndLogRequiredLengthWLog(cliprdr->log, s, header.dataLen))
1172 ret = cliprdr_server_receive_pdu(context, s, &header);
1173 if (ret != CHANNEL_RC_OK)
1175 char buffer1[128] = WINPR_C_ARRAY_INIT;
1176 char buffer2[128] = WINPR_C_ARRAY_INIT;
1177 WLog_Print(cliprdr->log, WLOG_ERROR,
1178 "cliprdr_server_receive_pdu [%s|%s] failed with error code %" PRIu32
"!",
1179 CB_MSG_TYPE_STRING(header.msgType, buffer1,
sizeof(buffer1)),
1180 CB_MSG_FLAGS_STRING(header.msgFlags, buffer2,
sizeof(buffer2)), ret);
1188static DWORD WINAPI cliprdr_server_thread(LPVOID arg)
1192 HANDLE events[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
1193 CliprdrServerContext* context = (CliprdrServerContext*)arg;
1194 UINT error = CHANNEL_RC_OK;
1196 WINPR_ASSERT(context);
1199 WINPR_ASSERT(cliprdr);
1201 HANDLE ChannelEvent = context->GetEventHandle(context);
1203 events[nCount++] = cliprdr->StopEvent;
1204 events[nCount++] = ChannelEvent;
1206 if (context->autoInitializationSequence)
1208 if ((error = cliprdr_server_init(context)))
1210 WLog_Print(cliprdr->log, WLOG_ERROR,
1211 "cliprdr_server_init failed with error %" PRIu32
"!", error);
1218 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
1220 if (status == WAIT_FAILED)
1222 error = GetLastError();
1223 WLog_Print(cliprdr->log, WLOG_ERROR,
1224 "WaitForMultipleObjects failed with error %" PRIu32
"", error);
1228 status = WaitForSingleObject(cliprdr->StopEvent, 0);
1230 if (status == WAIT_FAILED)
1232 error = GetLastError();
1233 WLog_Print(cliprdr->log, WLOG_ERROR,
1234 "WaitForSingleObject failed with error %" PRIu32
"", error);
1238 if (status == WAIT_OBJECT_0)
1241 status = WaitForSingleObject(ChannelEvent, 0);
1243 if (status == WAIT_FAILED)
1245 error = GetLastError();
1246 WLog_Print(cliprdr->log, WLOG_ERROR,
1247 "WaitForSingleObject failed with error %" PRIu32
"", error);
1251 if (status == WAIT_OBJECT_0)
1253 if ((error = context->CheckEventHandle(context)))
1255 WLog_Print(cliprdr->log, WLOG_ERROR,
1256 "CheckEventHandle failed with error %" PRIu32
"!", error);
1264 if (error && context->rdpcontext)
1265 setChannelError(context->rdpcontext, error,
"cliprdr_server_thread reported an error");
1276static UINT cliprdr_server_open(CliprdrServerContext* context)
1278 WINPR_ASSERT(context);
1281 WINPR_ASSERT(cliprdr);
1283 PULONG pSessionId =
nullptr;
1284 DWORD BytesReturned = 0;
1286 if (!WTSQuerySessionInformationA(cliprdr->vcm, WTS_CURRENT_SESSION, WTSSessionId,
1287 (LPSTR*)&pSessionId, &BytesReturned))
1288 return CHANNEL_RC_BAD_CHANNEL;
1290 DWORD SessionId = (DWORD)*pSessionId;
1291 WTSFreeMemory(pSessionId);
1293 cliprdr->ChannelHandle =
1294 WTSVirtualChannelOpenEx(SessionId, CLIPRDR_SVC_CHANNEL_NAME, CHANNEL_OPTION_SHOW_PROTOCOL);
1295 if (!cliprdr->ChannelHandle)
1297 WLog_Print(cliprdr->log, WLOG_ERROR,
"WTSVirtualChannelOpen for cliprdr failed!");
1298 return ERROR_INTERNAL_ERROR;
1301 WINPR_ASSERT(cliprdr->channelPduTracker ==
nullptr);
1302 cliprdr->channelPduTracker = ChannelPduTracker_new(cliprdr->ChannelHandle);
1303 if (!cliprdr->channelPduTracker)
1305 WLog_Print(cliprdr->log, WLOG_ERROR,
"ChannelPduTracker_new for cliprdr failed!");
1306 return ERROR_INTERNAL_ERROR;
1309 cliprdr->ChannelEvent =
nullptr;
1311 void* buffer =
nullptr;
1313 if (WTSVirtualChannelQuery(cliprdr->ChannelHandle, WTSVirtualEventHandle, &buffer,
1316 if (BytesReturned !=
sizeof(HANDLE))
1318 WLog_Print(cliprdr->log, WLOG_ERROR,
"BytesReturned has not size of HANDLE!");
1319 return ERROR_INTERNAL_ERROR;
1322 cliprdr->ChannelEvent = *(HANDLE*)buffer;
1323 WTSFreeMemory(buffer);
1326 if (!cliprdr->ChannelEvent)
1328 WLog_Print(cliprdr->log, WLOG_ERROR,
"WTSVirtualChannelQuery for cliprdr failed!");
1329 return ERROR_INTERNAL_ERROR;
1332 return CHANNEL_RC_OK;
1340static UINT cliprdr_server_close(CliprdrServerContext* context)
1342 WINPR_ASSERT(context);
1345 WINPR_ASSERT(cliprdr);
1347 ChannelPduTracker_free(cliprdr->channelPduTracker);
1348 cliprdr->channelPduTracker =
nullptr;
1350 if (cliprdr->ChannelHandle)
1352 (void)WTSVirtualChannelClose(cliprdr->ChannelHandle);
1353 cliprdr->ChannelHandle =
nullptr;
1356 return CHANNEL_RC_OK;
1364static UINT cliprdr_server_start(CliprdrServerContext* context)
1368 WINPR_ASSERT(context);
1371 WINPR_ASSERT(cliprdr);
1373 if (!cliprdr->ChannelHandle)
1375 error = context->Open(context);
1378 WLog_Print(cliprdr->log, WLOG_ERROR,
"Open failed!");
1383 cliprdr->StopEvent = CreateEvent(
nullptr, TRUE, FALSE,
nullptr);
1384 if (!cliprdr->StopEvent)
1386 WLog_Print(cliprdr->log, WLOG_ERROR,
"CreateEvent failed!");
1387 return ERROR_INTERNAL_ERROR;
1390 cliprdr->Thread = CreateThread(
nullptr, 0, cliprdr_server_thread, (
void*)context, 0,
nullptr);
1391 if (!cliprdr->Thread)
1393 WLog_Print(cliprdr->log, WLOG_ERROR,
"CreateThread failed!");
1394 (void)CloseHandle(cliprdr->StopEvent);
1395 cliprdr->StopEvent =
nullptr;
1396 return ERROR_INTERNAL_ERROR;
1399 return CHANNEL_RC_OK;
1407static UINT cliprdr_server_stop(CliprdrServerContext* context)
1409 UINT error = CHANNEL_RC_OK;
1411 WINPR_ASSERT(context);
1414 WINPR_ASSERT(cliprdr);
1416 if (cliprdr->StopEvent)
1418 (void)SetEvent(cliprdr->StopEvent);
1420 if (WaitForSingleObject(cliprdr->Thread, INFINITE) == WAIT_FAILED)
1422 error = GetLastError();
1423 WLog_Print(cliprdr->log, WLOG_ERROR,
1424 "WaitForSingleObject failed with error %" PRIu32
"", error);
1428 (void)CloseHandle(cliprdr->Thread);
1429 (void)CloseHandle(cliprdr->StopEvent);
1432 if (cliprdr->ChannelHandle)
1433 return context->Close(context);
1438static HANDLE cliprdr_server_get_event_handle(CliprdrServerContext* context)
1440 WINPR_ASSERT(context);
1443 WINPR_ASSERT(cliprdr);
1444 return cliprdr->ChannelEvent;
1452static UINT cliprdr_server_check_event_handle(CliprdrServerContext* context)
1454 return cliprdr_server_read(context);
1457CliprdrServerContext* cliprdr_server_context_new(HANDLE vcm)
1459 CliprdrServerContext* context = (CliprdrServerContext*)calloc(1,
sizeof(CliprdrServerContext));
1464 context->autoInitializationSequence = TRUE;
1465 context->Open = cliprdr_server_open;
1466 context->Close = cliprdr_server_close;
1467 context->Start = cliprdr_server_start;
1468 context->Stop = cliprdr_server_stop;
1469 context->GetEventHandle = cliprdr_server_get_event_handle;
1470 context->CheckEventHandle = cliprdr_server_check_event_handle;
1471 context->ServerCapabilities = cliprdr_server_capabilities;
1472 context->MonitorReady = cliprdr_server_monitor_ready;
1473 context->ServerFormatList = cliprdr_server_format_list;
1474 context->ServerFormatListResponse = cliprdr_server_format_list_response;
1475 context->ServerLockClipboardData = cliprdr_server_lock_clipboard_data;
1476 context->ServerUnlockClipboardData = cliprdr_server_unlock_clipboard_data;
1477 context->ServerFormatDataRequest = cliprdr_server_format_data_request;
1478 context->ServerFormatDataResponse = cliprdr_server_format_data_response;
1479 context->ServerFileContentsRequest = cliprdr_server_file_contents_request;
1480 context->ServerFileContentsResponse = cliprdr_server_file_contents_response;
1487 cliprdr->log = WLog_Get(TAG);
1493 cliprdr_server_context_free(context);
1497void cliprdr_server_context_free(CliprdrServerContext* context)
1502 free(context->handle);