FreeRDP
wlf_cliprdr.c
1 
21 #include <freerdp/config.h>
22 
23 #include <stdlib.h>
24 
25 #include <winpr/crt.h>
26 #include <winpr/image.h>
27 #include <winpr/stream.h>
28 #include <winpr/clipboard.h>
29 
30 #include <freerdp/log.h>
31 #include <freerdp/client/cliprdr.h>
32 #include <freerdp/channels/channels.h>
33 #include <freerdp/channels/cliprdr.h>
34 
35 #include <freerdp/client/client_cliprdr_file.h>
36 
37 #include "wlf_cliprdr.h"
38 
39 #define TAG CLIENT_TAG("wayland.cliprdr")
40 
41 #define mime_text_plain "text/plain"
42 #define mime_text_utf8 mime_text_plain ";charset=utf-8"
43 
44 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
45 static const char* mime_text[] = { mime_text_plain, mime_text_utf8, "UTF8_STRING",
46  "COMPOUND_TEXT", "TEXT", "STRING" };
47 
48 static const char mime_png[] = "image/png";
49 static const char mime_webp[] = "image/webp";
50 static const char mime_jpg[] = "image/jpeg";
51 static const char mime_tiff[] = "image/tiff";
52 static const char mime_uri_list[] = "text/uri-list";
53 static const char mime_html[] = "text/html";
54 
55 #define BMP_MIME_LIST "image/bmp", "image/x-bmp", "image/x-MS-bmp", "image/x-win-bitmap"
56 static const char* mime_bitmap[] = { BMP_MIME_LIST };
57 static const char* mime_image[] = { mime_png, mime_webp, mime_jpg, mime_tiff, BMP_MIME_LIST };
58 
59 static const char mime_gnome_copied_files[] = "x-special/gnome-copied-files";
60 static const char mime_mate_copied_files[] = "x-special/mate-copied-files";
61 
62 static const char type_FileGroupDescriptorW[] = "FileGroupDescriptorW";
63 static const char type_HtmlFormat[] = "HTML Format";
64 
65 typedef struct
66 {
67  FILE* responseFile;
68  UINT32 responseFormat;
69  char* responseMime;
70 } wlf_request;
71 
72 typedef struct
73 {
74  const FILE* responseFile;
75  UINT32 responseFormat;
76  const char* responseMime;
77 } wlf_const_request;
78 
79 struct wlf_clipboard
80 {
81  wlfContext* wfc;
82  rdpChannels* channels;
83  CliprdrClientContext* context;
84  wLog* log;
85 
86  UwacSeat* seat;
87  wClipboard* system;
88 
89  size_t numClientFormats;
90  CLIPRDR_FORMAT* clientFormats;
91 
92  size_t numServerFormats;
93  CLIPRDR_FORMAT* serverFormats;
94 
95  BOOL sync;
96 
97  CRITICAL_SECTION lock;
98  CliprdrFileContext* file;
99 
100  wQueue* request_queue;
101 };
102 
103 static void wlf_request_free(void* rq)
104 {
105  wlf_request* request = rq;
106  if (request)
107  {
108  free(request->responseMime);
109  if (request->responseFile)
110  (void)fclose(request->responseFile);
111  }
112  free(request);
113 }
114 
115 static wlf_request* wlf_request_new(void)
116 {
117  return calloc(1, sizeof(wlf_request));
118 }
119 
120 static void* wlf_request_clone(const void* oth)
121 {
122  const wlf_request* other = (const wlf_request*)oth;
123  wlf_request* copy = wlf_request_new();
124  if (!copy)
125  return NULL;
126  *copy = *other;
127  if (other->responseMime)
128  {
129  copy->responseMime = _strdup(other->responseMime);
130  if (!copy->responseMime)
131  goto fail;
132  }
133  return copy;
134 fail:
135  wlf_request_free(copy);
136  return NULL;
137 }
138 
139 static BOOL wlf_mime_is_file(const char* mime)
140 {
141  if (strncmp(mime_uri_list, mime, sizeof(mime_uri_list)) == 0)
142  return TRUE;
143  if (strncmp(mime_gnome_copied_files, mime, sizeof(mime_gnome_copied_files)) == 0)
144  return TRUE;
145  if (strncmp(mime_mate_copied_files, mime, sizeof(mime_mate_copied_files)) == 0)
146  return TRUE;
147  return FALSE;
148 }
149 
150 static BOOL wlf_mime_is_text(const char* mime)
151 {
152  for (size_t x = 0; x < ARRAYSIZE(mime_text); x++)
153  {
154  if (strcmp(mime, mime_text[x]) == 0)
155  return TRUE;
156  }
157 
158  return FALSE;
159 }
160 
161 static BOOL wlf_mime_is_image(const char* mime)
162 {
163  for (size_t x = 0; x < ARRAYSIZE(mime_image); x++)
164  {
165  if (strcmp(mime, mime_image[x]) == 0)
166  return TRUE;
167  }
168 
169  return FALSE;
170 }
171 
172 static BOOL wlf_mime_is_html(const char* mime)
173 {
174  if (strcmp(mime, mime_html) == 0)
175  return TRUE;
176 
177  return FALSE;
178 }
179 
180 static void wlf_cliprdr_free_server_formats(wfClipboard* clipboard)
181 {
182  if (clipboard && clipboard->serverFormats)
183  {
184  for (size_t j = 0; j < clipboard->numServerFormats; j++)
185  {
186  CLIPRDR_FORMAT* format = &clipboard->serverFormats[j];
187  free(format->formatName);
188  }
189 
190  free(clipboard->serverFormats);
191  clipboard->serverFormats = NULL;
192  clipboard->numServerFormats = 0;
193  }
194 
195  if (clipboard)
196  UwacClipboardOfferDestroy(clipboard->seat);
197 }
198 
199 static void wlf_cliprdr_free_client_formats(wfClipboard* clipboard)
200 {
201  if (clipboard && clipboard->numClientFormats)
202  {
203  for (size_t j = 0; j < clipboard->numClientFormats; j++)
204  {
205  CLIPRDR_FORMAT* format = &clipboard->clientFormats[j];
206  free(format->formatName);
207  }
208 
209  free(clipboard->clientFormats);
210  clipboard->clientFormats = NULL;
211  clipboard->numClientFormats = 0;
212  }
213 
214  if (clipboard)
215  UwacClipboardOfferDestroy(clipboard->seat);
216 }
217 
223 static UINT wlf_cliprdr_send_client_format_list(wfClipboard* clipboard)
224 {
225  WINPR_ASSERT(clipboard);
226 
227  const CLIPRDR_FORMAT_LIST formatList = { .common.msgFlags = 0,
228  .numFormats = (UINT32)clipboard->numClientFormats,
229  .formats = clipboard->clientFormats,
230  .common.msgType = CB_FORMAT_LIST };
231 
232  cliprdr_file_context_clear(clipboard->file);
233 
234  WLog_VRB(TAG, "-------------- client format list [%" PRIu32 "] ------------------",
235  formatList.numFormats);
236  for (UINT32 x = 0; x < formatList.numFormats; x++)
237  {
238  const CLIPRDR_FORMAT* format = &formatList.formats[x];
239  WLog_VRB(TAG, "client announces %" PRIu32 " [%s][%s]", format->formatId,
240  ClipboardGetFormatIdString(format->formatId), format->formatName);
241  }
242  WINPR_ASSERT(clipboard->context);
243  WINPR_ASSERT(clipboard->context->ClientFormatList);
244  return clipboard->context->ClientFormatList(clipboard->context, &formatList);
245 }
246 
247 static void wfl_cliprdr_add_client_format_id(wfClipboard* clipboard, UINT32 formatId)
248 {
249  CLIPRDR_FORMAT* format = NULL;
250  const char* name = ClipboardGetFormatName(clipboard->system, formatId);
251 
252  for (size_t x = 0; x < clipboard->numClientFormats; x++)
253  {
254  format = &clipboard->clientFormats[x];
255 
256  if (format->formatId == formatId)
257  return;
258  }
259 
260  format = realloc(clipboard->clientFormats,
261  (clipboard->numClientFormats + 1) * sizeof(CLIPRDR_FORMAT));
262 
263  if (!format)
264  return;
265 
266  clipboard->clientFormats = format;
267  format = &clipboard->clientFormats[clipboard->numClientFormats++];
268  format->formatId = formatId;
269  format->formatName = NULL;
270 
271  if (name && (formatId >= CF_MAX))
272  format->formatName = _strdup(name);
273 }
274 
275 static BOOL wlf_cliprdr_add_client_format(wfClipboard* clipboard, const char* mime)
276 {
277  WINPR_ASSERT(mime);
278  ClipboardLock(clipboard->system);
279  if (wlf_mime_is_html(mime))
280  {
281  UINT32 formatId = ClipboardGetFormatId(clipboard->system, type_HtmlFormat);
282  wfl_cliprdr_add_client_format_id(clipboard, formatId);
283  }
284  else if (wlf_mime_is_text(mime))
285  {
286  wfl_cliprdr_add_client_format_id(clipboard, CF_TEXT);
287  wfl_cliprdr_add_client_format_id(clipboard, CF_OEMTEXT);
288  wfl_cliprdr_add_client_format_id(clipboard, CF_UNICODETEXT);
289  }
290  else if (wlf_mime_is_image(mime))
291  {
292  for (size_t x = 0; x < ARRAYSIZE(mime_image); x++)
293  {
294  const char* mime_bmp = mime_image[x];
295  UINT32 formatId = ClipboardGetFormatId(clipboard->system, mime_bmp);
296  if (formatId != 0)
297  wfl_cliprdr_add_client_format_id(clipboard, formatId);
298  }
299  wfl_cliprdr_add_client_format_id(clipboard, CF_DIB);
300  wfl_cliprdr_add_client_format_id(clipboard, CF_TIFF);
301  }
302  else if (wlf_mime_is_file(mime))
303  {
304  const UINT32 fileFormatId =
305  ClipboardGetFormatId(clipboard->system, type_FileGroupDescriptorW);
306  wfl_cliprdr_add_client_format_id(clipboard, fileFormatId);
307  }
308 
309  ClipboardUnlock(clipboard->system);
310  if (wlf_cliprdr_send_client_format_list(clipboard) != CHANNEL_RC_OK)
311  return FALSE;
312  return TRUE;
313 }
314 
320 static UINT wlf_cliprdr_send_data_request(wfClipboard* clipboard, const wlf_const_request* rq)
321 {
322  WINPR_ASSERT(rq);
323 
324  CLIPRDR_FORMAT_DATA_REQUEST request = { .requestedFormatId = rq->responseFormat };
325 
326  if (!Queue_Enqueue(clipboard->request_queue, rq))
327  return ERROR_INTERNAL_ERROR;
328 
329  WINPR_ASSERT(clipboard);
330  WINPR_ASSERT(clipboard->context);
331  WINPR_ASSERT(clipboard->context->ClientFormatDataRequest);
332  return clipboard->context->ClientFormatDataRequest(clipboard->context, &request);
333 }
334 
340 static UINT wlf_cliprdr_send_data_response(wfClipboard* clipboard, const BYTE* data, size_t size)
341 {
342  CLIPRDR_FORMAT_DATA_RESPONSE response = { 0 };
343 
344  if (size > UINT32_MAX)
345  return ERROR_INVALID_PARAMETER;
346 
347  response.common.msgFlags = (data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
348  response.common.dataLen = (UINT32)size;
349  response.requestedFormatData = data;
350 
351  WINPR_ASSERT(clipboard);
352  WINPR_ASSERT(clipboard->context);
353  WINPR_ASSERT(clipboard->context->ClientFormatDataResponse);
354  return clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
355 }
356 
357 BOOL wlf_cliprdr_handle_event(wfClipboard* clipboard, const UwacClipboardEvent* event)
358 {
359  if (!clipboard || !event)
360  return FALSE;
361 
362  if (!clipboard->context)
363  return TRUE;
364 
365  switch (event->type)
366  {
367  case UWAC_EVENT_CLIPBOARD_AVAILABLE:
368  clipboard->seat = event->seat;
369  return TRUE;
370 
371  case UWAC_EVENT_CLIPBOARD_OFFER:
372  WLog_Print(clipboard->log, WLOG_DEBUG, "client announces mime %s", event->mime);
373  return wlf_cliprdr_add_client_format(clipboard, event->mime);
374 
375  case UWAC_EVENT_CLIPBOARD_SELECT:
376  WLog_Print(clipboard->log, WLOG_DEBUG, "client announces new data");
377  wlf_cliprdr_free_client_formats(clipboard);
378  return TRUE;
379 
380  default:
381  return FALSE;
382  }
383 }
384 
390 static UINT wlf_cliprdr_send_client_capabilities(wfClipboard* clipboard)
391 {
392  WINPR_ASSERT(clipboard);
393 
394  CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet = {
395  .capabilitySetType = CB_CAPSTYPE_GENERAL,
396  .capabilitySetLength = 12,
397  .version = CB_CAPS_VERSION_2,
398  .generalFlags =
399  CB_USE_LONG_FORMAT_NAMES | cliprdr_file_context_current_flags(clipboard->file)
400  };
401  CLIPRDR_CAPABILITIES capabilities = { .cCapabilitiesSets = 1,
402  .capabilitySets =
403  (CLIPRDR_CAPABILITY_SET*)&(generalCapabilitySet) };
404 
405  WINPR_ASSERT(clipboard);
406  WINPR_ASSERT(clipboard->context);
407  WINPR_ASSERT(clipboard->context->ClientCapabilities);
408  return clipboard->context->ClientCapabilities(clipboard->context, &capabilities);
409 }
410 
416 static UINT wlf_cliprdr_send_client_format_list_response(wfClipboard* clipboard, BOOL status)
417 {
418  const CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse = {
419  .common.msgType = CB_FORMAT_LIST_RESPONSE,
420  .common.msgFlags = status ? CB_RESPONSE_OK : CB_RESPONSE_FAIL,
421  .common.dataLen = 0
422  };
423  WINPR_ASSERT(clipboard);
424  WINPR_ASSERT(clipboard->context);
425  WINPR_ASSERT(clipboard->context->ClientFormatListResponse);
426  return clipboard->context->ClientFormatListResponse(clipboard->context, &formatListResponse);
427 }
428 
434 static UINT wlf_cliprdr_monitor_ready(CliprdrClientContext* context,
435  const CLIPRDR_MONITOR_READY* monitorReady)
436 {
437  UINT ret = 0;
438 
439  WINPR_UNUSED(monitorReady);
440  WINPR_ASSERT(context);
441  WINPR_ASSERT(monitorReady);
442 
443  wfClipboard* clipboard = cliprdr_file_context_get_context(context->custom);
444  WINPR_ASSERT(clipboard);
445 
446  if ((ret = wlf_cliprdr_send_client_capabilities(clipboard)) != CHANNEL_RC_OK)
447  return ret;
448 
449  if ((ret = wlf_cliprdr_send_client_format_list(clipboard)) != CHANNEL_RC_OK)
450  return ret;
451 
452  clipboard->sync = TRUE;
453  return CHANNEL_RC_OK;
454 }
455 
461 static UINT wlf_cliprdr_server_capabilities(CliprdrClientContext* context,
462  const CLIPRDR_CAPABILITIES* capabilities)
463 {
464  WINPR_ASSERT(context);
465  WINPR_ASSERT(capabilities);
466 
467  const BYTE* capsPtr = (const BYTE*)capabilities->capabilitySets;
468  WINPR_ASSERT(capsPtr);
469 
470  wfClipboard* clipboard = cliprdr_file_context_get_context(context->custom);
471  WINPR_ASSERT(clipboard);
472 
473  if (!cliprdr_file_context_remote_set_flags(clipboard->file, 0))
474  return ERROR_INTERNAL_ERROR;
475 
476  for (UINT32 i = 0; i < capabilities->cCapabilitiesSets; i++)
477  {
478  const CLIPRDR_CAPABILITY_SET* caps = (const CLIPRDR_CAPABILITY_SET*)capsPtr;
479 
480  if (caps->capabilitySetType == CB_CAPSTYPE_GENERAL)
481  {
482  const CLIPRDR_GENERAL_CAPABILITY_SET* generalCaps =
483  (const CLIPRDR_GENERAL_CAPABILITY_SET*)caps;
484 
485  if (!cliprdr_file_context_remote_set_flags(clipboard->file, generalCaps->generalFlags))
486  return ERROR_INTERNAL_ERROR;
487  }
488 
489  capsPtr += caps->capabilitySetLength;
490  }
491 
492  return CHANNEL_RC_OK;
493 }
494 
495 static UINT32 wlf_get_server_format_id(const wfClipboard* clipboard, const char* name)
496 {
497  WINPR_ASSERT(clipboard);
498  WINPR_ASSERT(name);
499 
500  for (UINT32 x = 0; x < clipboard->numServerFormats; x++)
501  {
502  const CLIPRDR_FORMAT* format = &clipboard->serverFormats[x];
503  if (!format->formatName)
504  continue;
505  if (strcmp(name, format->formatName) == 0)
506  return format->formatId;
507  }
508  return 0;
509 }
510 
511 static const char* wlf_get_server_format_name(const wfClipboard* clipboard, UINT32 formatId)
512 {
513  WINPR_ASSERT(clipboard);
514 
515  for (UINT32 x = 0; x < clipboard->numServerFormats; x++)
516  {
517  const CLIPRDR_FORMAT* format = &clipboard->serverFormats[x];
518  if (format->formatId == formatId)
519  return format->formatName;
520  }
521  return NULL;
522 }
523 
524 static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char* mime, int fd)
525 {
526  wfClipboard* clipboard = (wfClipboard*)context;
527  WINPR_UNUSED(seat);
528 
529  EnterCriticalSection(&clipboard->lock);
530 
531  wlf_const_request request = { 0 };
532  if (wlf_mime_is_html(mime))
533  {
534  request.responseMime = mime_html;
535  request.responseFormat = wlf_get_server_format_id(clipboard, type_HtmlFormat);
536  }
537  else if (wlf_mime_is_file(mime))
538  {
539  request.responseMime = mime;
540  request.responseFormat = wlf_get_server_format_id(clipboard, type_FileGroupDescriptorW);
541  }
542  else if (wlf_mime_is_text(mime))
543  {
544  request.responseMime = mime_text_plain;
545  request.responseFormat = CF_UNICODETEXT;
546  }
547  else if (wlf_mime_is_image(mime))
548  {
549  request.responseMime = mime;
550  if (strcmp(mime, mime_tiff) == 0)
551  request.responseFormat = CF_TIFF;
552  else
553  request.responseFormat = CF_DIB;
554  }
555 
556  if (request.responseMime != NULL)
557  {
558  request.responseFile = fdopen(fd, "w");
559 
560  if (request.responseFile)
561  wlf_cliprdr_send_data_request(clipboard, &request);
562  else
563  WLog_Print(clipboard->log, WLOG_ERROR,
564  "failed to open clipboard file descriptor for MIME %s",
565  request.responseMime);
566  }
567 
568  LeaveCriticalSection(&clipboard->lock);
569 }
570 
571 static void wlf_cliprdr_cancel_data(UwacSeat* seat, void* context)
572 {
573  wfClipboard* clipboard = (wfClipboard*)context;
574 
575  WINPR_UNUSED(seat);
576  WINPR_ASSERT(clipboard);
577  cliprdr_file_context_clear(clipboard->file);
578 }
579 
588 static UINT wlf_cliprdr_server_format_list(CliprdrClientContext* context,
589  const CLIPRDR_FORMAT_LIST* formatList)
590 {
591  BOOL html = FALSE;
592  BOOL text = FALSE;
593  BOOL image = FALSE;
594  BOOL file = FALSE;
595 
596  if (!context || !context->custom)
597  return ERROR_INVALID_PARAMETER;
598 
599  wfClipboard* clipboard = cliprdr_file_context_get_context(context->custom);
600  WINPR_ASSERT(clipboard);
601 
602  wlf_cliprdr_free_server_formats(clipboard);
603  cliprdr_file_context_clear(clipboard->file);
604 
605  if (!(clipboard->serverFormats =
606  (CLIPRDR_FORMAT*)calloc(formatList->numFormats, sizeof(CLIPRDR_FORMAT))))
607  {
608  WLog_Print(clipboard->log, WLOG_ERROR,
609  "failed to allocate %" PRIuz " CLIPRDR_FORMAT structs",
610  clipboard->numServerFormats);
611  return CHANNEL_RC_NO_MEMORY;
612  }
613 
614  clipboard->numServerFormats = formatList->numFormats;
615 
616  if (!clipboard->seat)
617  {
618  WLog_Print(clipboard->log, WLOG_ERROR,
619  "clipboard->seat=NULL, check your client implementation");
620  return ERROR_INTERNAL_ERROR;
621  }
622 
623  for (UINT32 i = 0; i < formatList->numFormats; i++)
624  {
625  const CLIPRDR_FORMAT* format = &formatList->formats[i];
626  CLIPRDR_FORMAT* srvFormat = &clipboard->serverFormats[i];
627  srvFormat->formatId = format->formatId;
628 
629  if (format->formatName)
630  {
631  srvFormat->formatName = _strdup(format->formatName);
632 
633  if (!srvFormat->formatName)
634  {
635  wlf_cliprdr_free_server_formats(clipboard);
636  return CHANNEL_RC_NO_MEMORY;
637  }
638  }
639 
640  if (format->formatName)
641  {
642  if (strcmp(format->formatName, type_HtmlFormat) == 0)
643  {
644  text = TRUE;
645  html = TRUE;
646  }
647  else if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0)
648  {
649  file = TRUE;
650  text = TRUE;
651  }
652  }
653  else
654  {
655  switch (format->formatId)
656  {
657  case CF_TEXT:
658  case CF_OEMTEXT:
659  case CF_UNICODETEXT:
660  text = TRUE;
661  break;
662 
663  case CF_DIB:
664  image = TRUE;
665  break;
666 
667  default:
668  break;
669  }
670  }
671  }
672 
673  if (html)
674  {
675  UwacClipboardOfferCreate(clipboard->seat, mime_html);
676  }
677 
678  if (file && cliprdr_file_context_has_local_support(clipboard->file))
679  {
680  UwacClipboardOfferCreate(clipboard->seat, mime_uri_list);
681  UwacClipboardOfferCreate(clipboard->seat, mime_gnome_copied_files);
682  UwacClipboardOfferCreate(clipboard->seat, mime_mate_copied_files);
683  }
684 
685  if (text)
686  {
687  for (size_t x = 0; x < ARRAYSIZE(mime_text); x++)
688  UwacClipboardOfferCreate(clipboard->seat, mime_text[x]);
689  }
690 
691  if (image)
692  {
693  for (size_t x = 0; x < ARRAYSIZE(mime_image); x++)
694  UwacClipboardOfferCreate(clipboard->seat, mime_image[x]);
695  }
696 
697  UwacClipboardOfferAnnounce(clipboard->seat, clipboard, wlf_cliprdr_transfer_data,
698  wlf_cliprdr_cancel_data);
699  return wlf_cliprdr_send_client_format_list_response(clipboard, TRUE);
700 }
701 
707 static UINT
708 wlf_cliprdr_server_format_list_response(CliprdrClientContext* context,
709  const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
710 {
711  WINPR_ASSERT(context);
712  WINPR_ASSERT(formatListResponse);
713 
714  if (formatListResponse->common.msgFlags & CB_RESPONSE_FAIL)
715  WLog_WARN(TAG, "format list update failed");
716  return CHANNEL_RC_OK;
717 }
718 
724 static UINT
725 wlf_cliprdr_server_format_data_request(CliprdrClientContext* context,
726  const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
727 {
728  UINT rc = CHANNEL_RC_OK;
729  char* data = NULL;
730  size_t size = 0;
731  const char* mime = NULL;
732  UINT32 formatId = 0;
733  UINT32 localFormatId = 0;
734  wfClipboard* clipboard = 0;
735 
736  UINT32 dsize = 0;
737  BYTE* ddata = NULL;
738 
739  WINPR_ASSERT(context);
740  WINPR_ASSERT(formatDataRequest);
741 
742  localFormatId = formatId = formatDataRequest->requestedFormatId;
743  clipboard = cliprdr_file_context_get_context(context->custom);
744  WINPR_ASSERT(clipboard);
745 
746  ClipboardLock(clipboard->system);
747  const UINT32 fileFormatId = ClipboardGetFormatId(clipboard->system, type_FileGroupDescriptorW);
748  const UINT32 htmlFormatId = ClipboardGetFormatId(clipboard->system, type_HtmlFormat);
749 
750  switch (formatId)
751  {
752  case CF_TEXT:
753  case CF_OEMTEXT:
754  case CF_UNICODETEXT:
755  localFormatId = ClipboardGetFormatId(clipboard->system, mime_text_plain);
756  mime = mime_text_utf8;
757  break;
758 
759  case CF_DIB:
760  case CF_DIBV5:
761  mime = mime_bitmap[0];
762  break;
763 
764  case CF_TIFF:
765  mime = mime_tiff;
766  break;
767 
768  default:
769  if (formatId == fileFormatId)
770  {
771  localFormatId = ClipboardGetFormatId(clipboard->system, mime_uri_list);
772  mime = mime_uri_list;
773  }
774  else if (formatId == htmlFormatId)
775  {
776  localFormatId = ClipboardGetFormatId(clipboard->system, mime_html);
777  mime = mime_html;
778  }
779  else
780  goto fail;
781  break;
782  }
783 
784  data = UwacClipboardDataGet(clipboard->seat, mime, &size);
785 
786  if (!data || (size > UINT32_MAX))
787  goto fail;
788 
789  if (fileFormatId == formatId)
790  {
791  if (!cliprdr_file_context_update_client_data(clipboard->file, data, size))
792  goto fail;
793  }
794 
795  const BOOL res = ClipboardSetData(clipboard->system, localFormatId, data, (UINT32)size);
796  free(data);
797 
798  UINT32 len = 0;
799  data = NULL;
800  if (res)
801  data = ClipboardGetData(clipboard->system, formatId, &len);
802 
803  if (!res || !data)
804  goto fail;
805 
806  if (fileFormatId == formatId)
807  {
808  const UINT32 flags = cliprdr_file_context_remote_get_flags(clipboard->file);
809  const UINT32 error = cliprdr_serialize_file_list_ex(
810  flags, (const FILEDESCRIPTORW*)data, len / sizeof(FILEDESCRIPTORW), &ddata, &dsize);
811  if (error)
812  goto fail;
813  }
814 fail:
815  ClipboardUnlock(clipboard->system);
816  rc = wlf_cliprdr_send_data_response(clipboard, ddata, dsize);
817  free(data);
818  return rc;
819 }
820 
826 static UINT
827 wlf_cliprdr_server_format_data_response(CliprdrClientContext* context,
828  const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
829 {
830  UINT rc = ERROR_INTERNAL_ERROR;
831 
832  WINPR_ASSERT(context);
833  WINPR_ASSERT(formatDataResponse);
834 
835  const UINT32 size = formatDataResponse->common.dataLen;
836  const BYTE* data = formatDataResponse->requestedFormatData;
837 
838  wfClipboard* clipboard = cliprdr_file_context_get_context(context->custom);
839  WINPR_ASSERT(clipboard);
840 
841  wlf_request* request = Queue_Dequeue(clipboard->request_queue);
842  if (!request)
843  goto fail;
844 
845  rc = CHANNEL_RC_OK;
846  if (formatDataResponse->common.msgFlags & CB_RESPONSE_FAIL)
847  {
848  WLog_WARN(TAG, "clipboard data request for format %" PRIu32 " [%s], mime %s failed",
849  request->responseFormat, ClipboardGetFormatIdString(request->responseFormat),
850  request->responseMime);
851  goto fail;
852  }
853  rc = ERROR_INTERNAL_ERROR;
854 
855  ClipboardLock(clipboard->system);
856  EnterCriticalSection(&clipboard->lock);
857 
858  BYTE* cdata = NULL;
859  UINT32 srcFormatId = 0;
860  UINT32 dstFormatId = 0;
861  switch (request->responseFormat)
862  {
863  case CF_TEXT:
864  case CF_OEMTEXT:
865  case CF_UNICODETEXT:
866  srcFormatId = request->responseFormat;
867  dstFormatId = ClipboardGetFormatId(clipboard->system, request->responseMime);
868  break;
869 
870  case CF_DIB:
871  case CF_DIBV5:
872  srcFormatId = request->responseFormat;
873  dstFormatId = ClipboardGetFormatId(clipboard->system, request->responseMime);
874  break;
875 
876  default:
877  {
878  const char* name = wlf_get_server_format_name(clipboard, request->responseFormat);
879  if (name)
880  {
881  if (strcmp(type_FileGroupDescriptorW, name) == 0)
882  {
883  srcFormatId =
884  ClipboardGetFormatId(clipboard->system, type_FileGroupDescriptorW);
885  dstFormatId = ClipboardGetFormatId(clipboard->system, request->responseMime);
886 
887  if (!cliprdr_file_context_update_server_data(clipboard->file, clipboard->system,
888  data, size))
889  goto unlock;
890  }
891  else if (strcmp(type_HtmlFormat, name) == 0)
892  {
893  srcFormatId = ClipboardGetFormatId(clipboard->system, type_HtmlFormat);
894  dstFormatId = ClipboardGetFormatId(clipboard->system, request->responseMime);
895  }
896  }
897  }
898  break;
899  }
900 
901  UINT32 len = 0;
902 
903  const BOOL sres = ClipboardSetData(clipboard->system, srcFormatId, data, size);
904  if (sres)
905  cdata = ClipboardGetData(clipboard->system, dstFormatId, &len);
906 
907  if (!sres || !cdata)
908  goto unlock;
909 
910  if (request->responseFile)
911  {
912  const size_t res = fwrite(cdata, 1, len, request->responseFile);
913  if (res == len)
914  rc = CHANNEL_RC_OK;
915  }
916  else
917  rc = CHANNEL_RC_OK;
918 
919 unlock:
920  free(cdata);
921  ClipboardUnlock(clipboard->system);
922  LeaveCriticalSection(&clipboard->lock);
923 fail:
924  wlf_request_free(request);
925  return rc;
926 }
927 
928 wfClipboard* wlf_clipboard_new(wlfContext* wfc)
929 {
930  rdpChannels* channels = NULL;
931  wfClipboard* clipboard = NULL;
932 
933  WINPR_ASSERT(wfc);
934 
935  clipboard = (wfClipboard*)calloc(1, sizeof(wfClipboard));
936 
937  if (!clipboard)
938  goto fail;
939 
940  InitializeCriticalSection(&clipboard->lock);
941  clipboard->wfc = wfc;
942  channels = wfc->common.context.channels;
943  clipboard->log = WLog_Get(TAG);
944  clipboard->channels = channels;
945  clipboard->system = ClipboardCreate();
946  if (!clipboard->system)
947  goto fail;
948 
949  clipboard->file = cliprdr_file_context_new(clipboard);
950  if (!clipboard->file)
951  goto fail;
952 
953  if (!cliprdr_file_context_set_locally_available(clipboard->file, TRUE))
954  goto fail;
955 
956  clipboard->request_queue = Queue_New(TRUE, -1, -1);
957  if (!clipboard->request_queue)
958  goto fail;
959 
960  wObject* obj = Queue_Object(clipboard->request_queue);
961  WINPR_ASSERT(obj);
962  obj->fnObjectFree = wlf_request_free;
963  obj->fnObjectNew = wlf_request_clone;
964 
965  return clipboard;
966 
967 fail:
968  wlf_clipboard_free(clipboard);
969  return NULL;
970 }
971 
972 void wlf_clipboard_free(wfClipboard* clipboard)
973 {
974  if (!clipboard)
975  return;
976 
977  cliprdr_file_context_free(clipboard->file);
978 
979  wlf_cliprdr_free_server_formats(clipboard);
980  wlf_cliprdr_free_client_formats(clipboard);
981  ClipboardDestroy(clipboard->system);
982 
983  EnterCriticalSection(&clipboard->lock);
984 
985  Queue_Free(clipboard->request_queue);
986  LeaveCriticalSection(&clipboard->lock);
987  DeleteCriticalSection(&clipboard->lock);
988  free(clipboard);
989 }
990 
991 BOOL wlf_cliprdr_init(wfClipboard* clipboard, CliprdrClientContext* cliprdr)
992 {
993  WINPR_ASSERT(clipboard);
994  WINPR_ASSERT(cliprdr);
995 
996  clipboard->context = cliprdr;
997  cliprdr->MonitorReady = wlf_cliprdr_monitor_ready;
998  cliprdr->ServerCapabilities = wlf_cliprdr_server_capabilities;
999  cliprdr->ServerFormatList = wlf_cliprdr_server_format_list;
1000  cliprdr->ServerFormatListResponse = wlf_cliprdr_server_format_list_response;
1001  cliprdr->ServerFormatDataRequest = wlf_cliprdr_server_format_data_request;
1002  cliprdr->ServerFormatDataResponse = wlf_cliprdr_server_format_data_response;
1003 
1004  return cliprdr_file_context_init(clipboard->file, cliprdr);
1005 }
1006 
1007 BOOL wlf_cliprdr_uninit(wfClipboard* clipboard, CliprdrClientContext* cliprdr)
1008 {
1009  WINPR_ASSERT(clipboard);
1010  if (!cliprdr_file_context_uninit(clipboard->file, cliprdr))
1011  return FALSE;
1012 
1013  if (cliprdr)
1014  cliprdr->custom = NULL;
1015 
1016  return TRUE;
1017 }
This struct contains function pointer to initialize/free objects.
Definition: collections.h:57