FreeRDP
Loading...
Searching...
No Matches
server/cliprdr_main.c
1
22#include <freerdp/config.h>
23
24#include <winpr/crt.h>
25#include <winpr/assert.h>
26#include <winpr/print.h>
27#include <winpr/stream.h>
28
29#include <freerdp/freerdp.h>
30#include <freerdp/channels/log.h>
31#include "cliprdr_main.h"
32#include "../cliprdr_common.h"
33
79static UINT cliprdr_server_packet_send(CliprdrServerPrivate* cliprdr, wStream* s)
80{
81 UINT rc = 0;
82 BOOL status = 0;
83 ULONG written = 0;
84
85 WINPR_ASSERT(cliprdr);
86
87 const size_t pos = Stream_GetPosition(s);
88 if ((pos < 8) || (pos > UINT32_MAX))
89 {
90 rc = ERROR_NO_DATA;
91 goto fail;
92 }
93
94 const UINT32 dataLen = (UINT32)(pos - 8);
95 if (!Stream_SetPosition(s, 4))
96 goto fail;
97 Stream_Write_UINT32(s, dataLen);
98
99 WINPR_ASSERT(pos <= UINT32_MAX);
100 status = WTSVirtualChannelWrite(cliprdr->ChannelHandle, Stream_BufferAs(s, char), (UINT32)pos,
101 &written);
102 rc = status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
103fail:
104 Stream_Free(s, TRUE);
105 return rc;
106}
107
113static UINT cliprdr_server_capabilities(CliprdrServerContext* context,
114 const CLIPRDR_CAPABILITIES* capabilities)
115{
116 size_t offset = 0;
117
118 WINPR_ASSERT(context);
119 WINPR_ASSERT(capabilities);
120
121 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
122
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);
126
127 if (capabilities->cCapabilitiesSets > UINT16_MAX)
128 {
129 WLog_Print(cliprdr->log, WLOG_ERROR, "Invalid number of capability sets in clipboard caps");
130 return ERROR_INVALID_PARAMETER;
131 }
132
133 wStream* s = cliprdr_packet_new(CB_CLIP_CAPS, 0, 4 + CB_CAPSTYPE_GENERAL_LEN);
134
135 if (!s)
136 {
137 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_new failed!");
138 return ERROR_INTERNAL_ERROR;
139 }
140
141 if ((capabilities->common.msgFlags & CB_RESPONSE_FAIL) == 0)
142 {
143 Stream_Write_UINT16(
144 s, (UINT16)capabilities->cCapabilitiesSets); /* cCapabilitiesSets (2 bytes) */
145 Stream_Write_UINT16(s, 0); /* pad1 (2 bytes) */
146 for (UINT32 x = 0; x < capabilities->cCapabilitiesSets; x++)
147 {
148 const CLIPRDR_CAPABILITY_SET* cap =
149 WINPR_PACKED_ALIGN_CAST(const CLIPRDR_CAPABILITY_SET*,
150 (((const BYTE*)capabilities->capabilitySets) + offset));
151 offset += cap->capabilitySetLength;
152
153 switch (cap->capabilitySetType)
154 {
155 case CB_CAPSTYPE_GENERAL:
156 {
157 const CLIPRDR_GENERAL_CAPABILITY_SET* generalCapabilitySet =
158 WINPR_PACKED_ALIGN_CAST(const CLIPRDR_GENERAL_CAPABILITY_SET*, cap);
159 Stream_Write_UINT16(
160 s,
161 generalCapabilitySet->capabilitySetType); /* capabilitySetType (2 bytes) */
162 Stream_Write_UINT16(
163 s,
164 generalCapabilitySet->capabilitySetLength); /* lengthCapability (2 bytes) */
165 Stream_Write_UINT32(s, generalCapabilitySet->version); /* version (4 bytes) */
166 Stream_Write_UINT32(
167 s, generalCapabilitySet->generalFlags); /* generalFlags (4 bytes) */
168 }
169 break;
170
171 default:
172 WLog_Print(cliprdr->log, WLOG_WARN, "Unknown capability set type %08" PRIx16,
173 cap->capabilitySetType);
174 if (!Stream_SafeZero(s, cap->capabilitySetLength))
175 {
176 WLog_Print(cliprdr->log, WLOG_ERROR, "short stream");
177 Stream_Free(s, TRUE);
178 return ERROR_NO_DATA;
179 }
180 break;
181 }
182 }
183 }
184 WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerCapabilities");
185 return cliprdr_server_packet_send(cliprdr, s);
186}
187
193static UINT cliprdr_server_monitor_ready(CliprdrServerContext* context,
194 const CLIPRDR_MONITOR_READY* monitorReady)
195{
196 WINPR_ASSERT(context);
197 WINPR_ASSERT(monitorReady);
198
199 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
200
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);
204
205 wStream* s = cliprdr_packet_new(CB_MONITOR_READY, monitorReady->common.msgFlags,
206 monitorReady->common.dataLen);
207
208 if (!s)
209 {
210 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_new failed!");
211 return ERROR_INTERNAL_ERROR;
212 }
213
214 WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerMonitorReady");
215 return cliprdr_server_packet_send(cliprdr, s);
216}
217
223static UINT cliprdr_server_format_list(CliprdrServerContext* context,
224 const CLIPRDR_FORMAT_LIST* formatList)
225{
226 WINPR_ASSERT(context);
227 WINPR_ASSERT(formatList);
228
229 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
230
231 wStream* s = cliprdr_packet_format_list_new(formatList, context->useLongFormatNames, FALSE);
232 if (!s)
233 {
234 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_format_list_new failed!");
235 return ERROR_INTERNAL_ERROR;
236 }
237
238 WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatList: numFormats: %" PRIu32 "",
239 formatList->numFormats);
240 return cliprdr_server_packet_send(cliprdr, s);
241}
242
248static UINT
249cliprdr_server_format_list_response(CliprdrServerContext* context,
250 const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
251{
252 WINPR_ASSERT(context);
253 WINPR_ASSERT(formatListResponse);
254
255 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
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);
259
260 wStream* s = cliprdr_packet_new(CB_FORMAT_LIST_RESPONSE, formatListResponse->common.msgFlags,
261 formatListResponse->common.dataLen);
262
263 if (!s)
264 {
265 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_new failed!");
266 return ERROR_INTERNAL_ERROR;
267 }
268
269 WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatListResponse");
270 return cliprdr_server_packet_send(cliprdr, s);
271}
272
278static UINT cliprdr_server_lock_clipboard_data(CliprdrServerContext* context,
279 const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
280{
281 WINPR_ASSERT(context);
282 WINPR_ASSERT(lockClipboardData);
283
284 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
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);
288
289 wStream* s = cliprdr_packet_lock_clipdata_new(lockClipboardData);
290 if (!s)
291 {
292 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_lock_clipdata_new failed!");
293 return ERROR_INTERNAL_ERROR;
294 }
295
296 WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerLockClipboardData: clipDataId: 0x%08" PRIX32 "",
297 lockClipboardData->clipDataId);
298 return cliprdr_server_packet_send(cliprdr, s);
299}
300
306static UINT
307cliprdr_server_unlock_clipboard_data(CliprdrServerContext* context,
308 const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
309{
310 WINPR_ASSERT(context);
311 WINPR_ASSERT(unlockClipboardData);
312
313 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
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);
317
318 wStream* s = cliprdr_packet_unlock_clipdata_new(unlockClipboardData);
319
320 if (!s)
321 {
322 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_unlock_clipdata_new failed!");
323 return ERROR_INTERNAL_ERROR;
324 }
325
326 WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerUnlockClipboardData: clipDataId: 0x%08" PRIX32 "",
327 unlockClipboardData->clipDataId);
328 return cliprdr_server_packet_send(cliprdr, s);
329}
330
336static UINT cliprdr_server_format_data_request(CliprdrServerContext* context,
337 const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
338{
339 WINPR_ASSERT(context);
340 WINPR_ASSERT(formatDataRequest);
341
342 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
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);
346
347 /* payload is always one UINT32, hardcode size like the client-side sender */
348 wStream* s = cliprdr_packet_new(CB_FORMAT_DATA_REQUEST, 0, 4);
349
350 if (!s)
351 {
352 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_new failed!");
353 return ERROR_INTERNAL_ERROR;
354 }
355
356 if ((formatDataRequest->common.msgFlags & CB_RESPONSE_FAIL) == 0)
357 Stream_Write_UINT32(s,
358 formatDataRequest->requestedFormatId); /* requestedFormatId (4 bytes) */
359 WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientFormatDataRequest");
360 return cliprdr_server_packet_send(cliprdr, s);
361}
362
368static UINT
369cliprdr_server_format_data_response(CliprdrServerContext* context,
370 const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
371{
372 WINPR_ASSERT(context);
373 WINPR_ASSERT(formatDataResponse);
374
375 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
376
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);
380
381 wStream* s = cliprdr_packet_new(CB_FORMAT_DATA_RESPONSE, formatDataResponse->common.msgFlags,
382 formatDataResponse->common.dataLen);
383
384 if (!s)
385 {
386 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_new failed!");
387 return ERROR_INTERNAL_ERROR;
388 }
389
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);
395}
396
402static UINT
403cliprdr_server_file_contents_request(CliprdrServerContext* context,
404 const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
405{
406 WINPR_ASSERT(context);
407 WINPR_ASSERT(fileContentsRequest);
408
409 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
410
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);
414
415 wStream* s = cliprdr_packet_file_contents_request_new(fileContentsRequest);
416 if (!s)
417 {
418 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_file_contents_request_new failed!");
419 return ERROR_INTERNAL_ERROR;
420 }
421
422 WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFileContentsRequest: streamId: 0x%08" PRIX32 "",
423 fileContentsRequest->streamId);
424 return cliprdr_server_packet_send(cliprdr, s);
425}
426
432static UINT
433cliprdr_server_file_contents_response(CliprdrServerContext* context,
434 const CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse)
435{
436 WINPR_ASSERT(context);
437 WINPR_ASSERT(fileContentsResponse);
438
439 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
440 WINPR_ASSERT(cliprdr);
441
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);
445
446 wStream* s = cliprdr_packet_file_contents_response_new(fileContentsResponse);
447 if (!s)
448 {
449 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_file_contents_response_new failed!");
450 return ERROR_INTERNAL_ERROR;
451 }
452
453 WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFileContentsResponse: streamId: 0x%08" PRIX32 "",
454 fileContentsResponse->streamId);
455 return cliprdr_server_packet_send(cliprdr, s);
456}
457
463static UINT cliprdr_server_receive_general_capability(CliprdrServerContext* context, wStream* s,
465{
466 WINPR_ASSERT(context);
467 WINPR_ASSERT(cap_set);
468
469 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
470 WINPR_ASSERT(cliprdr);
471
472 if (cap_set->capabilitySetLength != sizeof(CLIPRDR_GENERAL_CAPABILITY_SET))
473 {
474 WLog_Print(cliprdr->log, WLOG_ERROR,
475 "invalid capabilitySetLength %" PRIu16 " != %" PRIuz
476 " (capabilitySetType=CB_CAPSTYPE_GENERAL)",
477 cap_set->capabilitySetLength, sizeof(CLIPRDR_GENERAL_CAPABILITY_SET));
478 return ERROR_INVALID_DATA;
479 }
480
481 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
482 return ERROR_INVALID_DATA;
483
484 Stream_Read_UINT32(s, cap_set->version); /* version (4 bytes) */
485 Stream_Read_UINT32(s, cap_set->generalFlags); /* generalFlags (4 bytes) */
486
487 if (context->useLongFormatNames)
488 context->useLongFormatNames = (cap_set->generalFlags & CB_USE_LONG_FORMAT_NAMES) != 0;
489
490 if (context->streamFileClipEnabled)
491 context->streamFileClipEnabled = (cap_set->generalFlags & CB_STREAM_FILECLIP_ENABLED) != 0;
492
493 if (context->fileClipNoFilePaths)
494 context->fileClipNoFilePaths = (cap_set->generalFlags & CB_FILECLIP_NO_FILE_PATHS) != 0;
495
496 if (context->canLockClipData)
497 context->canLockClipData = (cap_set->generalFlags & CB_CAN_LOCK_CLIPDATA) != 0;
498
499 if (context->hasHugeFileSupport)
500 context->hasHugeFileSupport = (cap_set->generalFlags & CB_HUGE_FILE_SUPPORT_ENABLED) != 0;
501
502 return CHANNEL_RC_OK;
503}
504
505WINPR_ATTR_NODISCARD
506static BOOL cliprdr_capabilities_contain_capset(wLog* log, const CLIPRDR_CAPABILITIES* capabilities,
507 size_t capabilitiesSize,
508 const CLIPRDR_CAPABILITY_SET* capset)
509{
510 WINPR_ASSERT(capabilities);
511
512 const BYTE* offset = (const BYTE*)capabilities->capabilitySets;
513 size_t byteOffset = 0;
514 for (UINT32 x = 0; x < capabilities->cCapabilitiesSets; x++)
515 {
516 const CLIPRDR_CAPABILITY_SET* cur =
517 WINPR_PACKED_ALIGN_CAST(const CLIPRDR_CAPABILITY_SET*, offset);
518 byteOffset += cur->capabilitySetLength;
519 if (byteOffset > capabilitiesSize)
520 return FALSE;
521
522 offset += cur->capabilitySetLength;
523
524 if (cur->capabilitySetType == capset->capabilitySetType)
525 {
526 WLog_Print(log, WLOG_ERROR, "duplicate CLIPRDR_CAPABILITY_SET %" PRIu16,
527 cur->capabilitySetType);
528 return TRUE;
529 }
530 }
531 return FALSE;
532}
533
539static UINT cliprdr_server_receive_capabilities(CliprdrServerContext* context, wStream* s,
540 const CLIPRDR_HEADER* header)
541{
542 UINT error = ERROR_INVALID_DATA;
543 size_t cap_sets_size = 0;
544 CLIPRDR_CAPABILITIES capabilities = WINPR_C_ARRAY_INIT;
545
546 WINPR_ASSERT(context);
547 WINPR_UNUSED(header);
548
549 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
550 WINPR_ASSERT(cliprdr);
551
552 WLog_Print(cliprdr->log, WLOG_DEBUG, "CliprdrClientCapabilities");
553 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
554 return ERROR_INVALID_DATA;
555
556 const UINT16 cCapabilitiesSets = Stream_Get_UINT16(s); /* cCapabilitiesSets (2 bytes) */
557 Stream_Seek_UINT16(s); /* pad1 (2 bytes) */
558
559 for (size_t index = 0; index < cCapabilitiesSets; index++)
560 {
561 const size_t cap_set_offset = cap_sets_size;
562 if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(CLIPRDR_CAPABILITY_SET)))
563 goto out;
564 const UINT16 capabilitySetType = Stream_Get_UINT16(s); /* capabilitySetType (2 bytes) */
565 const UINT16 capabilitySetLength = Stream_Get_UINT16(s); /* capabilitySetLength (2 bytes) */
566
567 if (capabilitySetLength < sizeof(CLIPRDR_CAPABILITY_SET))
568 {
569 WLog_Print(cliprdr->log, WLOG_ERROR,
570 "invalid capabilitySetLength %" PRIu16 " < %" PRIuz
571 " (capabilitySetType=%" PRIu16 ")",
572 capabilitySetLength, sizeof(CLIPRDR_CAPABILITY_SET), capabilitySetType);
573 goto out;
574 }
575
576 if (!Stream_CheckAndLogRequiredLength(
577 TAG, s, (size_t)capabilitySetLength - sizeof(CLIPRDR_CAPABILITY_SET)))
578 goto out;
579
580 cap_sets_size += capabilitySetLength;
581
582 CLIPRDR_CAPABILITY_SET* tmp = realloc(capabilities.capabilitySets, cap_sets_size);
583 if (tmp == nullptr)
584 {
585 WLog_Print(cliprdr->log, WLOG_ERROR, "capabilities.capabilitySets realloc failed!");
586 free(capabilities.capabilitySets);
587 return CHANNEL_RC_NO_MEMORY;
588 }
589
590 capabilities.capabilitySets = tmp;
591
592 CLIPRDR_CAPABILITY_SET* capSet = WINPR_PACKED_ALIGN_CAST(
593 CLIPRDR_CAPABILITY_SET*, (((BYTE*)capabilities.capabilitySets) + cap_set_offset));
594
595 capSet->capabilitySetType = capabilitySetType;
596 capSet->capabilitySetLength = capabilitySetLength;
597
598 if (cliprdr_capabilities_contain_capset(cliprdr->log, &capabilities, cap_sets_size, capSet))
599 goto out;
600
601 capabilities.cCapabilitiesSets++;
602
603 switch (capSet->capabilitySetType)
604 {
605 case CB_CAPSTYPE_GENERAL:
606 error = cliprdr_server_receive_general_capability(
607 context, s, WINPR_PACKED_ALIGN_CAST(CLIPRDR_GENERAL_CAPABILITY_SET*, capSet));
608 if (error)
609 {
610 WLog_Print(
611 cliprdr->log, WLOG_ERROR,
612 "cliprdr_server_receive_general_capability failed with error %" PRIu32 "",
613 error);
614 goto out;
615 }
616 break;
617
618 default:
619 WLog_Print(cliprdr->log, WLOG_ERROR, "unknown cliprdr capability set: %" PRIu16 "",
620 capSet->capabilitySetType);
621 goto out;
622 }
623 }
624
625 error = CHANNEL_RC_OK;
626 IFCALLRET(context->ClientCapabilities, error, context, &capabilities);
627out:
628 free(capabilities.capabilitySets);
629 return error;
630}
631
637static UINT cliprdr_server_receive_temporary_directory(CliprdrServerContext* context, wStream* s,
638 const CLIPRDR_HEADER* header)
639{
640 UINT error = CHANNEL_RC_OK;
641
642 WINPR_ASSERT(context);
643 WINPR_UNUSED(header);
644
645 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
646 WINPR_ASSERT(cliprdr);
647
648 if (!Stream_CheckAndLogRequiredLength(TAG, s,
649 ARRAYSIZE(cliprdr->temporaryDirectory) * sizeof(WCHAR)))
650 return CHANNEL_RC_NO_MEMORY;
651
652 const WCHAR* wszTempDir = Stream_ConstPointer(s);
653
654 if (wszTempDir[ARRAYSIZE(cliprdr->temporaryDirectory) - 1] != 0)
655 {
656 WLog_Print(cliprdr->log, WLOG_ERROR, "wszTempDir[259] was not 0");
657 return ERROR_INVALID_DATA;
658 }
659
660 if (ConvertWCharNToUtf8(wszTempDir, ARRAYSIZE(cliprdr->temporaryDirectory),
661 cliprdr->temporaryDirectory,
662 ARRAYSIZE(cliprdr->temporaryDirectory)) < 0)
663 {
664 WLog_Print(cliprdr->log, WLOG_ERROR, "failed to convert temporary directory name");
665 return ERROR_INVALID_DATA;
666 }
667
668 size_t length = strnlen(cliprdr->temporaryDirectory, ARRAYSIZE(cliprdr->temporaryDirectory));
669
670 if (length >= ARRAYSIZE(cliprdr->temporaryDirectory))
671 length = ARRAYSIZE(cliprdr->temporaryDirectory) - 1;
672
673 CLIPRDR_TEMP_DIRECTORY tempDirectory = WINPR_C_ARRAY_INIT;
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);
679
680 if (error)
681 WLog_Print(cliprdr->log, WLOG_ERROR, "TempDirectory failed with error %" PRIu32 "!", error);
682
683 return error;
684}
685
691static UINT cliprdr_server_receive_format_list(CliprdrServerContext* context, wStream* s,
692 const CLIPRDR_HEADER* header)
693{
694 UINT error = CHANNEL_RC_OK;
695
696 WINPR_ASSERT(context);
697 WINPR_ASSERT(header);
698
699 CLIPRDR_FORMAT_LIST formatList = { .common = { .msgType = CB_FORMAT_LIST,
700 .msgFlags = header->msgFlags,
701 .dataLen = header->dataLen },
702 .numFormats = 0,
703 .formats = nullptr };
704 wLog* log = WLog_Get(TAG);
705 if ((error = cliprdr_read_format_list(log, s, &formatList, context->useLongFormatNames)))
706 goto out;
707
708 WLog_Print(log, WLOG_DEBUG, "ClientFormatList: numFormats: %" PRIu32 "", formatList.numFormats);
709 IFCALLRET(context->ClientFormatList, error, context, &formatList);
710
711 if (error)
712 WLog_Print(log, WLOG_ERROR, "ClientFormatList failed with error %" PRIu32 "!", error);
713
714out:
715 cliprdr_free_format_list(&formatList);
716 return error;
717}
718
724static UINT cliprdr_server_receive_format_list_response(CliprdrServerContext* context, wStream* s,
725 const CLIPRDR_HEADER* header)
726{
727 UINT error = CHANNEL_RC_OK;
728
729 WINPR_ASSERT(context);
730 WINPR_ASSERT(header);
731
732 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
733 WINPR_ASSERT(cliprdr);
734
735 WINPR_UNUSED(s);
736 WLog_Print(cliprdr->log, WLOG_DEBUG, "CliprdrClientFormatListResponse");
737
738 CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse = { .common = { .msgType =
739 CB_FORMAT_LIST_RESPONSE,
740 .msgFlags = header->msgFlags,
741 .dataLen = header->dataLen } };
742 IFCALLRET(context->ClientFormatListResponse, error, context, &formatListResponse);
743
744 if (error)
745 WLog_Print(cliprdr->log, WLOG_ERROR,
746 "ClientFormatListResponse failed with error %" PRIu32 "!", error);
747
748 return error;
749}
750
756static UINT cliprdr_server_receive_lock_clipdata(CliprdrServerContext* context, wStream* s,
757 const CLIPRDR_HEADER* header)
758{
759 UINT error = CHANNEL_RC_OK;
760
761 WINPR_ASSERT(context);
762 WINPR_ASSERT(header);
763
764 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
765 WINPR_ASSERT(cliprdr);
766
767 WLog_Print(cliprdr->log, WLOG_DEBUG, "CliprdrClientLockClipData");
768
769 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
770 return ERROR_INVALID_DATA;
771
772 CLIPRDR_LOCK_CLIPBOARD_DATA lockClipboardData = { .common = { .msgType = CB_LOCK_CLIPDATA,
773 .msgFlags = header->msgFlags,
774 .dataLen = header->dataLen },
775 .clipDataId = 0 };
776
777 Stream_Read_UINT32(s, lockClipboardData.clipDataId); /* clipDataId (4 bytes) */
778 IFCALLRET(context->ClientLockClipboardData, error, context, &lockClipboardData);
779
780 if (error)
781 WLog_Print(cliprdr->log, WLOG_ERROR,
782 "ClientLockClipboardData failed with error %" PRIu32 "!", error);
783
784 return error;
785}
786
792static UINT cliprdr_server_receive_unlock_clipdata(CliprdrServerContext* context, wStream* s,
793 const CLIPRDR_HEADER* header)
794{
795 WINPR_ASSERT(context);
796 WINPR_ASSERT(header);
797
798 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
799 WINPR_ASSERT(cliprdr);
800
801 WLog_Print(cliprdr->log, WLOG_DEBUG, "CliprdrClientUnlockClipData");
802
803 CLIPRDR_UNLOCK_CLIPBOARD_DATA unlockClipboardData = { .common = { .msgType = CB_UNLOCK_CLIPDATA,
804 .msgFlags = header->msgFlags,
805 .dataLen = header->dataLen },
806 .clipDataId = 0 };
807
808 UINT error = cliprdr_read_unlock_clipdata(s, &unlockClipboardData);
809 if (error)
810 return error;
811
812 IFCALLRET(context->ClientUnlockClipboardData, error, context, &unlockClipboardData);
813
814 if (error)
815 WLog_Print(cliprdr->log, WLOG_ERROR,
816 "ClientUnlockClipboardData failed with error %" PRIu32 "!", error);
817
818 return error;
819}
820
826static UINT cliprdr_server_receive_format_data_request(CliprdrServerContext* context, wStream* s,
827 const CLIPRDR_HEADER* header)
828{
829 WINPR_ASSERT(context);
830 WINPR_ASSERT(header);
831
832 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
833 WINPR_ASSERT(cliprdr);
834
835 WLog_Print(cliprdr->log, WLOG_DEBUG, "CliprdrClientFormatDataRequest");
836
837 CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest = { .common = { .msgType = CB_FORMAT_DATA_REQUEST,
838 .msgFlags = header->msgFlags,
839 .dataLen = header->dataLen },
840 .requestedFormatId = 0 };
841
842 UINT error = cliprdr_read_format_data_request(s, &formatDataRequest);
843 if (error)
844 return error;
845
846 context->lastRequestedFormatId = formatDataRequest.requestedFormatId;
847 IFCALLRET(context->ClientFormatDataRequest, error, context, &formatDataRequest);
848
849 if (error)
850 WLog_Print(cliprdr->log, WLOG_ERROR,
851 "ClientFormatDataRequest failed with error %" PRIu32 "!", error);
852
853 return error;
854}
855
861static UINT cliprdr_server_receive_format_data_response(CliprdrServerContext* context, wStream* s,
862 const CLIPRDR_HEADER* header)
863{
864 WINPR_ASSERT(context);
865 WINPR_ASSERT(header);
866
867 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
868 WINPR_ASSERT(cliprdr);
869
870 WLog_Print(cliprdr->log, WLOG_DEBUG, "CliprdrClientFormatDataResponse");
871
872 CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse = { .common = { .msgType =
873 CB_FORMAT_DATA_RESPONSE,
874 .msgFlags = header->msgFlags,
875 .dataLen = header->dataLen },
876 .requestedFormatData = nullptr };
877
878 UINT error = cliprdr_read_format_data_response(s, &formatDataResponse);
879 if (error)
880 return error;
881
882 IFCALLRET(context->ClientFormatDataResponse, error, context, &formatDataResponse);
883
884 if (error)
885 WLog_Print(cliprdr->log, WLOG_ERROR,
886 "ClientFormatDataResponse failed with error %" PRIu32 "!", error);
887
888 return error;
889}
890
896static UINT cliprdr_server_receive_filecontents_request(CliprdrServerContext* context, wStream* s,
897 const CLIPRDR_HEADER* header)
898{
899 WINPR_ASSERT(context);
900 WINPR_ASSERT(header);
901
902 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
903 WINPR_ASSERT(cliprdr);
904
905 WLog_Print(cliprdr->log, WLOG_DEBUG, "CliprdrClientFileContentsRequest");
906
907 CLIPRDR_FILE_CONTENTS_REQUEST request = { .common = { .msgType = CB_FILECONTENTS_REQUEST,
908 .msgFlags = header->msgFlags,
909 .dataLen = header->dataLen },
910 .streamId = 0,
911 .listIndex = 0,
912 .dwFlags = 0,
913 .nPositionLow = 0,
914 .nPositionHigh = 0,
915 .cbRequested = 0,
916 .haveClipDataId = FALSE,
917 .clipDataId = 0 };
918
919 UINT error = cliprdr_read_file_contents_request(s, &request);
920 if (error)
921 return error;
922
923 if (!context->hasHugeFileSupport)
924 {
925 if (request.nPositionHigh > 0)
926 return ERROR_INVALID_DATA;
927 if ((UINT64)request.nPositionLow + request.cbRequested > UINT32_MAX)
928 return ERROR_INVALID_DATA;
929 }
930 IFCALLRET(context->ClientFileContentsRequest, error, context, &request);
931
932 if (error)
933 WLog_Print(cliprdr->log, WLOG_ERROR,
934 "ClientFileContentsRequest failed with error %" PRIu32 "!", error);
935
936 return error;
937}
938
944static UINT cliprdr_server_receive_filecontents_response(CliprdrServerContext* context, wStream* s,
945 const CLIPRDR_HEADER* header)
946{
947 UINT error = CHANNEL_RC_OK;
948
949 WINPR_ASSERT(context);
950 WINPR_ASSERT(header);
951
952 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
953 WINPR_ASSERT(cliprdr);
954
955 WLog_Print(cliprdr->log, WLOG_DEBUG, "CliprdrClientFileContentsResponse");
956
957 CLIPRDR_FILE_CONTENTS_RESPONSE response = { .common = { .msgType = CB_FILECONTENTS_RESPONSE,
958 .msgFlags = header->msgFlags,
959 .dataLen = header->dataLen },
960 .streamId = 0,
961 .cbRequested = 0,
962 .requestedData = nullptr };
963
964 if ((error = cliprdr_read_file_contents_response(s, &response)))
965 return error;
966
967 IFCALLRET(context->ClientFileContentsResponse, error, context, &response);
968
969 if (error)
970 WLog_Print(cliprdr->log, WLOG_ERROR,
971 "ClientFileContentsResponse failed with error %" PRIu32 "!", error);
972
973 return error;
974}
975
981static UINT cliprdr_server_receive_pdu(CliprdrServerContext* context, wStream* s,
982 const CLIPRDR_HEADER* header)
983{
984 UINT error = 0;
985
986 WINPR_ASSERT(context);
987 WINPR_ASSERT(header);
988
989 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
990 WINPR_ASSERT(cliprdr);
991
992 {
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)),
999 header->dataLen);
1000 }
1001
1002 const size_t start = Stream_GetPosition(s);
1003 switch (header->msgType)
1004 {
1005 case CB_CLIP_CAPS:
1006 error = cliprdr_server_receive_capabilities(context, s, header);
1007 break;
1008
1009 case CB_TEMP_DIRECTORY:
1010 error = cliprdr_server_receive_temporary_directory(context, s, header);
1011 break;
1012
1013 case CB_FORMAT_LIST:
1014 error = cliprdr_server_receive_format_list(context, s, header);
1015 break;
1016
1017 case CB_FORMAT_LIST_RESPONSE:
1018 error = cliprdr_server_receive_format_list_response(context, s, header);
1019 break;
1020
1021 case CB_LOCK_CLIPDATA:
1022 error = cliprdr_server_receive_lock_clipdata(context, s, header);
1023 break;
1024
1025 case CB_UNLOCK_CLIPDATA:
1026 error = cliprdr_server_receive_unlock_clipdata(context, s, header);
1027 break;
1028
1029 case CB_FORMAT_DATA_REQUEST:
1030 error = cliprdr_server_receive_format_data_request(context, s, header);
1031 break;
1032
1033 case CB_FORMAT_DATA_RESPONSE:
1034 error = cliprdr_server_receive_format_data_response(context, s, header);
1035 break;
1036
1037 case CB_FILECONTENTS_REQUEST:
1038 error = cliprdr_server_receive_filecontents_request(context, s, header);
1039 break;
1040
1041 case CB_FILECONTENTS_RESPONSE:
1042 error = cliprdr_server_receive_filecontents_response(context, s, header);
1043 break;
1044
1045 default:
1046 error = ERROR_INVALID_DATA;
1047 WLog_Print(cliprdr->log, WLOG_ERROR, "Unexpected clipboard PDU type: %" PRIu16 "",
1048 header->msgType);
1049 break;
1050 }
1051
1052 const size_t end = Stream_GetPosition(s);
1053 const size_t diff = end - start;
1054 if (diff != header->dataLen)
1055 {
1056 char buffer1[64] = WINPR_C_ARRAY_INIT;
1057 WLog_WARN(
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);
1060 }
1061
1062 const size_t rem = Stream_GetRemainingLength(s);
1063 if (rem > 0)
1064 {
1065 char buffer1[64] = WINPR_C_ARRAY_INIT;
1066 char buffer2[64] = WINPR_C_ARRAY_INIT;
1067 WLog_WARN(
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);
1071 }
1072 return error;
1073}
1074
1080static UINT cliprdr_server_init(CliprdrServerContext* context)
1081{
1082 UINT32 generalFlags = 0;
1083 UINT error = 0;
1084 CLIPRDR_MONITOR_READY monitorReady = WINPR_C_ARRAY_INIT;
1085
1086 WINPR_ASSERT(context);
1087
1088 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
1089 WINPR_ASSERT(cliprdr);
1090
1091 monitorReady.common.msgType = CB_MONITOR_READY;
1092
1093 if (context->useLongFormatNames)
1094 generalFlags |= CB_USE_LONG_FORMAT_NAMES;
1095
1096 if (context->streamFileClipEnabled)
1097 generalFlags |= CB_STREAM_FILECLIP_ENABLED;
1098
1099 if (context->fileClipNoFilePaths)
1100 generalFlags |= CB_FILECLIP_NO_FILE_PATHS;
1101
1102 if (context->canLockClipData)
1103 generalFlags |= CB_CAN_LOCK_CLIPDATA;
1104
1105 if (context->hasHugeFileSupport)
1106 generalFlags |= CB_HUGE_FILE_SUPPORT_ENABLED;
1107
1108 CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet = { .capabilitySetType =
1109 CB_CAPSTYPE_GENERAL,
1110 .capabilitySetLength =
1111 CB_CAPSTYPE_GENERAL_LEN,
1112 .version = CB_CAPS_VERSION_2,
1113 .generalFlags = generalFlags };
1114
1115 CLIPRDR_CAPABILITIES capabilities = { .common = { .msgType = CB_CLIP_CAPS,
1116 .msgFlags = 0,
1117 .dataLen = 4 + CB_CAPSTYPE_GENERAL_LEN },
1118 .cCapabilitiesSets = 1,
1119 .capabilitySets =
1120 (CLIPRDR_CAPABILITY_SET*)&generalCapabilitySet };
1121
1122 if ((error = context->ServerCapabilities(context, &capabilities)))
1123 {
1124 WLog_Print(cliprdr->log, WLOG_ERROR, "ServerCapabilities failed with error %" PRIu32 "!",
1125 error);
1126 return error;
1127 }
1128
1129 if ((error = context->MonitorReady(context, &monitorReady)))
1130 {
1131 WLog_Print(cliprdr->log, WLOG_ERROR, "MonitorReady failed with error %" PRIu32 "!", error);
1132 return error;
1133 }
1134
1135 return error;
1136}
1137
1143static UINT cliprdr_server_read(CliprdrServerContext* context)
1144{
1145 WINPR_ASSERT(context);
1146
1147 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
1148 WINPR_ASSERT(cliprdr);
1149
1150 BOOL ok = FALSE;
1151 wStream* s = ChannelPduTracker_poll(cliprdr->channelPduTracker, &ok);
1152
1153 if (!ok)
1154 return ERROR_INVALID_DATA;
1155
1156 if (!s)
1157 return CHANNEL_RC_OK;
1158
1159 UINT ret = ERROR_INVALID_DATA;
1160 if (!Stream_CheckAndLogRequiredLengthWLog(cliprdr->log, s, 8))
1161 goto out;
1162
1163 const CLIPRDR_HEADER header = {
1164 .msgType = Stream_Get_UINT16(s), /* msgType (2 bytes) */
1165 .msgFlags = Stream_Get_UINT16(s), /* msgFlags (2 bytes) */
1166 .dataLen = Stream_Get_UINT32(s) /* dataLen (4 bytes) */
1167 };
1168
1169 if (!Stream_CheckAndLogRequiredLengthWLog(cliprdr->log, s, header.dataLen))
1170 goto out;
1171
1172 ret = cliprdr_server_receive_pdu(context, s, &header);
1173 if (ret != CHANNEL_RC_OK)
1174 {
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);
1181 }
1182
1183out:
1184 Stream_Release(s);
1185 return ret;
1186}
1187
1188static DWORD WINAPI cliprdr_server_thread(LPVOID arg)
1189{
1190 DWORD status = 0;
1191 DWORD nCount = 0;
1192 HANDLE events[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
1193 CliprdrServerContext* context = (CliprdrServerContext*)arg;
1194 UINT error = CHANNEL_RC_OK;
1195
1196 WINPR_ASSERT(context);
1197
1198 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
1199 WINPR_ASSERT(cliprdr);
1200
1201 HANDLE ChannelEvent = context->GetEventHandle(context);
1202
1203 events[nCount++] = cliprdr->StopEvent;
1204 events[nCount++] = ChannelEvent;
1205
1206 if (context->autoInitializationSequence)
1207 {
1208 if ((error = cliprdr_server_init(context)))
1209 {
1210 WLog_Print(cliprdr->log, WLOG_ERROR,
1211 "cliprdr_server_init failed with error %" PRIu32 "!", error);
1212 goto out;
1213 }
1214 }
1215
1216 while (1)
1217 {
1218 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
1219
1220 if (status == WAIT_FAILED)
1221 {
1222 error = GetLastError();
1223 WLog_Print(cliprdr->log, WLOG_ERROR,
1224 "WaitForMultipleObjects failed with error %" PRIu32 "", error);
1225 goto out;
1226 }
1227
1228 status = WaitForSingleObject(cliprdr->StopEvent, 0);
1229
1230 if (status == WAIT_FAILED)
1231 {
1232 error = GetLastError();
1233 WLog_Print(cliprdr->log, WLOG_ERROR,
1234 "WaitForSingleObject failed with error %" PRIu32 "", error);
1235 goto out;
1236 }
1237
1238 if (status == WAIT_OBJECT_0)
1239 break;
1240
1241 status = WaitForSingleObject(ChannelEvent, 0);
1242
1243 if (status == WAIT_FAILED)
1244 {
1245 error = GetLastError();
1246 WLog_Print(cliprdr->log, WLOG_ERROR,
1247 "WaitForSingleObject failed with error %" PRIu32 "", error);
1248 goto out;
1249 }
1250
1251 if (status == WAIT_OBJECT_0)
1252 {
1253 if ((error = context->CheckEventHandle(context)))
1254 {
1255 WLog_Print(cliprdr->log, WLOG_ERROR,
1256 "CheckEventHandle failed with error %" PRIu32 "!", error);
1257 break;
1258 }
1259 }
1260 }
1261
1262out:
1263
1264 if (error && context->rdpcontext)
1265 setChannelError(context->rdpcontext, error, "cliprdr_server_thread reported an error");
1266
1267 ExitThread(error);
1268 return error;
1269}
1270
1276static UINT cliprdr_server_open(CliprdrServerContext* context)
1277{
1278 WINPR_ASSERT(context);
1279
1280 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
1281 WINPR_ASSERT(cliprdr);
1282
1283 PULONG pSessionId = nullptr;
1284 DWORD BytesReturned = 0;
1285
1286 if (!WTSQuerySessionInformationA(cliprdr->vcm, WTS_CURRENT_SESSION, WTSSessionId,
1287 (LPSTR*)&pSessionId, &BytesReturned))
1288 return CHANNEL_RC_BAD_CHANNEL;
1289
1290 DWORD SessionId = (DWORD)*pSessionId;
1291 WTSFreeMemory(pSessionId);
1292
1293 cliprdr->ChannelHandle =
1294 WTSVirtualChannelOpenEx(SessionId, CLIPRDR_SVC_CHANNEL_NAME, CHANNEL_OPTION_SHOW_PROTOCOL);
1295 if (!cliprdr->ChannelHandle)
1296 {
1297 WLog_Print(cliprdr->log, WLOG_ERROR, "WTSVirtualChannelOpen for cliprdr failed!");
1298 return ERROR_INTERNAL_ERROR;
1299 }
1300
1301 WINPR_ASSERT(cliprdr->channelPduTracker == nullptr);
1302 cliprdr->channelPduTracker = ChannelPduTracker_new(cliprdr->ChannelHandle);
1303 if (!cliprdr->channelPduTracker)
1304 {
1305 WLog_Print(cliprdr->log, WLOG_ERROR, "ChannelPduTracker_new for cliprdr failed!");
1306 return ERROR_INTERNAL_ERROR;
1307 }
1308
1309 cliprdr->ChannelEvent = nullptr;
1310
1311 void* buffer = nullptr;
1312
1313 if (WTSVirtualChannelQuery(cliprdr->ChannelHandle, WTSVirtualEventHandle, &buffer,
1314 &BytesReturned))
1315 {
1316 if (BytesReturned != sizeof(HANDLE))
1317 {
1318 WLog_Print(cliprdr->log, WLOG_ERROR, "BytesReturned has not size of HANDLE!");
1319 return ERROR_INTERNAL_ERROR;
1320 }
1321
1322 cliprdr->ChannelEvent = *(HANDLE*)buffer;
1323 WTSFreeMemory(buffer);
1324 }
1325
1326 if (!cliprdr->ChannelEvent)
1327 {
1328 WLog_Print(cliprdr->log, WLOG_ERROR, "WTSVirtualChannelQuery for cliprdr failed!");
1329 return ERROR_INTERNAL_ERROR;
1330 }
1331
1332 return CHANNEL_RC_OK;
1333}
1334
1340static UINT cliprdr_server_close(CliprdrServerContext* context)
1341{
1342 WINPR_ASSERT(context);
1343
1344 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
1345 WINPR_ASSERT(cliprdr);
1346
1347 ChannelPduTracker_free(cliprdr->channelPduTracker);
1348 cliprdr->channelPduTracker = nullptr;
1349
1350 if (cliprdr->ChannelHandle)
1351 {
1352 (void)WTSVirtualChannelClose(cliprdr->ChannelHandle);
1353 cliprdr->ChannelHandle = nullptr;
1354 }
1355
1356 return CHANNEL_RC_OK;
1357}
1358
1364static UINT cliprdr_server_start(CliprdrServerContext* context)
1365{
1366 UINT error = 0;
1367
1368 WINPR_ASSERT(context);
1369
1370 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
1371 WINPR_ASSERT(cliprdr);
1372
1373 if (!cliprdr->ChannelHandle)
1374 {
1375 error = context->Open(context);
1376 if (error)
1377 {
1378 WLog_Print(cliprdr->log, WLOG_ERROR, "Open failed!");
1379 return error;
1380 }
1381 }
1382
1383 cliprdr->StopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
1384 if (!cliprdr->StopEvent)
1385 {
1386 WLog_Print(cliprdr->log, WLOG_ERROR, "CreateEvent failed!");
1387 return ERROR_INTERNAL_ERROR;
1388 }
1389
1390 cliprdr->Thread = CreateThread(nullptr, 0, cliprdr_server_thread, (void*)context, 0, nullptr);
1391 if (!cliprdr->Thread)
1392 {
1393 WLog_Print(cliprdr->log, WLOG_ERROR, "CreateThread failed!");
1394 (void)CloseHandle(cliprdr->StopEvent);
1395 cliprdr->StopEvent = nullptr;
1396 return ERROR_INTERNAL_ERROR;
1397 }
1398
1399 return CHANNEL_RC_OK;
1400}
1401
1407static UINT cliprdr_server_stop(CliprdrServerContext* context)
1408{
1409 UINT error = CHANNEL_RC_OK;
1410
1411 WINPR_ASSERT(context);
1412
1413 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
1414 WINPR_ASSERT(cliprdr);
1415
1416 if (cliprdr->StopEvent)
1417 {
1418 (void)SetEvent(cliprdr->StopEvent);
1419
1420 if (WaitForSingleObject(cliprdr->Thread, INFINITE) == WAIT_FAILED)
1421 {
1422 error = GetLastError();
1423 WLog_Print(cliprdr->log, WLOG_ERROR,
1424 "WaitForSingleObject failed with error %" PRIu32 "", error);
1425 return error;
1426 }
1427
1428 (void)CloseHandle(cliprdr->Thread);
1429 (void)CloseHandle(cliprdr->StopEvent);
1430 }
1431
1432 if (cliprdr->ChannelHandle)
1433 return context->Close(context);
1434
1435 return error;
1436}
1437
1438static HANDLE cliprdr_server_get_event_handle(CliprdrServerContext* context)
1439{
1440 WINPR_ASSERT(context);
1441
1442 CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*)context->handle;
1443 WINPR_ASSERT(cliprdr);
1444 return cliprdr->ChannelEvent;
1445}
1446
1452static UINT cliprdr_server_check_event_handle(CliprdrServerContext* context)
1453{
1454 return cliprdr_server_read(context);
1455}
1456
1457CliprdrServerContext* cliprdr_server_context_new(HANDLE vcm)
1458{
1459 CliprdrServerContext* context = (CliprdrServerContext*)calloc(1, sizeof(CliprdrServerContext));
1460
1461 if (!context)
1462 goto fail;
1463
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;
1481
1482 CliprdrServerPrivate* cliprdr = context->handle =
1483 (CliprdrServerPrivate*)calloc(1, sizeof(CliprdrServerPrivate));
1484 if (!cliprdr)
1485 goto fail;
1486
1487 cliprdr->log = WLog_Get(TAG);
1488 cliprdr->vcm = vcm;
1489
1490 return context;
1491
1492fail:
1493 cliprdr_server_context_free(context);
1494 return nullptr;
1495}
1496
1497void cliprdr_server_context_free(CliprdrServerContext* context)
1498{
1499 if (!context)
1500 return;
1501
1502 free(context->handle);
1503 free(context);
1504}