FreeRDP
Loading...
Searching...
No Matches
client/cliprdr_main.c
1
23#include <freerdp/config.h>
24
25#include <winpr/wtypes.h>
26#include <winpr/assert.h>
27#include <winpr/crt.h>
28#include <winpr/print.h>
29#include <winpr/clipboard.h>
30
31#include <freerdp/types.h>
32#include <freerdp/constants.h>
33#include <freerdp/freerdp.h>
34#include <freerdp/client/cliprdr.h>
35
36#include "../../../channels/client/addin.h"
37
38#include "cliprdr_main.h"
39#include "cliprdr_format.h"
40#include "../cliprdr_common.h"
41
42const char type_FileGroupDescriptorW[] = "FileGroupDescriptorW";
43const char type_FileContents[] = "FileContents";
44
45CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr)
46{
47 CliprdrClientContext* pInterface = NULL;
48
49 if (!cliprdr)
50 return NULL;
51
52 pInterface = (CliprdrClientContext*)cliprdr->channelEntryPoints.pInterface;
53 return pInterface;
54}
55
61static UINT cliprdr_packet_send(cliprdrPlugin* cliprdr, wStream* s)
62{
63 UINT status = CHANNEL_RC_OK;
64
65 WINPR_ASSERT(cliprdr);
66 WINPR_ASSERT(s);
67
68 const size_t pos = Stream_GetPosition(s);
69 const size_t dataLen = pos - 8;
70 WINPR_ASSERT(dataLen <= UINT32_MAX);
71
72 Stream_SetPosition(s, 4);
73 Stream_Write_UINT32(s, (UINT32)dataLen);
74 Stream_SetPosition(s, pos);
75
76 WLog_Print(cliprdr->log, WLOG_DEBUG, "Cliprdr Sending (%" PRIu32 " bytes)", dataLen + 8);
77
78 if (!cliprdr)
79 {
80 status = CHANNEL_RC_BAD_INIT_HANDLE;
81 }
82 else
83 {
84 WINPR_ASSERT(cliprdr->channelEntryPoints.pVirtualChannelWriteEx);
85 status = cliprdr->channelEntryPoints.pVirtualChannelWriteEx(
86 cliprdr->InitHandle, cliprdr->OpenHandle, Stream_Buffer(s),
87 (UINT32)Stream_GetPosition(s), s);
88 }
89
90 if (status != CHANNEL_RC_OK)
91 {
92 Stream_Free(s, TRUE);
93 WLog_Print(cliprdr->log, WLOG_ERROR, "VirtualChannelWrite failed with %s [%08" PRIX32 "]",
94 WTSErrorToString(status), status);
95 }
96
97 return status;
98}
99
100UINT cliprdr_send_error_response(cliprdrPlugin* cliprdr, UINT16 type)
101{
102 wStream* s = cliprdr_packet_new(type, CB_RESPONSE_FAIL, 0);
103 if (!s)
104 {
105 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_new failed!");
106 return ERROR_OUTOFMEMORY;
107 }
108
109 return cliprdr_packet_send(cliprdr, s);
110}
111
112static void cliprdr_print_general_capability_flags(wLog* log, UINT32 flags)
113{
114 WLog_Print(log, WLOG_DEBUG, "generalFlags (0x%08" PRIX32 ") {", flags);
115
116 if (flags & CB_USE_LONG_FORMAT_NAMES)
117 WLog_Print(log, WLOG_DEBUG, "\tCB_USE_LONG_FORMAT_NAMES");
118
119 if (flags & CB_STREAM_FILECLIP_ENABLED)
120 WLog_Print(log, WLOG_DEBUG, "\tCB_STREAM_FILECLIP_ENABLED");
121
122 if (flags & CB_FILECLIP_NO_FILE_PATHS)
123 WLog_Print(log, WLOG_DEBUG, "\tCB_FILECLIP_NO_FILE_PATHS");
124
125 if (flags & CB_CAN_LOCK_CLIPDATA)
126 WLog_Print(log, WLOG_DEBUG, "\tCB_CAN_LOCK_CLIPDATA");
127
128 if (flags & CB_HUGE_FILE_SUPPORT_ENABLED)
129 WLog_Print(log, WLOG_DEBUG, "\tCB_HUGE_FILE_SUPPORT_ENABLED");
130
131 WLog_Print(log, WLOG_DEBUG, "}");
132}
133
139static UINT cliprdr_process_general_capability(cliprdrPlugin* cliprdr, wStream* s)
140{
141 UINT32 version = 0;
142 UINT32 generalFlags = 0;
143 CLIPRDR_CAPABILITIES capabilities = { 0 };
144 CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet = { 0 };
145 CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
146 UINT error = CHANNEL_RC_OK;
147
148 WINPR_ASSERT(cliprdr);
149 WINPR_ASSERT(s);
150
151 if (!context)
152 {
153 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_get_client_interface failed!");
154 return ERROR_INTERNAL_ERROR;
155 }
156
157 if (!Stream_CheckAndLogRequiredLengthWLog(cliprdr->log, s, 8))
158 return ERROR_INVALID_DATA;
159
160 Stream_Read_UINT32(s, version); /* version (4 bytes) */
161 Stream_Read_UINT32(s, generalFlags); /* generalFlags (4 bytes) */
162 WLog_Print(cliprdr->log, WLOG_DEBUG, "Version: %" PRIu32 "", version);
163
164 cliprdr_print_general_capability_flags(cliprdr->log, generalFlags);
165
166 cliprdr->useLongFormatNames = (generalFlags & CB_USE_LONG_FORMAT_NAMES) ? TRUE : FALSE;
167 cliprdr->streamFileClipEnabled = (generalFlags & CB_STREAM_FILECLIP_ENABLED) ? TRUE : FALSE;
168 cliprdr->fileClipNoFilePaths = (generalFlags & CB_FILECLIP_NO_FILE_PATHS) ? TRUE : FALSE;
169 cliprdr->canLockClipData = (generalFlags & CB_CAN_LOCK_CLIPDATA) ? TRUE : FALSE;
170 cliprdr->hasHugeFileSupport = (generalFlags & CB_HUGE_FILE_SUPPORT_ENABLED) ? TRUE : FALSE;
171 cliprdr->capabilitiesReceived = TRUE;
172
173 capabilities.common.msgType = CB_CLIP_CAPS;
174 capabilities.cCapabilitiesSets = 1;
175 capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*)&(generalCapabilitySet);
176 generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
177 generalCapabilitySet.capabilitySetLength = 12;
178 generalCapabilitySet.version = version;
179 generalCapabilitySet.generalFlags = generalFlags;
180 IFCALLRET(context->ServerCapabilities, error, context, &capabilities);
181
182 if (error)
183 WLog_Print(cliprdr->log, WLOG_ERROR, "ServerCapabilities failed with error %" PRIu32 "!",
184 error);
185
186 return error;
187}
188
194static UINT cliprdr_process_clip_caps(cliprdrPlugin* cliprdr, wStream* s,
195 WINPR_ATTR_UNUSED UINT32 length,
196 WINPR_ATTR_UNUSED UINT16 flags)
197{
198 UINT16 lengthCapability = 0;
199 UINT16 cCapabilitiesSets = 0;
200 UINT16 capabilitySetType = 0;
201 UINT error = CHANNEL_RC_OK;
202
203 WINPR_ASSERT(cliprdr);
204 WINPR_ASSERT(s);
205
206 if (!Stream_CheckAndLogRequiredLengthWLog(cliprdr->log, s, 4))
207 return ERROR_INVALID_DATA;
208
209 Stream_Read_UINT16(s, cCapabilitiesSets); /* cCapabilitiesSets (2 bytes) */
210 Stream_Seek_UINT16(s); /* pad1 (2 bytes) */
211 WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerCapabilities");
212
213 for (UINT16 index = 0; index < cCapabilitiesSets; index++)
214 {
215 if (!Stream_CheckAndLogRequiredLengthWLog(cliprdr->log, s, 4))
216 return ERROR_INVALID_DATA;
217
218 Stream_Read_UINT16(s, capabilitySetType); /* capabilitySetType (2 bytes) */
219 Stream_Read_UINT16(s, lengthCapability); /* lengthCapability (2 bytes) */
220
221 if ((lengthCapability < 4) ||
222 (!Stream_CheckAndLogRequiredLengthWLog(cliprdr->log, s, lengthCapability - 4U)))
223 return ERROR_INVALID_DATA;
224
225 switch (capabilitySetType)
226 {
227 case CB_CAPSTYPE_GENERAL:
228 if ((error = cliprdr_process_general_capability(cliprdr, s)))
229 {
230 WLog_Print(cliprdr->log, WLOG_ERROR,
231 "cliprdr_process_general_capability failed with error %" PRIu32 "!",
232 error);
233 return error;
234 }
235
236 break;
237
238 default:
239 WLog_Print(cliprdr->log, WLOG_ERROR, "unknown cliprdr capability set: %" PRIu16 "",
240 capabilitySetType);
241 return CHANNEL_RC_BAD_PROC;
242 }
243 }
244
245 return error;
246}
247
253static UINT cliprdr_process_monitor_ready(cliprdrPlugin* cliprdr, WINPR_ATTR_UNUSED wStream* s,
254 UINT32 length, UINT16 flags)
255{
256 CLIPRDR_MONITOR_READY monitorReady = { 0 };
257 CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
258 UINT error = CHANNEL_RC_OK;
259
260 WINPR_ASSERT(cliprdr);
261 WINPR_ASSERT(s);
262
263 WLog_Print(cliprdr->log, WLOG_DEBUG, "MonitorReady");
264
265 if (!cliprdr->capabilitiesReceived)
266 {
273 cliprdr->useLongFormatNames = FALSE;
274 cliprdr->streamFileClipEnabled = FALSE;
275 cliprdr->fileClipNoFilePaths = TRUE;
276 cliprdr->canLockClipData = FALSE;
277 }
278
279 monitorReady.common.msgType = CB_MONITOR_READY;
280 monitorReady.common.msgFlags = flags;
281 monitorReady.common.dataLen = length;
282 IFCALLRET(context->MonitorReady, error, context, &monitorReady);
283
284 if (error)
285 WLog_Print(cliprdr->log, WLOG_ERROR, "MonitorReady failed with error %" PRIu32 "!", error);
286
287 return error;
288}
289
295static UINT cliprdr_process_filecontents_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 length,
296 UINT16 flags)
297{
298 CLIPRDR_FILE_CONTENTS_REQUEST request = { 0 };
299 CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
300 UINT error = CHANNEL_RC_OK;
301
302 WINPR_ASSERT(cliprdr);
303 WINPR_ASSERT(s);
304
305 WLog_Print(cliprdr->log, WLOG_DEBUG, "FileContentsRequest");
306
307 request.common.msgType = CB_FILECONTENTS_REQUEST;
308 request.common.msgFlags = flags;
309 request.common.dataLen = length;
310
311 if ((error = cliprdr_read_file_contents_request(s, &request)))
312 return error;
313
314 const UINT32 mask =
315 freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask);
316 if ((mask & (CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES)) == 0)
317 {
318 WLog_Print(cliprdr->log, WLOG_WARN, "local -> remote file copy disabled, ignoring request");
319 return cliprdr_send_error_response(cliprdr, CB_FILECONTENTS_RESPONSE);
320 }
321 IFCALLRET(context->ServerFileContentsRequest, error, context, &request);
322
323 if (error)
324 WLog_Print(cliprdr->log, WLOG_ERROR,
325 "ServerFileContentsRequest failed with error %" PRIu32 "!", error);
326
327 return error;
328}
329
335static UINT cliprdr_process_filecontents_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 length,
336 UINT16 flags)
337{
338 CLIPRDR_FILE_CONTENTS_RESPONSE response = { 0 };
339 CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
340 UINT error = CHANNEL_RC_OK;
341
342 WINPR_ASSERT(cliprdr);
343 WINPR_ASSERT(s);
344
345 WLog_Print(cliprdr->log, WLOG_DEBUG, "FileContentsResponse");
346
347 response.common.msgType = CB_FILECONTENTS_RESPONSE;
348 response.common.msgFlags = flags;
349 response.common.dataLen = length;
350
351 if ((error = cliprdr_read_file_contents_response(s, &response)))
352 return error;
353
354 IFCALLRET(context->ServerFileContentsResponse, error, context, &response);
355
356 if (error)
357 WLog_Print(cliprdr->log, WLOG_ERROR,
358 "ServerFileContentsResponse failed with error %" PRIu32 "!", error);
359
360 return error;
361}
362
368static UINT cliprdr_process_lock_clipdata(cliprdrPlugin* cliprdr, wStream* s, UINT32 length,
369 UINT16 flags)
370{
371 CLIPRDR_LOCK_CLIPBOARD_DATA lockClipboardData = { 0 };
372 CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
373 UINT error = CHANNEL_RC_OK;
374
375 WINPR_ASSERT(cliprdr);
376 WINPR_ASSERT(s);
377
378 WLog_Print(cliprdr->log, WLOG_DEBUG, "LockClipData");
379
380 if (!Stream_CheckAndLogRequiredLengthWLog(cliprdr->log, s, 4))
381 return ERROR_INVALID_DATA;
382
383 lockClipboardData.common.msgType = CB_LOCK_CLIPDATA;
384 lockClipboardData.common.msgFlags = flags;
385 lockClipboardData.common.dataLen = length;
386 Stream_Read_UINT32(s, lockClipboardData.clipDataId); /* clipDataId (4 bytes) */
387 IFCALLRET(context->ServerLockClipboardData, error, context, &lockClipboardData);
388
389 if (error)
390 WLog_Print(cliprdr->log, WLOG_ERROR,
391 "ServerLockClipboardData failed with error %" PRIu32 "!", error);
392
393 return error;
394}
395
401static UINT cliprdr_process_unlock_clipdata(cliprdrPlugin* cliprdr, wStream* s, UINT32 length,
402 UINT16 flags)
403{
404 CLIPRDR_UNLOCK_CLIPBOARD_DATA unlockClipboardData = { 0 };
405 CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
406 UINT error = CHANNEL_RC_OK;
407
408 WINPR_ASSERT(cliprdr);
409 WINPR_ASSERT(s);
410
411 WLog_Print(cliprdr->log, WLOG_DEBUG, "UnlockClipData");
412
413 if ((error = cliprdr_read_unlock_clipdata(s, &unlockClipboardData)))
414 return error;
415
416 unlockClipboardData.common.msgType = CB_UNLOCK_CLIPDATA;
417 unlockClipboardData.common.msgFlags = flags;
418 unlockClipboardData.common.dataLen = length;
419
420 IFCALLRET(context->ServerUnlockClipboardData, error, context, &unlockClipboardData);
421
422 if (error)
423 WLog_Print(cliprdr->log, WLOG_ERROR,
424 "ServerUnlockClipboardData failed with error %" PRIu32 "!", error);
425
426 return error;
427}
428
434static UINT cliprdr_order_recv(LPVOID userdata, wStream* s)
435{
436 cliprdrPlugin* cliprdr = userdata;
437 UINT16 msgType = 0;
438 UINT16 msgFlags = 0;
439 UINT32 dataLen = 0;
440 UINT error = 0;
441
442 WINPR_ASSERT(cliprdr);
443 WINPR_ASSERT(s);
444
445 if (!Stream_CheckAndLogRequiredLengthWLog(cliprdr->log, s, 8))
446 return ERROR_INVALID_DATA;
447
448 Stream_Read_UINT16(s, msgType); /* msgType (2 bytes) */
449 Stream_Read_UINT16(s, msgFlags); /* msgFlags (2 bytes) */
450 Stream_Read_UINT32(s, dataLen); /* dataLen (4 bytes) */
451
452 if (!Stream_CheckAndLogRequiredLengthWLog(cliprdr->log, s, dataLen))
453 return ERROR_INVALID_DATA;
454
455 char buffer1[64] = { 0 };
456 char buffer2[64] = { 0 };
457 WLog_Print(cliprdr->log, WLOG_DEBUG,
458 "msgType: %s (%" PRIu16 "), msgFlags: %s dataLen: %" PRIu32 "",
459 CB_MSG_TYPE_STRING(msgType, buffer1, sizeof(buffer1)), msgType,
460 CB_MSG_FLAGS_STRING(msgFlags, buffer2, sizeof(buffer2)), dataLen);
461
462 switch (msgType)
463 {
464 case CB_CLIP_CAPS:
465 if ((error = cliprdr_process_clip_caps(cliprdr, s, dataLen, msgFlags)))
466 WLog_Print(cliprdr->log, WLOG_ERROR,
467 "cliprdr_process_clip_caps failed with error %" PRIu32 "!", error);
468
469 break;
470
471 case CB_MONITOR_READY:
472 if ((error = cliprdr_process_monitor_ready(cliprdr, s, dataLen, msgFlags)))
473 WLog_Print(cliprdr->log, WLOG_ERROR,
474 "cliprdr_process_monitor_ready failed with error %" PRIu32 "!", error);
475
476 break;
477
478 case CB_FORMAT_LIST:
479 if ((error = cliprdr_process_format_list(cliprdr, s, dataLen, msgFlags)))
480 WLog_Print(cliprdr->log, WLOG_ERROR,
481 "cliprdr_process_format_list failed with error %" PRIu32 "!", error);
482
483 break;
484
485 case CB_FORMAT_LIST_RESPONSE:
486 if ((error = cliprdr_process_format_list_response(cliprdr, s, dataLen, msgFlags)))
487 WLog_Print(cliprdr->log, WLOG_ERROR,
488 "cliprdr_process_format_list_response failed with error %" PRIu32 "!",
489 error);
490
491 break;
492
493 case CB_FORMAT_DATA_REQUEST:
494 if ((error = cliprdr_process_format_data_request(cliprdr, s, dataLen, msgFlags)))
495 WLog_Print(cliprdr->log, WLOG_ERROR,
496 "cliprdr_process_format_data_request failed with error %" PRIu32 "!",
497 error);
498
499 break;
500
501 case CB_FORMAT_DATA_RESPONSE:
502 if ((error = cliprdr_process_format_data_response(cliprdr, s, dataLen, msgFlags)))
503 WLog_Print(cliprdr->log, WLOG_ERROR,
504 "cliprdr_process_format_data_response failed with error %" PRIu32 "!",
505 error);
506
507 break;
508
509 case CB_FILECONTENTS_REQUEST:
510 if ((error = cliprdr_process_filecontents_request(cliprdr, s, dataLen, msgFlags)))
511 WLog_Print(cliprdr->log, WLOG_ERROR,
512 "cliprdr_process_filecontents_request failed with error %" PRIu32 "!",
513 error);
514
515 break;
516
517 case CB_FILECONTENTS_RESPONSE:
518 if ((error = cliprdr_process_filecontents_response(cliprdr, s, dataLen, msgFlags)))
519 WLog_Print(cliprdr->log, WLOG_ERROR,
520 "cliprdr_process_filecontents_response failed with error %" PRIu32 "!",
521 error);
522
523 break;
524
525 case CB_LOCK_CLIPDATA:
526 if ((error = cliprdr_process_lock_clipdata(cliprdr, s, dataLen, msgFlags)))
527 WLog_Print(cliprdr->log, WLOG_ERROR,
528 "cliprdr_process_lock_clipdata failed with error %" PRIu32 "!", error);
529
530 break;
531
532 case CB_UNLOCK_CLIPDATA:
533 if ((error = cliprdr_process_unlock_clipdata(cliprdr, s, dataLen, msgFlags)))
534 WLog_Print(cliprdr->log, WLOG_ERROR,
535 "cliprdr_process_unlock_clipdata failed with error %" PRIu32 "!", error);
536
537 break;
538
539 default:
540 error = CHANNEL_RC_BAD_PROC;
541 WLog_Print(cliprdr->log, WLOG_ERROR, "unknown msgType %" PRIu16 "", msgType);
542 break;
543 }
544
545 Stream_Free(s, TRUE);
546 return error;
547}
548
558static UINT cliprdr_client_capabilities(CliprdrClientContext* context,
559 const CLIPRDR_CAPABILITIES* capabilities)
560{
561 wStream* s = NULL;
562 UINT32 flags = 0;
563 const CLIPRDR_GENERAL_CAPABILITY_SET* generalCapabilitySet = NULL;
564 cliprdrPlugin* cliprdr = NULL;
565
566 WINPR_ASSERT(context);
567
568 cliprdr = (cliprdrPlugin*)context->handle;
569 WINPR_ASSERT(cliprdr);
570
571 s = cliprdr_packet_new(CB_CLIP_CAPS, 0, 4 + CB_CAPSTYPE_GENERAL_LEN);
572
573 if (!s)
574 {
575 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_new failed!");
576 return ERROR_INTERNAL_ERROR;
577 }
578
579 Stream_Write_UINT16(s, 1); /* cCapabilitiesSets */
580 Stream_Write_UINT16(s, 0); /* pad1 */
581 generalCapabilitySet = (const CLIPRDR_GENERAL_CAPABILITY_SET*)capabilities->capabilitySets;
582 Stream_Write_UINT16(s, generalCapabilitySet->capabilitySetType); /* capabilitySetType */
583 Stream_Write_UINT16(s, generalCapabilitySet->capabilitySetLength); /* lengthCapability */
584 Stream_Write_UINT32(s, generalCapabilitySet->version); /* version */
585 flags = generalCapabilitySet->generalFlags;
586
587 /* Client capabilities are sent in response to server capabilities.
588 * -> Do not request features the server does not support.
589 * -> Update clipboard context feature state to what was agreed upon.
590 */
591 if (!cliprdr->useLongFormatNames)
592 flags &= (uint32_t)~CB_USE_LONG_FORMAT_NAMES;
593 if (!cliprdr->streamFileClipEnabled)
594 flags &= (uint32_t)~CB_STREAM_FILECLIP_ENABLED;
595 if (!cliprdr->fileClipNoFilePaths)
596 flags &= (uint32_t)~CB_FILECLIP_NO_FILE_PATHS;
597 if (!cliprdr->canLockClipData)
598 flags &= (uint32_t)~CB_CAN_LOCK_CLIPDATA;
599 if (!cliprdr->hasHugeFileSupport)
600 flags &= (uint32_t)~CB_HUGE_FILE_SUPPORT_ENABLED;
601
602 cliprdr->useLongFormatNames = (flags & CB_USE_LONG_FORMAT_NAMES) ? TRUE : FALSE;
603 cliprdr->streamFileClipEnabled = (flags & CB_STREAM_FILECLIP_ENABLED) ? TRUE : FALSE;
604 cliprdr->fileClipNoFilePaths = (flags & CB_FILECLIP_NO_FILE_PATHS) ? TRUE : FALSE;
605 cliprdr->canLockClipData = (flags & CB_CAN_LOCK_CLIPDATA) ? TRUE : FALSE;
606 cliprdr->hasHugeFileSupport = (flags & CB_HUGE_FILE_SUPPORT_ENABLED) ? TRUE : FALSE;
607
608 Stream_Write_UINT32(s, flags); /* generalFlags */
609 WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientCapabilities");
610
611 cliprdr->initialFormatListSent = FALSE;
612
613 return cliprdr_packet_send(cliprdr, s);
614}
615
621static UINT cliprdr_temp_directory(CliprdrClientContext* context,
622 const CLIPRDR_TEMP_DIRECTORY* tempDirectory)
623{
624 wStream* s = NULL;
625 cliprdrPlugin* cliprdr = NULL;
626
627 WINPR_ASSERT(context);
628 WINPR_ASSERT(tempDirectory);
629
630 cliprdr = (cliprdrPlugin*)context->handle;
631 WINPR_ASSERT(cliprdr);
632
633 const size_t tmpDirCharLen = sizeof(tempDirectory->szTempDir) / sizeof(WCHAR);
634 s = cliprdr_packet_new(CB_TEMP_DIRECTORY, 0, tmpDirCharLen * sizeof(WCHAR));
635
636 if (!s)
637 {
638 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_new failed!");
639 return ERROR_INTERNAL_ERROR;
640 }
641
642 if (Stream_Write_UTF16_String_From_UTF8(s, tmpDirCharLen - 1, tempDirectory->szTempDir,
643 ARRAYSIZE(tempDirectory->szTempDir), TRUE) < 0)
644 {
645 Stream_Free(s, TRUE);
646 return ERROR_INTERNAL_ERROR;
647 }
648 /* Path must be 260 UTF16 characters with '\0' termination.
649 * ensure this here */
650 Stream_Write_UINT16(s, 0);
651
652 WLog_Print(cliprdr->log, WLOG_DEBUG, "TempDirectory: %s", tempDirectory->szTempDir);
653 return cliprdr_packet_send(cliprdr, s);
654}
655
661static UINT cliprdr_client_format_list(CliprdrClientContext* context,
662 const CLIPRDR_FORMAT_LIST* formatList)
663{
664 wStream* s = NULL;
665 cliprdrPlugin* cliprdr = NULL;
666
667 WINPR_ASSERT(context);
668 WINPR_ASSERT(formatList);
669
670 cliprdr = (cliprdrPlugin*)context->handle;
671 WINPR_ASSERT(cliprdr);
672
673 {
674 const UINT32 mask = CB_RESPONSE_OK | CB_RESPONSE_FAIL;
675 if ((formatList->common.msgFlags & mask) != 0)
676 WLog_Print(cliprdr->log, WLOG_WARN,
677 "Sending clipboard request with invalid flags msgFlags = 0x%08" PRIx32
678 ". Correct in your client!",
679 formatList->common.msgFlags & mask);
680 }
681
682 const UINT32 mask =
683 freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask);
684 CLIPRDR_FORMAT_LIST filterList = cliprdr_filter_format_list(
685 formatList, mask, CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES);
686
687 /* Allow initial format list from monitor ready, but ignore later attempts */
688 if ((filterList.numFormats == 0) && cliprdr->initialFormatListSent)
689 {
690 cliprdr_free_format_list(&filterList);
691 return CHANNEL_RC_OK;
692 }
693 cliprdr->initialFormatListSent = TRUE;
694
695 const uint32_t level = WLOG_DEBUG;
696 if (WLog_IsLevelActive(cliprdr->log, level))
697 {
698 WLog_Print(cliprdr->log, level, "ClientFormatList: numFormats: %" PRIu32 "",
699 formatList->numFormats);
700 for (size_t x = 0; x < filterList.numFormats; x++)
701 {
702 const CLIPRDR_FORMAT* format = &filterList.formats[x];
703 WLog_Print(cliprdr->log, level, "[%" PRIu32 "]: id=0x%08" PRIx32 " [%s|%s]", x,
704 format->formatId, ClipboardGetFormatIdString(format->formatId),
705 format->formatName);
706 }
707 }
708
709 s = cliprdr_packet_format_list_new(&filterList, cliprdr->useLongFormatNames, FALSE);
710 cliprdr_free_format_list(&filterList);
711
712 if (!s)
713 {
714 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_format_list_new failed!");
715 return ERROR_INTERNAL_ERROR;
716 }
717
718 return cliprdr_packet_send(cliprdr, s);
719}
720
726static UINT
727cliprdr_client_format_list_response(CliprdrClientContext* context,
728 const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
729{
730 wStream* s = NULL;
731 cliprdrPlugin* cliprdr = NULL;
732
733 WINPR_ASSERT(context);
734 WINPR_ASSERT(formatListResponse);
735
736 cliprdr = (cliprdrPlugin*)context->handle;
737 WINPR_ASSERT(cliprdr);
738
739 s = cliprdr_packet_new(CB_FORMAT_LIST_RESPONSE, formatListResponse->common.msgFlags, 0);
740
741 if (!s)
742 {
743 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_new failed!");
744 return ERROR_INTERNAL_ERROR;
745 }
746
747 WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientFormatListResponse");
748 return cliprdr_packet_send(cliprdr, s);
749}
750
756static UINT cliprdr_client_lock_clipboard_data(CliprdrClientContext* context,
757 const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
758{
759 wStream* s = NULL;
760 cliprdrPlugin* cliprdr = NULL;
761
762 WINPR_ASSERT(context);
763 WINPR_ASSERT(lockClipboardData);
764
765 cliprdr = (cliprdrPlugin*)context->handle;
766 WINPR_ASSERT(cliprdr);
767
768 s = cliprdr_packet_lock_clipdata_new(lockClipboardData);
769
770 if (!s)
771 {
772 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_lock_clipdata_new failed!");
773 return ERROR_INTERNAL_ERROR;
774 }
775
776 WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientLockClipboardData: clipDataId: 0x%08" PRIX32 "",
777 lockClipboardData->clipDataId);
778 return cliprdr_packet_send(cliprdr, s);
779}
780
786static UINT
787cliprdr_client_unlock_clipboard_data(CliprdrClientContext* context,
788 const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
789{
790 wStream* s = NULL;
791 cliprdrPlugin* cliprdr = NULL;
792
793 WINPR_ASSERT(context);
794 WINPR_ASSERT(unlockClipboardData);
795
796 cliprdr = (cliprdrPlugin*)context->handle;
797 WINPR_ASSERT(cliprdr);
798
799 s = cliprdr_packet_unlock_clipdata_new(unlockClipboardData);
800
801 if (!s)
802 {
803 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_unlock_clipdata_new failed!");
804 return ERROR_INTERNAL_ERROR;
805 }
806
807 WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientUnlockClipboardData: clipDataId: 0x%08" PRIX32 "",
808 unlockClipboardData->clipDataId);
809 return cliprdr_packet_send(cliprdr, s);
810}
811
817static UINT cliprdr_client_format_data_request(CliprdrClientContext* context,
818 const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
819{
820 WINPR_ASSERT(context);
821 WINPR_ASSERT(formatDataRequest);
822
823 cliprdrPlugin* cliprdr = (cliprdrPlugin*)context->handle;
824 WINPR_ASSERT(cliprdr);
825
826 const UINT32 mask =
827 freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask);
828 if ((mask & (CLIPRDR_FLAG_REMOTE_TO_LOCAL | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES)) == 0)
829 {
830 WLog_Print(cliprdr->log, WLOG_WARN, "remote -> local copy disabled, ignoring request");
831 return CHANNEL_RC_OK;
832 }
833
834 wStream* s = cliprdr_packet_new(CB_FORMAT_DATA_REQUEST, 0, 4);
835 if (!s)
836 {
837 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_new failed!");
838 return ERROR_INTERNAL_ERROR;
839 }
840
841 Stream_Write_UINT32(s, formatDataRequest->requestedFormatId); /* requestedFormatId (4 bytes) */
842 WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientFormatDataRequest(0x%08" PRIx32 " [%s])",
843 formatDataRequest->requestedFormatId,
844 ClipboardGetFormatIdString(formatDataRequest->requestedFormatId));
845 return cliprdr_packet_send(cliprdr, s);
846}
847
853static UINT
854cliprdr_client_format_data_response(CliprdrClientContext* context,
855 const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
856{
857 WINPR_ASSERT(context);
858 WINPR_ASSERT(formatDataResponse);
859
860 cliprdrPlugin* cliprdr = (cliprdrPlugin*)context->handle;
861 WINPR_ASSERT(cliprdr);
862
863 WINPR_ASSERT(
864 (freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask) &
865 (CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES)) != 0);
866
867 wStream* s = cliprdr_packet_new(CB_FORMAT_DATA_RESPONSE, formatDataResponse->common.msgFlags,
868 formatDataResponse->common.dataLen);
869
870 if (!s)
871 {
872 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_new failed!");
873 return ERROR_INTERNAL_ERROR;
874 }
875
876 Stream_Write(s, formatDataResponse->requestedFormatData, formatDataResponse->common.dataLen);
877 WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientFormatDataResponse");
878 return cliprdr_packet_send(cliprdr, s);
879}
880
886static UINT
887cliprdr_client_file_contents_request(CliprdrClientContext* context,
888 const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
889{
890 wStream* s = NULL;
891
892 WINPR_ASSERT(context);
893 WINPR_ASSERT(fileContentsRequest);
894
895 cliprdrPlugin* cliprdr = (cliprdrPlugin*)context->handle;
896 if (!cliprdr)
897 return ERROR_INTERNAL_ERROR;
898
899 const UINT32 mask =
900 freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask);
901 if ((mask & CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES) == 0)
902 {
903 WLog_Print(cliprdr->log, WLOG_WARN, "remote -> local file copy disabled, ignoring request");
904 return CHANNEL_RC_OK;
905 }
906
907 if (!cliprdr->hasHugeFileSupport)
908 {
909 if (((UINT64)fileContentsRequest->cbRequested + fileContentsRequest->nPositionLow) >
910 UINT32_MAX)
911 return ERROR_INVALID_PARAMETER;
912 if (fileContentsRequest->nPositionHigh != 0)
913 return ERROR_INVALID_PARAMETER;
914 }
915
916 s = cliprdr_packet_file_contents_request_new(fileContentsRequest);
917
918 if (!s)
919 {
920 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_file_contents_request_new failed!");
921 return ERROR_INTERNAL_ERROR;
922 }
923
924 WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientFileContentsRequest: streamId: 0x%08" PRIX32 "",
925 fileContentsRequest->streamId);
926 return cliprdr_packet_send(cliprdr, s);
927}
928
934static UINT
935cliprdr_client_file_contents_response(CliprdrClientContext* context,
936 const CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse)
937{
938 wStream* s = NULL;
939 cliprdrPlugin* cliprdr = NULL;
940
941 WINPR_ASSERT(context);
942 WINPR_ASSERT(fileContentsResponse);
943
944 cliprdr = (cliprdrPlugin*)context->handle;
945 WINPR_ASSERT(cliprdr);
946
947 const UINT32 mask =
948 freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask);
949 if ((mask & CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES) == 0)
950 return cliprdr_send_error_response(cliprdr, CB_FILECONTENTS_RESPONSE);
951
952 s = cliprdr_packet_file_contents_response_new(fileContentsResponse);
953
954 if (!s)
955 {
956 WLog_Print(cliprdr->log, WLOG_ERROR, "cliprdr_packet_file_contents_response_new failed!");
957 return ERROR_INTERNAL_ERROR;
958 }
959
960 WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientFileContentsResponse: streamId: 0x%08" PRIX32 "",
961 fileContentsResponse->streamId);
962 return cliprdr_packet_send(cliprdr, s);
963}
964
965static VOID VCAPITYPE cliprdr_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
966 UINT event, LPVOID pData,
967 UINT32 dataLength, UINT32 totalLength,
968 UINT32 dataFlags)
969{
970 UINT error = CHANNEL_RC_OK;
971 cliprdrPlugin* cliprdr = (cliprdrPlugin*)lpUserParam;
972 WINPR_ASSERT(cliprdr);
973
974 switch (event)
975 {
976 case CHANNEL_EVENT_DATA_RECEIVED:
977 if (cliprdr->OpenHandle != openHandle)
978 {
979 WLog_Print(cliprdr->log, WLOG_ERROR, "error no match");
980 return;
981 }
982 if ((error = channel_client_post_message(cliprdr->MsgsHandle, pData, dataLength,
983 totalLength, dataFlags)))
984 WLog_Print(cliprdr->log, WLOG_ERROR, "failed with error %" PRIu32 "", error);
985
986 break;
987
988 case CHANNEL_EVENT_WRITE_CANCELLED:
989 case CHANNEL_EVENT_WRITE_COMPLETE:
990 {
991 wStream* s = (wStream*)pData;
992 Stream_Free(s, TRUE);
993 }
994 break;
995
996 case CHANNEL_EVENT_USER:
997 break;
998 default:
999 break;
1000 }
1001
1002 if (error && cliprdr->context->rdpcontext)
1003 setChannelError(cliprdr->context->rdpcontext, error,
1004 "cliprdr_virtual_channel_open_event_ex reported an error");
1005}
1006
1012static UINT cliprdr_virtual_channel_event_connected(cliprdrPlugin* cliprdr,
1013 WINPR_ATTR_UNUSED LPVOID pData,
1014 WINPR_ATTR_UNUSED UINT32 dataLength)
1015{
1016 DWORD status = 0;
1017 WINPR_ASSERT(cliprdr);
1018 WINPR_ASSERT(cliprdr->context);
1019
1020 WINPR_ASSERT(cliprdr->channelEntryPoints.pVirtualChannelOpenEx);
1021 status = cliprdr->channelEntryPoints.pVirtualChannelOpenEx(
1022 cliprdr->InitHandle, &cliprdr->OpenHandle, cliprdr->channelDef.name,
1023 cliprdr_virtual_channel_open_event_ex);
1024 if (status != CHANNEL_RC_OK)
1025 return status;
1026
1027 cliprdr->MsgsHandle = channel_client_create_handler(
1028 cliprdr->context->rdpcontext, cliprdr, cliprdr_order_recv, CLIPRDR_SVC_CHANNEL_NAME);
1029 if (!cliprdr->MsgsHandle)
1030 return ERROR_INTERNAL_ERROR;
1031
1032 return status;
1033}
1034
1040static UINT cliprdr_virtual_channel_event_disconnected(cliprdrPlugin* cliprdr)
1041{
1042 UINT rc = 0;
1043
1044 WINPR_ASSERT(cliprdr);
1045
1046 channel_client_quit_handler(cliprdr->MsgsHandle);
1047 cliprdr->MsgsHandle = NULL;
1048
1049 if (cliprdr->OpenHandle == 0)
1050 return CHANNEL_RC_OK;
1051
1052 WINPR_ASSERT(cliprdr->channelEntryPoints.pVirtualChannelCloseEx);
1053 rc = cliprdr->channelEntryPoints.pVirtualChannelCloseEx(cliprdr->InitHandle,
1054 cliprdr->OpenHandle);
1055
1056 if (CHANNEL_RC_OK != rc)
1057 {
1058 WLog_Print(cliprdr->log, WLOG_ERROR, "pVirtualChannelClose failed with %s [%08" PRIX32 "]",
1059 WTSErrorToString(rc), rc);
1060 return rc;
1061 }
1062
1063 cliprdr->OpenHandle = 0;
1064
1065 return CHANNEL_RC_OK;
1066}
1067
1073static UINT cliprdr_virtual_channel_event_terminated(cliprdrPlugin* cliprdr)
1074{
1075 WINPR_ASSERT(cliprdr);
1076
1077 cliprdr->InitHandle = 0;
1078 free(cliprdr->context);
1079 free(cliprdr);
1080 return CHANNEL_RC_OK;
1081}
1082
1083static VOID VCAPITYPE cliprdr_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
1084 UINT event, LPVOID pData,
1085 UINT dataLength)
1086{
1087 UINT error = CHANNEL_RC_OK;
1088 cliprdrPlugin* cliprdr = (cliprdrPlugin*)lpUserParam;
1089 WINPR_ASSERT(cliprdr);
1090
1091 if (cliprdr->InitHandle != pInitHandle)
1092 {
1093 WLog_Print(cliprdr->log, WLOG_ERROR, "error no match");
1094 return;
1095 }
1096
1097 switch (event)
1098 {
1099 case CHANNEL_EVENT_CONNECTED:
1100 if ((error = cliprdr_virtual_channel_event_connected(cliprdr, pData, dataLength)))
1101 WLog_Print(cliprdr->log, WLOG_ERROR,
1102 "cliprdr_virtual_channel_event_connected failed with error %" PRIu32 "!",
1103 error);
1104
1105 break;
1106
1107 case CHANNEL_EVENT_DISCONNECTED:
1108 if ((error = cliprdr_virtual_channel_event_disconnected(cliprdr)))
1109 WLog_Print(cliprdr->log, WLOG_ERROR,
1110 "cliprdr_virtual_channel_event_disconnected failed with error %" PRIu32
1111 "!",
1112 error);
1113
1114 break;
1115
1116 case CHANNEL_EVENT_TERMINATED:
1117 if ((error = cliprdr_virtual_channel_event_terminated(cliprdr)))
1118 WLog_Print(cliprdr->log, WLOG_ERROR,
1119 "cliprdr_virtual_channel_event_terminated failed with error %" PRIu32
1120 "!",
1121 error);
1122 break;
1123 default:
1124 break;
1125 }
1126
1127 if (error && cliprdr->context->rdpcontext)
1128 setChannelError(cliprdr->context->rdpcontext, error,
1129 "cliprdr_virtual_channel_init_event reported an error");
1130}
1131
1132/* cliprdr is always built-in */
1133#define VirtualChannelEntryEx cliprdr_VirtualChannelEntryEx
1134
1135FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
1136 PVOID pInitHandle))
1137{
1138 UINT rc = 0;
1139 CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx = NULL;
1140 cliprdrPlugin* cliprdr = (cliprdrPlugin*)calloc(1, sizeof(cliprdrPlugin));
1141
1142 wLog* log = WLog_Get(CHANNELS_TAG("cliprdr.client"));
1143 WINPR_ASSERT(log);
1144
1145 if (!cliprdr)
1146 {
1147 WLog_Print(log, WLOG_ERROR, "calloc failed!");
1148 return FALSE;
1149 }
1150
1151 cliprdr->log = log;
1152 cliprdr->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
1153 CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL;
1154 (void)sprintf_s(cliprdr->channelDef.name, ARRAYSIZE(cliprdr->channelDef.name),
1155 CLIPRDR_SVC_CHANNEL_NAME);
1156 pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
1157 WINPR_ASSERT(pEntryPointsEx);
1158
1159 if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
1160 (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
1161 {
1162 CliprdrClientContext* context =
1163 (CliprdrClientContext*)calloc(1, sizeof(CliprdrClientContext));
1164
1165 if (!context)
1166 {
1167 WLog_Print(cliprdr->log, WLOG_ERROR, "calloc failed!");
1168 free(cliprdr);
1169 return FALSE;
1170 }
1171
1172 context->handle = (void*)cliprdr;
1173 context->custom = NULL;
1174 context->ClientCapabilities = cliprdr_client_capabilities;
1175 context->TempDirectory = cliprdr_temp_directory;
1176 context->ClientFormatList = cliprdr_client_format_list;
1177 context->ClientFormatListResponse = cliprdr_client_format_list_response;
1178 context->ClientLockClipboardData = cliprdr_client_lock_clipboard_data;
1179 context->ClientUnlockClipboardData = cliprdr_client_unlock_clipboard_data;
1180 context->ClientFormatDataRequest = cliprdr_client_format_data_request;
1181 context->ClientFormatDataResponse = cliprdr_client_format_data_response;
1182 context->ClientFileContentsRequest = cliprdr_client_file_contents_request;
1183 context->ClientFileContentsResponse = cliprdr_client_file_contents_response;
1184 cliprdr->context = context;
1185 context->rdpcontext = pEntryPointsEx->context;
1186 }
1187
1188 WLog_Print(cliprdr->log, WLOG_DEBUG, "VirtualChannelEntryEx");
1189 CopyMemory(&(cliprdr->channelEntryPoints), pEntryPoints,
1191 cliprdr->InitHandle = pInitHandle;
1192 rc = cliprdr->channelEntryPoints.pVirtualChannelInitEx(
1193 cliprdr, cliprdr->context, pInitHandle, &cliprdr->channelDef, 1,
1194 VIRTUAL_CHANNEL_VERSION_WIN2000, cliprdr_virtual_channel_init_event_ex);
1195
1196 if (CHANNEL_RC_OK != rc)
1197 {
1198 WLog_Print(cliprdr->log, WLOG_ERROR, "pVirtualChannelInit failed with %s [%08" PRIX32 "]",
1199 WTSErrorToString(rc), rc);
1200 free(cliprdr->context);
1201 free(cliprdr);
1202 return FALSE;
1203 }
1204
1205 cliprdr->channelEntryPoints.pInterface = cliprdr->context;
1206 return TRUE;
1207}
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
Definition svc.h:60