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