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