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