FreeRDP
xf_cliprdr.c
1 
22 #include <freerdp/config.h>
23 
24 #include <stdlib.h>
25 #include <errno.h>
26 
27 #include <X11/Xlib.h>
28 #include <X11/Xatom.h>
29 
30 #ifdef WITH_XFIXES
31 #include <X11/extensions/Xfixes.h>
32 #endif
33 
34 #include <winpr/crt.h>
35 #include <winpr/assert.h>
36 #include <winpr/image.h>
37 #include <winpr/stream.h>
38 #include <winpr/clipboard.h>
39 #include <winpr/path.h>
40 
41 #include <freerdp/utils/signal.h>
42 #include <freerdp/log.h>
43 #include <freerdp/client/cliprdr.h>
44 #include <freerdp/channels/channels.h>
45 #include <freerdp/channels/cliprdr.h>
46 
47 #include <freerdp/client/client_cliprdr_file.h>
48 
49 #include "xf_cliprdr.h"
50 #include "xf_event.h"
51 #include "xf_utils.h"
52 
53 #define TAG CLIENT_TAG("x11.cliprdr")
54 
55 #define MAX_CLIPBOARD_FORMATS 255
56 
57 #ifdef WITH_DEBUG_CLIPRDR
58 #define DEBUG_CLIPRDR(...) WLog_DBG(TAG, __VA_ARGS__)
59 #else
60 #define DEBUG_CLIPRDR(...) \
61  do \
62  { \
63  } while (0)
64 #endif
65 
66 typedef struct
67 {
68  Atom atom;
69  UINT32 formatToRequest;
70  UINT32 localFormat;
71  char* formatName;
72 } xfCliprdrFormat;
73 
74 typedef struct
75 {
76  BYTE* data;
77  UINT32 data_length;
78 } xfCachedData;
79 
80 typedef struct
81 {
82  UINT32 localFormat;
83  UINT32 formatToRequest;
84  char* formatName;
85 } RequestedFormat;
86 
87 struct xf_clipboard
88 {
89  xfContext* xfc;
90  rdpChannels* channels;
91  CliprdrClientContext* context;
92 
93  wClipboard* system;
94 
95  Window root_window;
96  Atom clipboard_atom;
97  Atom property_atom;
98 
99  Atom timestamp_property_atom;
100  Time selection_ownership_timestamp;
101 
102  Atom raw_transfer_atom;
103  Atom raw_format_list_atom;
104 
105  UINT32 numClientFormats;
106  xfCliprdrFormat clientFormats[20];
107 
108  UINT32 numServerFormats;
109  CLIPRDR_FORMAT* serverFormats;
110 
111  size_t numTargets;
112  Atom targets[20];
113 
114  int requestedFormatId;
115 
116  wHashTable* cachedData;
117  wHashTable* cachedRawData;
118 
119  BOOL data_raw_format;
120 
121  RequestedFormat* requestedFormat;
122 
123  XSelectionEvent* respond;
124 
125  Window owner;
126  BOOL sync;
127 
128  /* INCR mechanism */
129  Atom incr_atom;
130  BOOL incr_starts;
131  BYTE* incr_data;
132  int incr_data_length;
133  long event_mask;
134 
135  /* XFixes extension */
136  int xfixes_event_base;
137  int xfixes_error_base;
138  BOOL xfixes_supported;
139 
140  /* last sent data */
141  CLIPRDR_FORMAT* lastSentFormats;
142  UINT32 lastSentNumFormats;
143  CliprdrFileContext* file;
144 };
145 
146 static const char mime_text_plain[] = "text/plain";
147 static const char mime_uri_list[] = "text/uri-list";
148 static const char mime_html[] = "text/html";
149 static const char* mime_bitmap[] = { "image/bmp", "image/x-bmp", "image/x-MS-bmp",
150  "image/x-win-bitmap" };
151 static const char mime_webp[] = "image/webp";
152 static const char mime_png[] = "image/png";
153 static const char mime_jpeg[] = "image/jpeg";
154 static const char mime_tiff[] = "image/tiff";
155 static const char* mime_images[] = { mime_webp, mime_png, mime_jpeg, mime_tiff };
156 
157 static const char mime_gnome_copied_files[] = "x-special/gnome-copied-files";
158 static const char mime_mate_copied_files[] = "x-special/mate-copied-files";
159 
160 static const char type_FileGroupDescriptorW[] = "FileGroupDescriptorW";
161 static const char type_HtmlFormat[] = "HTML Format";
162 
163 static void xf_cliprdr_clear_cached_data(xfClipboard* clipboard);
164 static UINT xf_cliprdr_send_client_format_list(xfClipboard* clipboard, BOOL force);
165 static void xf_cliprdr_set_selection_owner(xfContext* xfc, xfClipboard* clipboard, Time timestamp);
166 
167 static void requested_format_free(RequestedFormat** ppRequestedFormat)
168 {
169  if (!ppRequestedFormat)
170  return;
171  if (!(*ppRequestedFormat))
172  return;
173 
174  free((*ppRequestedFormat)->formatName);
175  free(*ppRequestedFormat);
176  *ppRequestedFormat = NULL;
177 }
178 
179 static BOOL requested_format_replace(RequestedFormat** ppRequestedFormat, UINT32 formatId,
180  const char* formatName)
181 {
182  if (!ppRequestedFormat)
183  return FALSE;
184 
185  requested_format_free(ppRequestedFormat);
186  RequestedFormat* requested = calloc(1, sizeof(RequestedFormat));
187  if (!requested)
188  return FALSE;
189  requested->localFormat = formatId;
190  requested->formatToRequest = formatId;
191  if (formatName)
192  {
193  requested->formatName = _strdup(formatName);
194  if (!requested->formatName)
195  {
196  free(requested);
197  return FALSE;
198  }
199  }
200 
201  *ppRequestedFormat = requested;
202  return TRUE;
203 }
204 
205 static BOOL requested_format_matches(const RequestedFormat* pRequestedFormat, UINT32 formatId,
206  const char* formatName)
207 {
208  if (!pRequestedFormat)
209  return FALSE;
210  if (pRequestedFormat->formatToRequest != formatId)
211  return FALSE;
212  if (formatName || pRequestedFormat->formatName)
213  {
214  if (!formatName || !pRequestedFormat->formatName)
215  return FALSE;
216  if (strcmp(formatName, pRequestedFormat->formatName) != 0)
217  return FALSE;
218  }
219  return TRUE;
220 }
221 
222 static void xf_cached_data_free(void* ptr)
223 {
224  xfCachedData* cached_data = ptr;
225  if (!cached_data)
226  return;
227 
228  free(cached_data->data);
229  free(cached_data);
230 }
231 
232 static xfCachedData* xf_cached_data_new(BYTE* data, size_t data_length)
233 {
234  if (data_length > UINT32_MAX)
235  return NULL;
236 
237  xfCachedData* cached_data = calloc(1, sizeof(xfCachedData));
238  if (!cached_data)
239  return NULL;
240 
241  cached_data->data = data;
242  cached_data->data_length = (UINT32)data_length;
243 
244  return cached_data;
245 }
246 
247 static xfCachedData* xf_cached_data_new_copy(const BYTE* data, size_t data_length)
248 {
249  BYTE* copy = NULL;
250  if (data_length > 0)
251  {
252  copy = malloc(data_length);
253  if (!copy)
254  return NULL;
255  memcpy(copy, data, data_length);
256  }
257 
258  xfCachedData* cache = xf_cached_data_new(copy, data_length);
259  if (!cache)
260  free(copy);
261  return cache;
262 }
263 
264 static void xf_clipboard_free_server_formats(xfClipboard* clipboard)
265 {
266  WINPR_ASSERT(clipboard);
267  if (clipboard->serverFormats)
268  {
269  for (size_t i = 0; i < clipboard->numServerFormats; i++)
270  {
271  CLIPRDR_FORMAT* format = &clipboard->serverFormats[i];
272  free(format->formatName);
273  }
274 
275  free(clipboard->serverFormats);
276  clipboard->serverFormats = NULL;
277  }
278 }
279 
280 static BOOL xf_cliprdr_update_owner(xfClipboard* clipboard)
281 {
282  WINPR_ASSERT(clipboard);
283 
284  xfContext* xfc = clipboard->xfc;
285  WINPR_ASSERT(xfc);
286 
287  if (!clipboard->sync)
288  return FALSE;
289 
290  Window owner = XGetSelectionOwner(xfc->display, clipboard->clipboard_atom);
291  if (clipboard->owner == owner)
292  return FALSE;
293 
294  clipboard->owner = owner;
295  return TRUE;
296 }
297 
298 static void xf_cliprdr_check_owner(xfClipboard* clipboard)
299 {
300  if (xf_cliprdr_update_owner(clipboard))
301  xf_cliprdr_send_client_format_list(clipboard, FALSE);
302 }
303 
304 static BOOL xf_cliprdr_is_self_owned(xfClipboard* clipboard)
305 {
306  xfContext* xfc = NULL;
307 
308  WINPR_ASSERT(clipboard);
309 
310  xfc = clipboard->xfc;
311  WINPR_ASSERT(xfc);
312  return XGetSelectionOwner(xfc->display, clipboard->clipboard_atom) == xfc->drawable;
313 }
314 
315 static void xf_cliprdr_set_raw_transfer_enabled(xfClipboard* clipboard, BOOL enabled)
316 {
317  UINT32 data = enabled;
318  xfContext* xfc = NULL;
319 
320  WINPR_ASSERT(clipboard);
321 
322  xfc = clipboard->xfc;
323  WINPR_ASSERT(xfc);
324  LogTagAndXChangeProperty(TAG, xfc->display, xfc->drawable, clipboard->raw_transfer_atom,
325  XA_INTEGER, 32, PropModeReplace, (BYTE*)&data, 1);
326 }
327 
328 static BOOL xf_cliprdr_is_raw_transfer_available(xfClipboard* clipboard)
329 {
330  Atom type = 0;
331  int format = 0;
332  int result = 0;
333  unsigned long length = 0;
334  unsigned long bytes_left = 0;
335  UINT32* data = NULL;
336  UINT32 is_enabled = 0;
337  Window owner = None;
338  xfContext* xfc = NULL;
339 
340  WINPR_ASSERT(clipboard);
341 
342  xfc = clipboard->xfc;
343  WINPR_ASSERT(xfc);
344 
345  owner = XGetSelectionOwner(xfc->display, clipboard->clipboard_atom);
346 
347  if (owner != None)
348  {
349  result = LogTagAndXGetWindowProperty(TAG, xfc->display, owner, clipboard->raw_transfer_atom,
350  0, 4, 0, XA_INTEGER, &type, &format, &length,
351  &bytes_left, (BYTE**)&data);
352  }
353 
354  if (data)
355  {
356  is_enabled = *data;
357  XFree(data);
358  }
359 
360  if ((owner == None) || (owner == xfc->drawable))
361  return FALSE;
362 
363  if (result != Success)
364  return FALSE;
365 
366  return is_enabled ? TRUE : FALSE;
367 }
368 
369 static BOOL xf_cliprdr_formats_equal(const CLIPRDR_FORMAT* server, const xfCliprdrFormat* client)
370 {
371  WINPR_ASSERT(server);
372  WINPR_ASSERT(client);
373 
374  if (server->formatName && client->formatName)
375  {
376  /* The server may be using short format names while we store them in full form. */
377  return (0 == strncmp(server->formatName, client->formatName, strlen(server->formatName)));
378  }
379 
380  if (!server->formatName && !client->formatName)
381  {
382  return (server->formatId == client->formatToRequest);
383  }
384 
385  return FALSE;
386 }
387 
388 static const xfCliprdrFormat* xf_cliprdr_get_client_format_by_id(xfClipboard* clipboard,
389  UINT32 formatId)
390 {
391  WINPR_ASSERT(clipboard);
392 
393  for (size_t index = 0; index < clipboard->numClientFormats; index++)
394  {
395  const xfCliprdrFormat* format = &(clipboard->clientFormats[index]);
396 
397  if (format->formatToRequest == formatId)
398  return format;
399  }
400 
401  return NULL;
402 }
403 
404 static const xfCliprdrFormat* xf_cliprdr_get_client_format_by_atom(xfClipboard* clipboard,
405  Atom atom)
406 {
407  WINPR_ASSERT(clipboard);
408 
409  for (UINT32 i = 0; i < clipboard->numClientFormats; i++)
410  {
411  const xfCliprdrFormat* format = &(clipboard->clientFormats[i]);
412 
413  if (format->atom == atom)
414  return format;
415  }
416 
417  return NULL;
418 }
419 
420 static const CLIPRDR_FORMAT* xf_cliprdr_get_server_format_by_atom(xfClipboard* clipboard, Atom atom)
421 {
422  WINPR_ASSERT(clipboard);
423 
424  for (size_t i = 0; i < clipboard->numClientFormats; i++)
425  {
426  const xfCliprdrFormat* client_format = &(clipboard->clientFormats[i]);
427 
428  if (client_format->atom == atom)
429  {
430  for (size_t j = 0; j < clipboard->numServerFormats; j++)
431  {
432  const CLIPRDR_FORMAT* server_format = &(clipboard->serverFormats[j]);
433 
434  if (xf_cliprdr_formats_equal(server_format, client_format))
435  return server_format;
436  }
437  }
438  }
439 
440  return NULL;
441 }
442 
448 static UINT xf_cliprdr_send_data_request(xfClipboard* clipboard, UINT32 formatId,
449  const xfCliprdrFormat* cformat)
450 {
451  CLIPRDR_FORMAT_DATA_REQUEST request = { 0 };
452  request.requestedFormatId = formatId;
453 
454  DEBUG_CLIPRDR("requesting format 0x%08" PRIx32 " [%s] {local 0x%08" PRIx32 "} [%s]", formatId,
455  ClipboardGetFormatIdString(formatId), cformat->localFormat, cformat->formatName);
456 
457  WINPR_ASSERT(clipboard);
458  WINPR_ASSERT(clipboard->context);
459  WINPR_ASSERT(clipboard->context->ClientFormatDataRequest);
460  return clipboard->context->ClientFormatDataRequest(clipboard->context, &request);
461 }
462 
468 static UINT xf_cliprdr_send_data_response(xfClipboard* clipboard, const xfCliprdrFormat* format,
469  const BYTE* data, size_t size)
470 {
471  CLIPRDR_FORMAT_DATA_RESPONSE response = { 0 };
472 
473  WINPR_ASSERT(clipboard);
474 
475  /* No request currently pending, do not send a response. */
476  if (clipboard->requestedFormatId < 0)
477  return CHANNEL_RC_OK;
478 
479  if (size == 0)
480  {
481  if (format)
482  DEBUG_CLIPRDR("send CB_RESPONSE_FAIL response {format 0x%08" PRIx32
483  " [%s] {local 0x%08" PRIx32 "} [%s]",
484  format->formatToRequest,
485  ClipboardGetFormatIdString(format->formatToRequest), format->localFormat,
486  format->formatName);
487  else
488  DEBUG_CLIPRDR("send CB_RESPONSE_FAIL response");
489  }
490  else
491  {
492  WINPR_ASSERT(format);
493  DEBUG_CLIPRDR("send response format 0x%08" PRIx32 " [%s] {local 0x%08" PRIx32 "} [%s]",
494  format->formatToRequest, ClipboardGetFormatIdString(format->formatToRequest),
495  format->localFormat, format->formatName);
496  }
497  /* Request handled, reset to invalid */
498  clipboard->requestedFormatId = -1;
499 
500  response.common.msgFlags = (data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
501 
502  WINPR_ASSERT(size <= UINT32_MAX);
503  response.common.dataLen = (UINT32)size;
504  response.requestedFormatData = data;
505 
506  WINPR_ASSERT(clipboard->context);
507  WINPR_ASSERT(clipboard->context->ClientFormatDataResponse);
508  return clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
509 }
510 
511 static wStream* xf_cliprdr_serialize_server_format_list(xfClipboard* clipboard)
512 {
513  UINT32 formatCount = 0;
514  wStream* s = NULL;
515 
516  WINPR_ASSERT(clipboard);
517 
518  /* Typical MS Word format list is about 80 bytes long. */
519  if (!(s = Stream_New(NULL, 128)))
520  {
521  WLog_ERR(TAG, "failed to allocate serialized format list");
522  goto error;
523  }
524 
525  /* If present, the last format is always synthetic CF_RAW. Do not include it. */
526  formatCount = (clipboard->numServerFormats > 0) ? clipboard->numServerFormats - 1 : 0;
527  Stream_Write_UINT32(s, formatCount);
528 
529  for (UINT32 i = 0; i < formatCount; i++)
530  {
531  CLIPRDR_FORMAT* format = &clipboard->serverFormats[i];
532  size_t name_length = format->formatName ? strlen(format->formatName) : 0;
533 
534  DEBUG_CLIPRDR("server announced 0x%08" PRIx32 " [%s][%s]", format->formatId,
535  ClipboardGetFormatIdString(format->formatId), format->formatName);
536  if (!Stream_EnsureRemainingCapacity(s, sizeof(UINT32) + name_length + 1))
537  {
538  WLog_ERR(TAG, "failed to expand serialized format list");
539  goto error;
540  }
541 
542  Stream_Write_UINT32(s, format->formatId);
543 
544  if (format->formatName)
545  Stream_Write(s, format->formatName, name_length);
546 
547  Stream_Write_UINT8(s, '\0');
548  }
549 
550  Stream_SealLength(s);
551  return s;
552 error:
553  Stream_Free(s, TRUE);
554  return NULL;
555 }
556 
557 static CLIPRDR_FORMAT* xf_cliprdr_parse_server_format_list(BYTE* data, size_t length,
558  UINT32* numFormats)
559 {
560  wStream* s = NULL;
561  CLIPRDR_FORMAT* formats = NULL;
562 
563  WINPR_ASSERT(data || (length == 0));
564  WINPR_ASSERT(numFormats);
565 
566  if (!(s = Stream_New(data, length)))
567  {
568  WLog_ERR(TAG, "failed to allocate stream for parsing serialized format list");
569  goto error;
570  }
571 
572  if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(UINT32)))
573  goto error;
574 
575  Stream_Read_UINT32(s, *numFormats);
576 
577  if (*numFormats > MAX_CLIPBOARD_FORMATS)
578  {
579  WLog_ERR(TAG, "unexpectedly large number of formats: %" PRIu32 "", *numFormats);
580  goto error;
581  }
582 
583  if (!(formats = (CLIPRDR_FORMAT*)calloc(*numFormats, sizeof(CLIPRDR_FORMAT))))
584  {
585  WLog_ERR(TAG, "failed to allocate format list");
586  goto error;
587  }
588 
589  for (UINT32 i = 0; i < *numFormats; i++)
590  {
591  const char* formatName = NULL;
592  size_t formatNameLength = 0;
593 
594  if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(UINT32)))
595  goto error;
596 
597  Stream_Read_UINT32(s, formats[i].formatId);
598  formatName = (const char*)Stream_Pointer(s);
599  formatNameLength = strnlen(formatName, Stream_GetRemainingLength(s));
600 
601  if (formatNameLength == Stream_GetRemainingLength(s))
602  {
603  WLog_ERR(TAG, "missing terminating null byte, %" PRIuz " bytes left to read",
604  formatNameLength);
605  goto error;
606  }
607 
608  formats[i].formatName = strndup(formatName, formatNameLength);
609  Stream_Seek(s, formatNameLength + 1);
610  }
611 
612  Stream_Free(s, FALSE);
613  return formats;
614 error:
615  Stream_Free(s, FALSE);
616  free(formats);
617  *numFormats = 0;
618  return NULL;
619 }
620 
621 static void xf_cliprdr_free_formats(CLIPRDR_FORMAT* formats, UINT32 numFormats)
622 {
623  WINPR_ASSERT(formats || (numFormats == 0));
624 
625  for (UINT32 i = 0; i < numFormats; i++)
626  {
627  free(formats[i].formatName);
628  }
629 
630  free(formats);
631 }
632 
633 static CLIPRDR_FORMAT* xf_cliprdr_get_raw_server_formats(xfClipboard* clipboard, UINT32* numFormats)
634 {
635  Atom type = None;
636  int format = 0;
637  unsigned long length = 0;
638  unsigned long remaining = 0;
639  BYTE* data = NULL;
640  CLIPRDR_FORMAT* formats = NULL;
641  xfContext* xfc = NULL;
642 
643  WINPR_ASSERT(clipboard);
644  WINPR_ASSERT(numFormats);
645 
646  xfc = clipboard->xfc;
647  WINPR_ASSERT(xfc);
648 
649  *numFormats = 0;
650 
651  Window owner = XGetSelectionOwner(xfc->display, clipboard->clipboard_atom);
652  LogTagAndXGetWindowProperty(TAG, xfc->display, owner, clipboard->raw_format_list_atom, 0, 4096,
653  False, clipboard->raw_format_list_atom, &type, &format, &length,
654  &remaining, &data);
655 
656  if (data && length > 0 && format == 8 && type == clipboard->raw_format_list_atom)
657  {
658  formats = xf_cliprdr_parse_server_format_list(data, length, numFormats);
659  }
660  else
661  {
662  WLog_ERR(TAG,
663  "failed to retrieve raw format list: data=%p, length=%lu, format=%d, type=%lu "
664  "(expected=%lu)",
665  (void*)data, length, format, (unsigned long)type,
666  (unsigned long)clipboard->raw_format_list_atom);
667  }
668 
669  if (data)
670  XFree(data);
671 
672  return formats;
673 }
674 
675 static BOOL xf_cliprdr_should_add_format(const CLIPRDR_FORMAT* formats, size_t count,
676  const xfCliprdrFormat* xformat)
677 {
678  WINPR_ASSERT(formats);
679 
680  if (!xformat)
681  return FALSE;
682 
683  for (size_t x = 0; x < count; x++)
684  {
685  const CLIPRDR_FORMAT* format = &formats[x];
686  if (format->formatId == xformat->formatToRequest)
687  return FALSE;
688  }
689  return TRUE;
690 }
691 
692 static CLIPRDR_FORMAT* xf_cliprdr_get_formats_from_targets(xfClipboard* clipboard,
693  UINT32* numFormats)
694 {
695  Atom atom = None;
696  BYTE* data = NULL;
697  int format_property = 0;
698  unsigned long length = 0;
699  unsigned long bytes_left = 0;
700  CLIPRDR_FORMAT* formats = NULL;
701 
702  WINPR_ASSERT(clipboard);
703  WINPR_ASSERT(numFormats);
704 
705  xfContext* xfc = clipboard->xfc;
706  WINPR_ASSERT(xfc);
707 
708  *numFormats = 0;
709  LogTagAndXGetWindowProperty(TAG, xfc->display, xfc->drawable, clipboard->property_atom, 0, 200,
710  0, XA_ATOM, &atom, &format_property, &length, &bytes_left, &data);
711 
712  if (length > 0)
713  {
714  if (!data)
715  {
716  WLog_ERR(TAG, "XGetWindowProperty set length = %lu but data is NULL", length);
717  goto out;
718  }
719 
720  if (!(formats = (CLIPRDR_FORMAT*)calloc(length, sizeof(CLIPRDR_FORMAT))))
721  {
722  WLog_ERR(TAG, "failed to allocate %lu CLIPRDR_FORMAT structs", length);
723  goto out;
724  }
725  }
726 
727  for (unsigned long i = 0; i < length; i++)
728  {
729  Atom tatom = ((Atom*)data)[i];
730  const xfCliprdrFormat* format = xf_cliprdr_get_client_format_by_atom(clipboard, tatom);
731 
732  if (xf_cliprdr_should_add_format(formats, *numFormats, format))
733  {
734  CLIPRDR_FORMAT* cformat = &formats[*numFormats];
735  cformat->formatId = format->formatToRequest;
736  if (format->formatName)
737  {
738  cformat->formatName = _strdup(format->formatName);
739  WINPR_ASSERT(cformat->formatName);
740  }
741  else
742  cformat->formatName = NULL;
743 
744  *numFormats += 1;
745  }
746  }
747 
748 out:
749 
750  if (data)
751  XFree(data);
752 
753  return formats;
754 }
755 
756 static CLIPRDR_FORMAT* xf_cliprdr_get_client_formats(xfClipboard* clipboard, UINT32* numFormats)
757 {
758  CLIPRDR_FORMAT* formats = NULL;
759 
760  WINPR_ASSERT(clipboard);
761  WINPR_ASSERT(numFormats);
762 
763  *numFormats = 0;
764 
765  if (xf_cliprdr_is_raw_transfer_available(clipboard))
766  {
767  formats = xf_cliprdr_get_raw_server_formats(clipboard, numFormats);
768  }
769 
770  if (*numFormats == 0)
771  {
772  xf_cliprdr_free_formats(formats, *numFormats);
773  formats = xf_cliprdr_get_formats_from_targets(clipboard, numFormats);
774  }
775 
776  return formats;
777 }
778 
779 static void xf_cliprdr_provide_server_format_list(xfClipboard* clipboard)
780 {
781  wStream* formats = NULL;
782  xfContext* xfc = NULL;
783 
784  WINPR_ASSERT(clipboard);
785 
786  xfc = clipboard->xfc;
787  WINPR_ASSERT(xfc);
788 
789  formats = xf_cliprdr_serialize_server_format_list(clipboard);
790 
791  if (formats)
792  {
793  const size_t len = Stream_Length(formats);
794  WINPR_ASSERT(len <= INT32_MAX);
795  LogTagAndXChangeProperty(TAG, xfc->display, xfc->drawable, clipboard->raw_format_list_atom,
796  clipboard->raw_format_list_atom, 8, PropModeReplace,
797  Stream_Buffer(formats), (int)len);
798  }
799  else
800  {
801  LogTagAndXDeleteProperty(TAG, xfc->display, xfc->drawable, clipboard->raw_format_list_atom);
802  }
803 
804  Stream_Free(formats, TRUE);
805 }
806 
807 static BOOL xf_clipboard_format_equal(const CLIPRDR_FORMAT* a, const CLIPRDR_FORMAT* b)
808 {
809  WINPR_ASSERT(a);
810  WINPR_ASSERT(b);
811 
812  if (a->formatId != b->formatId)
813  return FALSE;
814  if (!a->formatName && !b->formatName)
815  return TRUE;
816  if (!a->formatName || !b->formatName)
817  return FALSE;
818  return strcmp(a->formatName, b->formatName) == 0;
819 }
820 
821 static BOOL xf_clipboard_changed(xfClipboard* clipboard, const CLIPRDR_FORMAT* formats,
822  UINT32 numFormats)
823 {
824  WINPR_ASSERT(clipboard);
825  WINPR_ASSERT(formats || (numFormats == 0));
826 
827  if (clipboard->lastSentNumFormats != numFormats)
828  return TRUE;
829 
830  for (UINT32 x = 0; x < numFormats; x++)
831  {
832  const CLIPRDR_FORMAT* cur = &clipboard->lastSentFormats[x];
833  BOOL contained = FALSE;
834  for (UINT32 y = 0; y < numFormats; y++)
835  {
836  if (xf_clipboard_format_equal(cur, &formats[y]))
837  {
838  contained = TRUE;
839  break;
840  }
841  }
842  if (!contained)
843  return TRUE;
844  }
845 
846  return FALSE;
847 }
848 
849 static void xf_clipboard_formats_free(xfClipboard* clipboard)
850 {
851  WINPR_ASSERT(clipboard);
852 
853  xf_cliprdr_free_formats(clipboard->lastSentFormats, clipboard->lastSentNumFormats);
854  clipboard->lastSentFormats = NULL;
855  clipboard->lastSentNumFormats = 0;
856 }
857 
858 static BOOL xf_clipboard_copy_formats(xfClipboard* clipboard, const CLIPRDR_FORMAT* formats,
859  UINT32 numFormats)
860 {
861  WINPR_ASSERT(clipboard);
862  WINPR_ASSERT(formats || (numFormats == 0));
863 
864  xf_clipboard_formats_free(clipboard);
865  if (numFormats > 0)
866  clipboard->lastSentFormats = calloc(numFormats, sizeof(CLIPRDR_FORMAT));
867  if (!clipboard->lastSentFormats)
868  return FALSE;
869  clipboard->lastSentNumFormats = numFormats;
870  for (UINT32 x = 0; x < numFormats; x++)
871  {
872  CLIPRDR_FORMAT* lcur = &clipboard->lastSentFormats[x];
873  const CLIPRDR_FORMAT* cur = &formats[x];
874  *lcur = *cur;
875  if (cur->formatName)
876  lcur->formatName = _strdup(cur->formatName);
877  }
878  return FALSE;
879 }
880 
881 static UINT xf_cliprdr_send_format_list(xfClipboard* clipboard, const CLIPRDR_FORMAT* formats,
882  UINT32 numFormats, BOOL force)
883 {
884  union
885  {
886  const CLIPRDR_FORMAT* cpv;
887  CLIPRDR_FORMAT* pv;
888  } cnv = { .cpv = formats };
889  const CLIPRDR_FORMAT_LIST formatList = { .common.msgFlags = 0,
890  .numFormats = numFormats,
891  .formats = cnv.pv,
892  .common.msgType = CB_FORMAT_LIST };
893  UINT ret = 0;
894 
895  WINPR_ASSERT(clipboard);
896  WINPR_ASSERT(formats || (numFormats == 0));
897 
898  if (!force && !xf_clipboard_changed(clipboard, formats, numFormats))
899  return CHANNEL_RC_OK;
900 
901 #if defined(WITH_DEBUG_CLIPRDR)
902  for (UINT32 x = 0; x < numFormats; x++)
903  {
904  const CLIPRDR_FORMAT* format = &formats[x];
905  DEBUG_CLIPRDR("announcing format 0x%08" PRIx32 " [%s] [%s]", format->formatId,
906  ClipboardGetFormatIdString(format->formatId), format->formatName);
907  }
908 #endif
909 
910  xf_clipboard_copy_formats(clipboard, formats, numFormats);
911  /* Ensure all pending requests are answered. */
912  xf_cliprdr_send_data_response(clipboard, NULL, NULL, 0);
913 
914  xf_cliprdr_clear_cached_data(clipboard);
915 
916  ret = cliprdr_file_context_notify_new_client_format_list(clipboard->file);
917  if (ret)
918  return ret;
919 
920  WINPR_ASSERT(clipboard->context);
921  WINPR_ASSERT(clipboard->context->ClientFormatList);
922  return clipboard->context->ClientFormatList(clipboard->context, &formatList);
923 }
924 
925 static void xf_cliprdr_get_requested_targets(xfClipboard* clipboard)
926 {
927  UINT32 numFormats = 0;
928  CLIPRDR_FORMAT* formats = xf_cliprdr_get_client_formats(clipboard, &numFormats);
929  xf_cliprdr_send_format_list(clipboard, formats, numFormats, FALSE);
930  xf_cliprdr_free_formats(formats, numFormats);
931 }
932 
933 static void xf_cliprdr_process_requested_data(xfClipboard* clipboard, BOOL hasData,
934  const BYTE* data, size_t size)
935 {
936  BOOL bSuccess = 0;
937  UINT32 SrcSize = 0;
938  UINT32 DstSize = 0;
939  INT64 srcFormatId = -1;
940  BYTE* pDstData = NULL;
941  const xfCliprdrFormat* format = NULL;
942 
943  WINPR_ASSERT(clipboard);
944 
945  if (clipboard->incr_starts && hasData)
946  return;
947 
948  format = xf_cliprdr_get_client_format_by_id(clipboard, clipboard->requestedFormatId);
949 
950  if (!hasData || !data || !format)
951  {
952  xf_cliprdr_send_data_response(clipboard, format, NULL, 0);
953  return;
954  }
955 
956  switch (format->formatToRequest)
957  {
958  case CF_RAW:
959  srcFormatId = CF_RAW;
960  break;
961 
962  case CF_TEXT:
963  case CF_OEMTEXT:
964  case CF_UNICODETEXT:
965  size = strlen((const char*)data) + 1;
966  srcFormatId = format->localFormat;
967  break;
968 
969  default:
970  srcFormatId = format->localFormat;
971  break;
972  }
973 
974  if (srcFormatId < 0)
975  {
976  xf_cliprdr_send_data_response(clipboard, format, NULL, 0);
977  return;
978  }
979 
980  ClipboardLock(clipboard->system);
981  SrcSize = (UINT32)size;
982  bSuccess = ClipboardSetData(clipboard->system, (UINT32)srcFormatId, data, SrcSize);
983 
984  if (bSuccess)
985  {
986  DstSize = 0;
987  pDstData = (BYTE*)ClipboardGetData(clipboard->system, format->formatToRequest, &DstSize);
988  }
989  ClipboardUnlock(clipboard->system);
990 
991  if (!pDstData)
992  {
993  xf_cliprdr_send_data_response(clipboard, format, NULL, 0);
994  return;
995  }
996 
997  /*
998  * File lists require a bit of postprocessing to convert them from WinPR's FILDESCRIPTOR
999  * format to CLIPRDR_FILELIST expected by the server.
1000  *
1001  * We check for "FileGroupDescriptorW" format being registered (i.e., nonzero) in order
1002  * to not process CF_RAW as a file list in case WinPR does not support file transfers.
1003  */
1004  ClipboardLock(clipboard->system);
1005  if (format->formatToRequest &&
1006  (format->formatToRequest ==
1007  ClipboardGetFormatId(clipboard->system, type_FileGroupDescriptorW)))
1008  {
1009  UINT error = NO_ERROR;
1010  FILEDESCRIPTORW* file_array = (FILEDESCRIPTORW*)pDstData;
1011  UINT32 file_count = DstSize / sizeof(FILEDESCRIPTORW);
1012  pDstData = NULL;
1013  DstSize = 0;
1014 
1015  const UINT32 flags = cliprdr_file_context_remote_get_flags(clipboard->file);
1016  error = cliprdr_serialize_file_list_ex(flags, file_array, file_count, &pDstData, &DstSize);
1017 
1018  if (error)
1019  WLog_ERR(TAG, "failed to serialize CLIPRDR_FILELIST: 0x%08X", error);
1020  else
1021  {
1022  UINT32 formatId = ClipboardGetFormatId(clipboard->system, mime_uri_list);
1023  UINT32 url_size = 0;
1024 
1025  char* url = ClipboardGetData(clipboard->system, formatId, &url_size);
1026  cliprdr_file_context_update_client_data(clipboard->file, url, url_size);
1027  free(url);
1028  }
1029 
1030  free(file_array);
1031  }
1032  ClipboardUnlock(clipboard->system);
1033 
1034  xf_cliprdr_send_data_response(clipboard, format, pDstData, DstSize);
1035  free(pDstData);
1036 }
1037 
1038 static BOOL xf_add_input_flags(xfClipboard* clipboard, long mask)
1039 {
1040  WINPR_ASSERT(clipboard);
1041 
1042  xfContext* xfc = clipboard->xfc;
1043  WINPR_ASSERT(xfc);
1044 
1045  XWindowAttributes attr = { 0 };
1046  XGetWindowAttributes(xfc->display, xfc->drawable, &attr);
1047  if ((attr.all_event_masks & mask) == 0)
1048  clipboard->event_mask = attr.all_event_masks;
1049 
1050  XSelectInput(xfc->display, xfc->drawable, attr.all_event_masks | mask);
1051  return TRUE;
1052 }
1053 
1054 static BOOL xf_restore_input_flags(xfClipboard* clipboard)
1055 {
1056  WINPR_ASSERT(clipboard);
1057 
1058  xfContext* xfc = clipboard->xfc;
1059  WINPR_ASSERT(xfc);
1060 
1061  if (clipboard->event_mask != 0)
1062  {
1063  XSelectInput(xfc->display, xfc->drawable, clipboard->event_mask);
1064  clipboard->event_mask = 0;
1065  }
1066  return TRUE;
1067 }
1068 
1069 static BOOL xf_cliprdr_get_requested_data(xfClipboard* clipboard, Atom target)
1070 {
1071  Atom type = 0;
1072  BYTE* data = NULL;
1073  BOOL has_data = FALSE;
1074  int format_property = 0;
1075  unsigned long dummy = 0;
1076  unsigned long length = 0;
1077  unsigned long bytes_left = 0;
1078  const xfCliprdrFormat* format = NULL;
1079  xfContext* xfc = NULL;
1080 
1081  WINPR_ASSERT(clipboard);
1082 
1083  xfc = clipboard->xfc;
1084  WINPR_ASSERT(xfc);
1085 
1086  format = xf_cliprdr_get_client_format_by_id(clipboard, clipboard->requestedFormatId);
1087 
1088  if (!format || (format->atom != target))
1089  {
1090  xf_cliprdr_send_data_response(clipboard, format, NULL, 0);
1091  return FALSE;
1092  }
1093 
1094  LogTagAndXGetWindowProperty(TAG, xfc->display, xfc->drawable, clipboard->property_atom, 0, 0, 0,
1095  target, &type, &format_property, &length, &bytes_left, &data);
1096 
1097  if (data)
1098  {
1099  XFree(data);
1100  data = NULL;
1101  }
1102 
1103  if (bytes_left <= 0 && !clipboard->incr_starts)
1104  {
1105  }
1106  else if (type == clipboard->incr_atom)
1107  {
1108  clipboard->incr_starts = TRUE;
1109 
1110  if (clipboard->incr_data)
1111  {
1112  free(clipboard->incr_data);
1113  clipboard->incr_data = NULL;
1114  }
1115 
1116  clipboard->incr_data_length = 0;
1117  has_data = TRUE; /* data will be followed in PropertyNotify event */
1118  xf_add_input_flags(clipboard, PropertyChangeMask);
1119  }
1120  else
1121  {
1122  if (bytes_left <= 0)
1123  {
1124  /* INCR finish */
1125  data = clipboard->incr_data;
1126  clipboard->incr_data = NULL;
1127  bytes_left = clipboard->incr_data_length;
1128  clipboard->incr_data_length = 0;
1129  clipboard->incr_starts = FALSE;
1130 
1131  /* Restore previous event mask */
1132  xf_restore_input_flags(clipboard);
1133 
1134  has_data = TRUE;
1135  }
1136  else if (LogTagAndXGetWindowProperty(
1137  TAG, xfc->display, xfc->drawable, clipboard->property_atom, 0, bytes_left, 0,
1138  target, &type, &format_property, &length, &dummy, &data) == Success)
1139  {
1140  if (clipboard->incr_starts)
1141  {
1142  BYTE* new_data = NULL;
1143  bytes_left = length * format_property / 8;
1144  new_data =
1145  (BYTE*)realloc(clipboard->incr_data, clipboard->incr_data_length + bytes_left);
1146 
1147  if (new_data)
1148  {
1149 
1150  clipboard->incr_data = new_data;
1151  CopyMemory(clipboard->incr_data + clipboard->incr_data_length, data,
1152  bytes_left);
1153  clipboard->incr_data_length += bytes_left;
1154  XFree(data);
1155  data = NULL;
1156  }
1157  }
1158 
1159  has_data = TRUE;
1160  }
1161  else
1162  {
1163  }
1164  }
1165 
1166  LogTagAndXDeleteProperty(TAG, xfc->display, xfc->drawable, clipboard->property_atom);
1167  xf_cliprdr_process_requested_data(clipboard, has_data, data, bytes_left);
1168 
1169  if (data)
1170  XFree(data);
1171 
1172  return TRUE;
1173 }
1174 
1175 static void xf_cliprdr_append_target(xfClipboard* clipboard, Atom target)
1176 {
1177  WINPR_ASSERT(clipboard);
1178 
1179  if (clipboard->numTargets >= ARRAYSIZE(clipboard->targets))
1180  return;
1181 
1182  for (size_t i = 0; i < clipboard->numTargets; i++)
1183  {
1184  if (clipboard->targets[i] == target)
1185  return;
1186  }
1187 
1188  clipboard->targets[clipboard->numTargets++] = target;
1189 }
1190 
1191 static void xf_cliprdr_provide_targets(xfClipboard* clipboard, const XSelectionEvent* respond)
1192 {
1193  xfContext* xfc = NULL;
1194 
1195  WINPR_ASSERT(clipboard);
1196 
1197  xfc = clipboard->xfc;
1198  WINPR_ASSERT(xfc);
1199 
1200  if (respond->property != None)
1201  {
1202  WINPR_ASSERT(clipboard->numTargets <= INT32_MAX);
1203  LogTagAndXChangeProperty(TAG, xfc->display, respond->requestor, respond->property, XA_ATOM,
1204  32, PropModeReplace, (BYTE*)clipboard->targets,
1205  (int)clipboard->numTargets);
1206  }
1207 }
1208 
1209 static void xf_cliprdr_provide_timestamp(xfClipboard* clipboard, const XSelectionEvent* respond)
1210 {
1211  xfContext* xfc = NULL;
1212 
1213  WINPR_ASSERT(clipboard);
1214 
1215  xfc = clipboard->xfc;
1216  WINPR_ASSERT(xfc);
1217 
1218  if (respond->property != None)
1219  {
1220  LogTagAndXChangeProperty(TAG, xfc->display, respond->requestor, respond->property,
1221  XA_INTEGER, 32, PropModeReplace,
1222  (BYTE*)&clipboard->selection_ownership_timestamp, 1);
1223  }
1224 }
1225 
1226 static void xf_cliprdr_provide_data(xfClipboard* clipboard, const XSelectionEvent* respond,
1227  const BYTE* data, UINT32 size)
1228 {
1229  xfContext* xfc = NULL;
1230 
1231  WINPR_ASSERT(clipboard);
1232 
1233  xfc = clipboard->xfc;
1234  WINPR_ASSERT(xfc);
1235 
1236  if (respond->property != None)
1237  {
1238  LogTagAndXChangeProperty(TAG, xfc->display, respond->requestor, respond->property,
1239  respond->target, 8, PropModeReplace, data, size);
1240  }
1241 }
1242 
1243 static void log_selection_event(xfContext* xfc, const XEvent* event)
1244 {
1245  const DWORD level = WLOG_TRACE;
1246  static wLog* _log_cached_ptr = NULL;
1247  if (!_log_cached_ptr)
1248  _log_cached_ptr = WLog_Get(TAG);
1249  if (WLog_IsLevelActive(_log_cached_ptr, level))
1250  {
1251 
1252  switch (event->type)
1253  {
1254  case SelectionClear:
1255  {
1256  const XSelectionClearEvent* xevent = &event->xselectionclear;
1257  char* selection =
1258  Safe_XGetAtomName(_log_cached_ptr, xfc->display, xevent->selection);
1259  WLog_Print(_log_cached_ptr, level, "got event %s [selection %s]",
1260  x11_event_string(event->type), selection);
1261  XFree(selection);
1262  }
1263  break;
1264  case SelectionNotify:
1265  {
1266  const XSelectionEvent* xevent = &event->xselection;
1267  char* selection =
1268  Safe_XGetAtomName(_log_cached_ptr, xfc->display, xevent->selection);
1269  char* target = Safe_XGetAtomName(_log_cached_ptr, xfc->display, xevent->target);
1270  char* property = Safe_XGetAtomName(_log_cached_ptr, xfc->display, xevent->property);
1271  WLog_Print(_log_cached_ptr, level,
1272  "got event %s [selection %s, target %s, property %s]",
1273  x11_event_string(event->type), selection, target, property);
1274  XFree(selection);
1275  XFree(target);
1276  XFree(property);
1277  }
1278  break;
1279  case SelectionRequest:
1280  {
1281  const XSelectionRequestEvent* xevent = &event->xselectionrequest;
1282  char* selection =
1283  Safe_XGetAtomName(_log_cached_ptr, xfc->display, xevent->selection);
1284  char* target = Safe_XGetAtomName(_log_cached_ptr, xfc->display, xevent->target);
1285  char* property = Safe_XGetAtomName(_log_cached_ptr, xfc->display, xevent->property);
1286  WLog_Print(_log_cached_ptr, level,
1287  "got event %s [selection %s, target %s, property %s]",
1288  x11_event_string(event->type), selection, target, property);
1289  XFree(selection);
1290  XFree(target);
1291  XFree(property);
1292  }
1293  break;
1294  case PropertyNotify:
1295  {
1296  const XPropertyEvent* xevent = &event->xproperty;
1297  char* atom = Safe_XGetAtomName(_log_cached_ptr, xfc->display, xevent->atom);
1298  WLog_Print(_log_cached_ptr, level, "got event %s [atom %s]",
1299  x11_event_string(event->type), atom);
1300  XFree(atom);
1301  }
1302  break;
1303  default:
1304  break;
1305  }
1306  }
1307 }
1308 
1309 static BOOL xf_cliprdr_process_selection_notify(xfClipboard* clipboard,
1310  const XSelectionEvent* xevent)
1311 {
1312  WINPR_ASSERT(clipboard);
1313  WINPR_ASSERT(xevent);
1314 
1315  if (xevent->target == clipboard->targets[1])
1316  {
1317  if (xevent->property == None)
1318  {
1319  xf_cliprdr_send_client_format_list(clipboard, FALSE);
1320  }
1321  else
1322  {
1323  xf_cliprdr_get_requested_targets(clipboard);
1324  }
1325 
1326  return TRUE;
1327  }
1328  else
1329  {
1330  return xf_cliprdr_get_requested_data(clipboard, xevent->target);
1331  }
1332 }
1333 
1334 void xf_cliprdr_clear_cached_data(xfClipboard* clipboard)
1335 {
1336  WINPR_ASSERT(clipboard);
1337 
1338  ClipboardLock(clipboard->system);
1339  ClipboardEmpty(clipboard->system);
1340 
1341  HashTable_Clear(clipboard->cachedData);
1342  HashTable_Clear(clipboard->cachedRawData);
1343 
1344  cliprdr_file_context_clear(clipboard->file);
1345  ClipboardUnlock(clipboard->system);
1346 }
1347 
1348 static void* format_to_cache_slot(UINT32 format)
1349 {
1350  union
1351  {
1352  uintptr_t uptr;
1353  void* vptr;
1354  } cnv;
1355  cnv.uptr = 0x100000000ULL + format;
1356  return cnv.vptr;
1357 }
1358 
1359 static UINT32 get_dst_format_id_for_local_request(xfClipboard* clipboard,
1360  const xfCliprdrFormat* format)
1361 {
1362  UINT32 dstFormatId = 0;
1363 
1364  WINPR_ASSERT(format);
1365 
1366  if (!format->formatName)
1367  return format->localFormat;
1368 
1369  ClipboardLock(clipboard->system);
1370  if (strcmp(format->formatName, type_HtmlFormat) == 0)
1371  dstFormatId = ClipboardGetFormatId(clipboard->system, mime_html);
1372  ClipboardUnlock(clipboard->system);
1373 
1374  if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0)
1375  dstFormatId = format->localFormat;
1376 
1377  return dstFormatId;
1378 }
1379 
1380 static void get_src_format_info_for_local_request(xfClipboard* clipboard,
1381  const xfCliprdrFormat* format,
1382  UINT32* srcFormatId, BOOL* nullTerminated)
1383 {
1384  *srcFormatId = 0;
1385  *nullTerminated = FALSE;
1386 
1387  if (format->formatName)
1388  {
1389  ClipboardLock(clipboard->system);
1390  if (strcmp(format->formatName, type_HtmlFormat) == 0)
1391  {
1392  *srcFormatId = ClipboardGetFormatId(clipboard->system, type_HtmlFormat);
1393  *nullTerminated = TRUE;
1394  }
1395  else if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0)
1396  {
1397  *srcFormatId = ClipboardGetFormatId(clipboard->system, type_FileGroupDescriptorW);
1398  *nullTerminated = TRUE;
1399  }
1400  ClipboardUnlock(clipboard->system);
1401  }
1402  else
1403  {
1404  *srcFormatId = format->formatToRequest;
1405  switch (format->formatToRequest)
1406  {
1407  case CF_TEXT:
1408  case CF_OEMTEXT:
1409  case CF_UNICODETEXT:
1410  *nullTerminated = TRUE;
1411  break;
1412  case CF_DIB:
1413  *srcFormatId = CF_DIB;
1414  break;
1415  case CF_TIFF:
1416  *srcFormatId = CF_TIFF;
1417  break;
1418  default:
1419  break;
1420  }
1421  }
1422 }
1423 
1424 static xfCachedData* convert_data_from_existing_raw_data(xfClipboard* clipboard,
1425  xfCachedData* cached_raw_data,
1426  UINT32 srcFormatId, BOOL nullTerminated,
1427  UINT32 dstFormatId)
1428 {
1429  xfCachedData* cached_data = NULL;
1430  BOOL success = 0;
1431  BYTE* dst_data = NULL;
1432  UINT32 dst_size = 0;
1433 
1434  WINPR_ASSERT(clipboard);
1435  WINPR_ASSERT(cached_raw_data);
1436  WINPR_ASSERT(cached_raw_data->data);
1437 
1438  ClipboardLock(clipboard->system);
1439  success = ClipboardSetData(clipboard->system, srcFormatId, cached_raw_data->data,
1440  cached_raw_data->data_length);
1441  if (!success)
1442  {
1443  WLog_WARN(TAG, "Failed to set clipboard data (formatId: %u, data: %p, data_length: %u)",
1444  srcFormatId, cached_raw_data->data, cached_raw_data->data_length);
1445  ClipboardUnlock(clipboard->system);
1446  return NULL;
1447  }
1448 
1449  dst_data = ClipboardGetData(clipboard->system, dstFormatId, &dst_size);
1450  if (!dst_data)
1451  {
1452  WLog_WARN(TAG, "Failed to get converted clipboard data");
1453  ClipboardUnlock(clipboard->system);
1454  return NULL;
1455  }
1456  ClipboardUnlock(clipboard->system);
1457 
1458  if (nullTerminated)
1459  {
1460  BYTE* nullTerminator = memchr(dst_data, '\0', dst_size);
1461  if (nullTerminator)
1462  {
1463  const intptr_t diff = nullTerminator - dst_data;
1464  WINPR_ASSERT(diff >= 0);
1465  WINPR_ASSERT(diff <= UINT32_MAX);
1466  dst_size = (UINT32)diff;
1467  }
1468  }
1469 
1470  cached_data = xf_cached_data_new(dst_data, dst_size);
1471  if (!cached_data)
1472  {
1473  WLog_WARN(TAG, "Failed to allocate cache entry");
1474  free(dst_data);
1475  return NULL;
1476  }
1477 
1478  if (!HashTable_Insert(clipboard->cachedData, format_to_cache_slot(dstFormatId), cached_data))
1479  {
1480  WLog_WARN(TAG, "Failed to cache clipboard data");
1481  xf_cached_data_free(cached_data);
1482  return NULL;
1483  }
1484 
1485  return cached_data;
1486 }
1487 
1488 static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard,
1489  const XSelectionRequestEvent* xevent)
1490 {
1491  int fmt = 0;
1492  Atom type = 0;
1493  UINT32 formatId = 0;
1494  XSelectionEvent* respond = NULL;
1495  BYTE* data = NULL;
1496  BOOL delayRespond = 0;
1497  BOOL rawTransfer = 0;
1498  unsigned long length = 0;
1499  unsigned long bytes_left = 0;
1500  xfContext* xfc = NULL;
1501 
1502  WINPR_ASSERT(clipboard);
1503  WINPR_ASSERT(xevent);
1504 
1505  xfc = clipboard->xfc;
1506  WINPR_ASSERT(xfc);
1507 
1508  if (xevent->owner != xfc->drawable)
1509  return FALSE;
1510 
1511  delayRespond = FALSE;
1512 
1513  if (!(respond = (XSelectionEvent*)calloc(1, sizeof(XSelectionEvent))))
1514  {
1515  WLog_ERR(TAG, "failed to allocate XEvent data");
1516  return FALSE;
1517  }
1518 
1519  respond->property = None;
1520  respond->type = SelectionNotify;
1521  respond->display = xevent->display;
1522  respond->requestor = xevent->requestor;
1523  respond->selection = xevent->selection;
1524  respond->target = xevent->target;
1525  respond->time = xevent->time;
1526 
1527  if (xevent->target == clipboard->targets[0]) /* TIMESTAMP */
1528  {
1529  /* Someone else requests the selection's timestamp */
1530  respond->property = xevent->property;
1531  xf_cliprdr_provide_timestamp(clipboard, respond);
1532  }
1533  else if (xevent->target == clipboard->targets[1]) /* TARGETS */
1534  {
1535  /* Someone else requests our available formats */
1536  respond->property = xevent->property;
1537  xf_cliprdr_provide_targets(clipboard, respond);
1538  }
1539  else
1540  {
1541  const CLIPRDR_FORMAT* format =
1542  xf_cliprdr_get_server_format_by_atom(clipboard, xevent->target);
1543  const xfCliprdrFormat* cformat =
1544  xf_cliprdr_get_client_format_by_atom(clipboard, xevent->target);
1545 
1546  if (format && (xevent->requestor != xfc->drawable))
1547  {
1548  formatId = format->formatId;
1549  rawTransfer = FALSE;
1550  xfCachedData* cached_data = NULL;
1551  UINT32 dstFormatId = 0;
1552 
1553  if (formatId == CF_RAW)
1554  {
1555  if (LogTagAndXGetWindowProperty(
1556  TAG, xfc->display, xevent->requestor, clipboard->property_atom, 0, 4, 0,
1557  XA_INTEGER, &type, &fmt, &length, &bytes_left, &data) != Success)
1558  {
1559  }
1560 
1561  if (data)
1562  {
1563  rawTransfer = TRUE;
1564  CopyMemory(&formatId, data, 4);
1565  XFree(data);
1566  }
1567  }
1568 
1569  dstFormatId = get_dst_format_id_for_local_request(clipboard, cformat);
1570  DEBUG_CLIPRDR("formatId: %u, dstFormatId: %u", formatId, dstFormatId);
1571 
1572  if (!rawTransfer)
1573  cached_data = HashTable_GetItemValue(clipboard->cachedData,
1574  format_to_cache_slot(dstFormatId));
1575  else
1576  cached_data = HashTable_GetItemValue(clipboard->cachedRawData,
1577  format_to_cache_slot(formatId));
1578 
1579  DEBUG_CLIPRDR("hasCachedData: %u, rawTransfer: %u", cached_data ? 1 : 0, rawTransfer);
1580 
1581  if (!cached_data && !rawTransfer)
1582  {
1583  UINT32 srcFormatId = 0;
1584  BOOL nullTerminated = FALSE;
1585  xfCachedData* cached_raw_data = NULL;
1586 
1587  get_src_format_info_for_local_request(clipboard, cformat, &srcFormatId,
1588  &nullTerminated);
1589  cached_raw_data =
1590  HashTable_GetItemValue(clipboard->cachedRawData, (void*)(UINT_PTR)srcFormatId);
1591 
1592  DEBUG_CLIPRDR("hasCachedRawData: %u, rawDataLength: %u", cached_raw_data ? 1 : 0,
1593  cached_raw_data ? cached_raw_data->data_length : 0);
1594 
1595  if (cached_raw_data && cached_raw_data->data_length != 0)
1596  cached_data = convert_data_from_existing_raw_data(
1597  clipboard, cached_raw_data, srcFormatId, nullTerminated, dstFormatId);
1598  }
1599 
1600  DEBUG_CLIPRDR("hasCachedData: %u", cached_data ? 1 : 0);
1601 
1602  if (cached_data)
1603  {
1604  /* Cached clipboard data available. Send it now */
1605  respond->property = xevent->property;
1606 
1607  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1608  xf_cliprdr_provide_data(clipboard, respond, cached_data->data,
1609  cached_data->data_length);
1610  }
1611  else if (clipboard->respond)
1612  {
1613  /* duplicate request */
1614  }
1615  else
1616  {
1617  WINPR_ASSERT(cformat);
1618 
1623  respond->property = xevent->property;
1624  clipboard->respond = respond;
1625  requested_format_replace(&clipboard->requestedFormat, formatId,
1626  cformat->formatName);
1627  clipboard->data_raw_format = rawTransfer;
1628  delayRespond = TRUE;
1629  xf_cliprdr_send_data_request(clipboard, formatId, cformat);
1630  }
1631  }
1632  }
1633 
1634  if (!delayRespond)
1635  {
1636  union
1637  {
1638  XEvent* ev;
1639  XSelectionEvent* sev;
1640  } conv;
1641 
1642  conv.sev = respond;
1643  XSendEvent(xfc->display, xevent->requestor, 0, 0, conv.ev);
1644  XFlush(xfc->display);
1645  free(respond);
1646  }
1647 
1648  return TRUE;
1649 }
1650 
1651 static BOOL xf_cliprdr_process_selection_clear(xfClipboard* clipboard,
1652  const XSelectionClearEvent* xevent)
1653 {
1654  xfContext* xfc = NULL;
1655 
1656  WINPR_ASSERT(clipboard);
1657  WINPR_ASSERT(xevent);
1658 
1659  xfc = clipboard->xfc;
1660  WINPR_ASSERT(xfc);
1661 
1662  WINPR_UNUSED(xevent);
1663 
1664  if (xf_cliprdr_is_self_owned(clipboard))
1665  return FALSE;
1666 
1667  LogTagAndXDeleteProperty(TAG, xfc->display, clipboard->root_window, clipboard->property_atom);
1668  return TRUE;
1669 }
1670 
1671 static BOOL xf_cliprdr_process_property_notify(xfClipboard* clipboard, const XPropertyEvent* xevent)
1672 {
1673  const xfCliprdrFormat* format = NULL;
1674  xfContext* xfc = NULL;
1675 
1676  if (!clipboard)
1677  return TRUE;
1678 
1679  xfc = clipboard->xfc;
1680  WINPR_ASSERT(xfc);
1681  WINPR_ASSERT(xevent);
1682 
1683  if (xevent->atom == clipboard->timestamp_property_atom)
1684  {
1685  /* This is the response to the property change we did
1686  * in xf_cliprdr_prepare_to_set_selection_owner. Now
1687  * we can set ourselves as the selection owner. (See
1688  * comments in those functions below.) */
1689  xf_cliprdr_set_selection_owner(xfc, clipboard, xevent->time);
1690  return TRUE;
1691  }
1692 
1693  if (xevent->atom != clipboard->property_atom)
1694  return FALSE; /* Not cliprdr-related */
1695 
1696  if (xevent->window == clipboard->root_window)
1697  {
1698  xf_cliprdr_send_client_format_list(clipboard, FALSE);
1699  }
1700  else if ((xevent->window == xfc->drawable) && (xevent->state == PropertyNewValue) &&
1701  clipboard->incr_starts)
1702  {
1703  format = xf_cliprdr_get_client_format_by_id(clipboard, clipboard->requestedFormatId);
1704 
1705  if (format)
1706  xf_cliprdr_get_requested_data(clipboard, format->atom);
1707  }
1708 
1709  return TRUE;
1710 }
1711 
1712 void xf_cliprdr_handle_xevent(xfContext* xfc, const XEvent* event)
1713 {
1714  xfClipboard* clipboard = NULL;
1715 
1716  if (!xfc || !event)
1717  return;
1718 
1719  clipboard = xfc->clipboard;
1720 
1721  if (!clipboard)
1722  return;
1723 
1724 #ifdef WITH_XFIXES
1725 
1726  if (clipboard->xfixes_supported &&
1727  event->type == XFixesSelectionNotify + clipboard->xfixes_event_base)
1728  {
1729  const XFixesSelectionNotifyEvent* se = (const XFixesSelectionNotifyEvent*)event;
1730 
1731  if (se->subtype == XFixesSetSelectionOwnerNotify)
1732  {
1733  if (se->selection != clipboard->clipboard_atom)
1734  return;
1735 
1736  if (XGetSelectionOwner(xfc->display, se->selection) == xfc->drawable)
1737  return;
1738 
1739  clipboard->owner = None;
1740  xf_cliprdr_check_owner(clipboard);
1741  }
1742 
1743  return;
1744  }
1745 
1746 #endif
1747 
1748  switch (event->type)
1749  {
1750  case SelectionNotify:
1751  log_selection_event(xfc, event);
1752  xf_cliprdr_process_selection_notify(clipboard, &event->xselection);
1753  break;
1754 
1755  case SelectionRequest:
1756  log_selection_event(xfc, event);
1757  xf_cliprdr_process_selection_request(clipboard, &event->xselectionrequest);
1758  break;
1759 
1760  case SelectionClear:
1761  log_selection_event(xfc, event);
1762  xf_cliprdr_process_selection_clear(clipboard, &event->xselectionclear);
1763  break;
1764 
1765  case PropertyNotify:
1766  log_selection_event(xfc, event);
1767  xf_cliprdr_process_property_notify(clipboard, &event->xproperty);
1768  break;
1769 
1770  case FocusIn:
1771  if (!clipboard->xfixes_supported)
1772  {
1773  xf_cliprdr_check_owner(clipboard);
1774  }
1775 
1776  break;
1777  default:
1778  break;
1779  }
1780 }
1781 
1787 static UINT xf_cliprdr_send_client_capabilities(xfClipboard* clipboard)
1788 {
1789  CLIPRDR_CAPABILITIES capabilities = { 0 };
1790  CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet = { 0 };
1791 
1792  WINPR_ASSERT(clipboard);
1793 
1794  capabilities.cCapabilitiesSets = 1;
1795  capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*)&(generalCapabilitySet);
1796  generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
1797  generalCapabilitySet.capabilitySetLength = 12;
1798  generalCapabilitySet.version = CB_CAPS_VERSION_2;
1799  generalCapabilitySet.generalFlags = CB_USE_LONG_FORMAT_NAMES;
1800 
1801  WINPR_ASSERT(clipboard);
1802  generalCapabilitySet.generalFlags |= cliprdr_file_context_current_flags(clipboard->file);
1803 
1804  WINPR_ASSERT(clipboard->context);
1805  WINPR_ASSERT(clipboard->context->ClientCapabilities);
1806  return clipboard->context->ClientCapabilities(clipboard->context, &capabilities);
1807 }
1808 
1814 static UINT xf_cliprdr_send_client_format_list(xfClipboard* clipboard, BOOL force)
1815 {
1816  WINPR_ASSERT(clipboard);
1817 
1818  xfContext* xfc = clipboard->xfc;
1819  WINPR_ASSERT(xfc);
1820 
1821  UINT32 numFormats = 0;
1822  CLIPRDR_FORMAT* formats = xf_cliprdr_get_client_formats(clipboard, &numFormats);
1823 
1824  const UINT ret = xf_cliprdr_send_format_list(clipboard, formats, numFormats, force);
1825 
1826  if (clipboard->owner && clipboard->owner != xfc->drawable)
1827  {
1828  /* Request the owner for TARGETS, and wait for SelectionNotify event */
1829  LogTagAndXConvertSelection(TAG, xfc->display, clipboard->clipboard_atom,
1830  clipboard->targets[1], clipboard->property_atom, xfc->drawable,
1831  CurrentTime);
1832  }
1833 
1834  xf_cliprdr_free_formats(formats, numFormats);
1835 
1836  return ret;
1837 }
1838 
1844 static UINT xf_cliprdr_send_client_format_list_response(xfClipboard* clipboard, BOOL status)
1845 {
1846  CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse = { 0 };
1847 
1848  formatListResponse.common.msgType = CB_FORMAT_LIST_RESPONSE;
1849  formatListResponse.common.msgFlags = status ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
1850  formatListResponse.common.dataLen = 0;
1851 
1852  WINPR_ASSERT(clipboard);
1853  WINPR_ASSERT(clipboard->context);
1854  WINPR_ASSERT(clipboard->context->ClientFormatListResponse);
1855  return clipboard->context->ClientFormatListResponse(clipboard->context, &formatListResponse);
1856 }
1857 
1863 static UINT xf_cliprdr_monitor_ready(CliprdrClientContext* context,
1864  const CLIPRDR_MONITOR_READY* monitorReady)
1865 {
1866  UINT ret = 0;
1867  xfClipboard* clipboard = NULL;
1868 
1869  WINPR_ASSERT(context);
1870  WINPR_ASSERT(monitorReady);
1871 
1872  clipboard = cliprdr_file_context_get_context(context->custom);
1873  WINPR_ASSERT(clipboard);
1874 
1875  WINPR_UNUSED(monitorReady);
1876 
1877  if ((ret = xf_cliprdr_send_client_capabilities(clipboard)) != CHANNEL_RC_OK)
1878  return ret;
1879 
1880  xf_clipboard_formats_free(clipboard);
1881 
1882  if ((ret = xf_cliprdr_send_client_format_list(clipboard, TRUE)) != CHANNEL_RC_OK)
1883  return ret;
1884 
1885  clipboard->sync = TRUE;
1886  return CHANNEL_RC_OK;
1887 }
1888 
1894 static UINT xf_cliprdr_server_capabilities(CliprdrClientContext* context,
1895  const CLIPRDR_CAPABILITIES* capabilities)
1896 {
1897  const CLIPRDR_GENERAL_CAPABILITY_SET* generalCaps = NULL;
1898  const BYTE* capsPtr = NULL;
1899  xfClipboard* clipboard = NULL;
1900 
1901  WINPR_ASSERT(context);
1902  WINPR_ASSERT(capabilities);
1903 
1904  clipboard = cliprdr_file_context_get_context(context->custom);
1905  WINPR_ASSERT(clipboard);
1906 
1907  capsPtr = (const BYTE*)capabilities->capabilitySets;
1908  WINPR_ASSERT(capsPtr);
1909 
1910  cliprdr_file_context_remote_set_flags(clipboard->file, 0);
1911 
1912  for (UINT32 i = 0; i < capabilities->cCapabilitiesSets; i++)
1913  {
1914  const CLIPRDR_CAPABILITY_SET* caps = (const CLIPRDR_CAPABILITY_SET*)capsPtr;
1915 
1916  if (caps->capabilitySetType == CB_CAPSTYPE_GENERAL)
1917  {
1918  generalCaps = (const CLIPRDR_GENERAL_CAPABILITY_SET*)caps;
1919 
1920  cliprdr_file_context_remote_set_flags(clipboard->file, generalCaps->generalFlags);
1921  }
1922 
1923  capsPtr += caps->capabilitySetLength;
1924  }
1925 
1926  return CHANNEL_RC_OK;
1927 }
1928 
1929 static void xf_cliprdr_prepare_to_set_selection_owner(xfContext* xfc, xfClipboard* clipboard)
1930 {
1931  WINPR_ASSERT(xfc);
1932  WINPR_ASSERT(clipboard);
1933  /*
1934  * When you're writing to the selection in response to a
1935  * normal X event like a mouse click or keyboard action, you
1936  * get the selection timestamp by copying the time field out
1937  * of that X event. Here, we're doing it on our own
1938  * initiative, so we have to _request_ the X server time.
1939  *
1940  * There isn't a GetServerTime request in the X protocol, so I
1941  * work around it by setting a property on our own window, and
1942  * waiting for a PropertyNotify event to come back telling me
1943  * it's been done - which will have a timestamp we can use.
1944  */
1945 
1946  /* We have to set the property to some value, but it doesn't
1947  * matter what. Set it to its own name, which we have here
1948  * anyway! */
1949  Atom value = clipboard->timestamp_property_atom;
1950 
1951  LogTagAndXChangeProperty(TAG, xfc->display, xfc->drawable, clipboard->timestamp_property_atom,
1952  XA_ATOM, 32, PropModeReplace, (BYTE*)&value, 1);
1953  XFlush(xfc->display);
1954 }
1955 
1956 static void xf_cliprdr_set_selection_owner(xfContext* xfc, xfClipboard* clipboard, Time timestamp)
1957 {
1958  WINPR_ASSERT(xfc);
1959  WINPR_ASSERT(clipboard);
1960  /*
1961  * Actually set ourselves up as the selection owner, now that
1962  * we have a timestamp to use.
1963  */
1964 
1965  clipboard->selection_ownership_timestamp = timestamp;
1966  XSetSelectionOwner(xfc->display, clipboard->clipboard_atom, xfc->drawable, timestamp);
1967  XFlush(xfc->display);
1968 }
1969 
1975 static UINT xf_cliprdr_server_format_list(CliprdrClientContext* context,
1976  const CLIPRDR_FORMAT_LIST* formatList)
1977 {
1978  xfContext* xfc = NULL;
1979  UINT ret = 0;
1980  xfClipboard* clipboard = NULL;
1981 
1982  WINPR_ASSERT(context);
1983  WINPR_ASSERT(formatList);
1984 
1985  clipboard = cliprdr_file_context_get_context(context->custom);
1986  WINPR_ASSERT(clipboard);
1987 
1988  xfc = clipboard->xfc;
1989  WINPR_ASSERT(xfc);
1990 
1991  /* Clear the active SelectionRequest, as it is now invalid */
1992  free(clipboard->respond);
1993  clipboard->respond = NULL;
1994 
1995  xf_clipboard_formats_free(clipboard);
1996  xf_cliprdr_clear_cached_data(clipboard);
1997  requested_format_free(&clipboard->requestedFormat);
1998 
1999  xf_clipboard_free_server_formats(clipboard);
2000 
2001  clipboard->numServerFormats = formatList->numFormats + 1; /* +1 for CF_RAW */
2002 
2003  if (!(clipboard->serverFormats =
2004  (CLIPRDR_FORMAT*)calloc(clipboard->numServerFormats, sizeof(CLIPRDR_FORMAT))))
2005  {
2006  WLog_ERR(TAG, "failed to allocate %d CLIPRDR_FORMAT structs", clipboard->numServerFormats);
2007  return CHANNEL_RC_NO_MEMORY;
2008  }
2009 
2010  for (size_t i = 0; i < formatList->numFormats; i++)
2011  {
2012  const CLIPRDR_FORMAT* format = &formatList->formats[i];
2013  CLIPRDR_FORMAT* srvFormat = &clipboard->serverFormats[i];
2014 
2015  srvFormat->formatId = format->formatId;
2016 
2017  if (format->formatName)
2018  {
2019  srvFormat->formatName = _strdup(format->formatName);
2020 
2021  if (!srvFormat->formatName)
2022  {
2023  for (UINT32 k = 0; k < i; k++)
2024  free(clipboard->serverFormats[k].formatName);
2025 
2026  clipboard->numServerFormats = 0;
2027  free(clipboard->serverFormats);
2028  clipboard->serverFormats = NULL;
2029  return CHANNEL_RC_NO_MEMORY;
2030  }
2031  }
2032  }
2033 
2034  ret = cliprdr_file_context_notify_new_server_format_list(clipboard->file);
2035  if (ret)
2036  return ret;
2037 
2038  /* CF_RAW is always implicitly supported by the server */
2039  {
2040  CLIPRDR_FORMAT* format = &clipboard->serverFormats[formatList->numFormats];
2041  format->formatId = CF_RAW;
2042  format->formatName = NULL;
2043  }
2044  xf_cliprdr_provide_server_format_list(clipboard);
2045  clipboard->numTargets = 2;
2046 
2047  for (size_t i = 0; i < formatList->numFormats; i++)
2048  {
2049  const CLIPRDR_FORMAT* format = &formatList->formats[i];
2050 
2051  for (size_t j = 0; j < clipboard->numClientFormats; j++)
2052  {
2053  const xfCliprdrFormat* clientFormat = &clipboard->clientFormats[j];
2054  if (xf_cliprdr_formats_equal(format, clientFormat))
2055  {
2056  if ((clientFormat->formatName != NULL) &&
2057  (strcmp(type_FileGroupDescriptorW, clientFormat->formatName) == 0))
2058  {
2059  if (!cliprdr_file_context_has_local_support(clipboard->file))
2060  continue;
2061  }
2062  xf_cliprdr_append_target(clipboard, clientFormat->atom);
2063  }
2064  }
2065  }
2066 
2067  ret = xf_cliprdr_send_client_format_list_response(clipboard, TRUE);
2068  if (xfc->remote_app)
2069  xf_cliprdr_set_selection_owner(xfc, clipboard, CurrentTime);
2070  else
2071  xf_cliprdr_prepare_to_set_selection_owner(xfc, clipboard);
2072  return ret;
2073 }
2074 
2080 static UINT
2081 xf_cliprdr_server_format_list_response(CliprdrClientContext* context,
2082  const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
2083 {
2084  WINPR_ASSERT(context);
2085  WINPR_ASSERT(formatListResponse);
2086  // xfClipboard* clipboard = (xfClipboard*) context->custom;
2087  return CHANNEL_RC_OK;
2088 }
2089 
2095 static UINT
2096 xf_cliprdr_server_format_data_request(CliprdrClientContext* context,
2097  const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
2098 {
2099  BOOL rawTransfer = 0;
2100  const xfCliprdrFormat* format = NULL;
2101  UINT32 formatId = 0;
2102  xfContext* xfc = NULL;
2103  xfClipboard* clipboard = NULL;
2104 
2105  WINPR_ASSERT(context);
2106  WINPR_ASSERT(formatDataRequest);
2107 
2108  clipboard = cliprdr_file_context_get_context(context->custom);
2109  WINPR_ASSERT(clipboard);
2110 
2111  xfc = clipboard->xfc;
2112  WINPR_ASSERT(xfc);
2113 
2114  formatId = formatDataRequest->requestedFormatId;
2115 
2116  rawTransfer = xf_cliprdr_is_raw_transfer_available(clipboard);
2117 
2118  if (rawTransfer)
2119  {
2120  format = xf_cliprdr_get_client_format_by_id(clipboard, CF_RAW);
2121  LogTagAndXChangeProperty(TAG, xfc->display, xfc->drawable, clipboard->property_atom,
2122  XA_INTEGER, 32, PropModeReplace, (BYTE*)&formatId, 1);
2123  }
2124  else
2125  format = xf_cliprdr_get_client_format_by_id(clipboard, formatId);
2126 
2127  clipboard->requestedFormatId = rawTransfer ? CF_RAW : formatId;
2128  if (!format)
2129  return xf_cliprdr_send_data_response(clipboard, format, NULL, 0);
2130 
2131  DEBUG_CLIPRDR("requested format 0x%08" PRIx32 " [%s] {local 0x%08" PRIx32 "} [%s]",
2132  format->formatToRequest, ClipboardGetFormatIdString(format->formatToRequest),
2133  format->localFormat, format->formatName);
2134  LogTagAndXConvertSelection(TAG, xfc->display, clipboard->clipboard_atom, format->atom,
2135  clipboard->property_atom, xfc->drawable, CurrentTime);
2136  XFlush(xfc->display);
2137  /* After this point, we expect a SelectionNotify event from the clipboard owner. */
2138  return CHANNEL_RC_OK;
2139 }
2140 
2146 static UINT
2147 xf_cliprdr_server_format_data_response(CliprdrClientContext* context,
2148  const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
2149 {
2150  BOOL bSuccess = 0;
2151  BYTE* pDstData = NULL;
2152  UINT32 DstSize = 0;
2153  UINT32 SrcSize = 0;
2154  UINT32 srcFormatId = 0;
2155  UINT32 dstFormatId = 0;
2156  BOOL nullTerminated = FALSE;
2157  UINT32 size = 0;
2158  const BYTE* data = NULL;
2159  xfContext* xfc = NULL;
2160  xfClipboard* clipboard = NULL;
2161  xfCachedData* cached_data = NULL;
2162 
2163  WINPR_ASSERT(context);
2164  WINPR_ASSERT(formatDataResponse);
2165 
2166  clipboard = cliprdr_file_context_get_context(context->custom);
2167  WINPR_ASSERT(clipboard);
2168 
2169  xfc = clipboard->xfc;
2170  WINPR_ASSERT(xfc);
2171 
2172  size = formatDataResponse->common.dataLen;
2173  data = formatDataResponse->requestedFormatData;
2174 
2175  if (formatDataResponse->common.msgFlags == CB_RESPONSE_FAIL)
2176  {
2177  WLog_WARN(TAG, "Format Data Response PDU msgFlags is CB_RESPONSE_FAIL");
2178  free(clipboard->respond);
2179  clipboard->respond = NULL;
2180  return CHANNEL_RC_OK;
2181  }
2182 
2183  if (!clipboard->respond)
2184  return CHANNEL_RC_OK;
2185 
2186  pDstData = NULL;
2187  DstSize = 0;
2188  srcFormatId = 0;
2189  dstFormatId = 0;
2190 
2191  const RequestedFormat* format = clipboard->requestedFormat;
2192  if (clipboard->data_raw_format)
2193  {
2194  srcFormatId = CF_RAW;
2195  dstFormatId = CF_RAW;
2196  }
2197  else if (!format)
2198  return ERROR_INTERNAL_ERROR;
2199  else if (format->formatName)
2200  {
2201  ClipboardLock(clipboard->system);
2202  if (strcmp(format->formatName, type_HtmlFormat) == 0)
2203  {
2204  srcFormatId = ClipboardGetFormatId(clipboard->system, type_HtmlFormat);
2205  dstFormatId = ClipboardGetFormatId(clipboard->system, mime_html);
2206  nullTerminated = TRUE;
2207  }
2208 
2209  if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0)
2210  {
2211  if (!cliprdr_file_context_update_server_data(clipboard->file, clipboard->system, data,
2212  size))
2213  WLog_WARN(TAG, "failed to update file descriptors");
2214 
2215  srcFormatId = ClipboardGetFormatId(clipboard->system, type_FileGroupDescriptorW);
2216  const xfCliprdrFormat* dstTargetFormat =
2217  xf_cliprdr_get_client_format_by_atom(clipboard, clipboard->respond->target);
2218  if (!dstTargetFormat)
2219  {
2220  dstFormatId = ClipboardGetFormatId(clipboard->system, mime_uri_list);
2221  }
2222  else
2223  {
2224  dstFormatId = dstTargetFormat->localFormat;
2225  }
2226 
2227  nullTerminated = TRUE;
2228  }
2229  ClipboardUnlock(clipboard->system);
2230  }
2231  else
2232  {
2233  srcFormatId = format->formatToRequest;
2234  dstFormatId = format->localFormat;
2235  switch (format->formatToRequest)
2236  {
2237  case CF_TEXT:
2238  nullTerminated = TRUE;
2239  break;
2240 
2241  case CF_OEMTEXT:
2242  nullTerminated = TRUE;
2243  break;
2244 
2245  case CF_UNICODETEXT:
2246  nullTerminated = TRUE;
2247  break;
2248 
2249  case CF_DIB:
2250  srcFormatId = CF_DIB;
2251  break;
2252 
2253  case CF_TIFF:
2254  srcFormatId = CF_TIFF;
2255  break;
2256 
2257  default:
2258  break;
2259  }
2260  }
2261 
2262  DEBUG_CLIPRDR("requested format 0x%08" PRIx32 " [%s] {local 0x%08" PRIx32 "} [%s]",
2263  format->formatToRequest, ClipboardGetFormatIdString(format->formatToRequest),
2264  format->localFormat, format->formatName);
2265  SrcSize = size;
2266 
2267  DEBUG_CLIPRDR("srcFormatId: %u, dstFormatId: %u", srcFormatId, dstFormatId);
2268 
2269  ClipboardLock(clipboard->system);
2270  bSuccess = ClipboardSetData(clipboard->system, srcFormatId, data, SrcSize);
2271 
2272  BOOL willQuit = FALSE;
2273  if (bSuccess)
2274  {
2275  if (SrcSize == 0)
2276  {
2277  WLog_DBG(TAG, "skipping, empty data detected!");
2278  free(clipboard->respond);
2279  clipboard->respond = NULL;
2280  willQuit = TRUE;
2281  }
2282  else
2283  {
2284  pDstData = (BYTE*)ClipboardGetData(clipboard->system, dstFormatId, &DstSize);
2285 
2286  if (!pDstData)
2287  {
2288  WLog_WARN(TAG, "failed to get clipboard data in format %s [source format %s]",
2289  ClipboardGetFormatName(clipboard->system, dstFormatId),
2290  ClipboardGetFormatName(clipboard->system, srcFormatId));
2291  }
2292 
2293  if (nullTerminated && pDstData)
2294  {
2295  BYTE* nullTerminator = memchr(pDstData, '\0', DstSize);
2296  if (nullTerminator)
2297  {
2298  const intptr_t diff = nullTerminator - pDstData;
2299  WINPR_ASSERT(diff >= 0);
2300  WINPR_ASSERT(diff <= UINT32_MAX);
2301  DstSize = (UINT32)diff;
2302  }
2303  }
2304  }
2305  }
2306  ClipboardUnlock(clipboard->system);
2307  if (willQuit)
2308  return CHANNEL_RC_OK;
2309 
2310  /* Cache converted and original data to avoid doing a possibly costly
2311  * conversion again on subsequent requests */
2312  if (pDstData)
2313  {
2314  cached_data = xf_cached_data_new(pDstData, DstSize);
2315  if (!cached_data)
2316  {
2317  WLog_WARN(TAG, "Failed to allocate cache entry");
2318  free(pDstData);
2319  return CHANNEL_RC_OK;
2320  }
2321  if (!HashTable_Insert(clipboard->cachedData, format_to_cache_slot(dstFormatId),
2322  cached_data))
2323  {
2324  WLog_WARN(TAG, "Failed to cache clipboard data");
2325  xf_cached_data_free(cached_data);
2326  return CHANNEL_RC_OK;
2327  }
2328  }
2329 
2330  /* We have to copy the original data again, as pSrcData is now owned
2331  * by clipboard->system. Memory allocation failure is not fatal here
2332  * as this is only a cached value. */
2333  {
2334  // clipboard->cachedData owns cached_data
2335  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc
2336  xfCachedData* cached_raw_data = xf_cached_data_new_copy(data, size);
2337  if (!cached_raw_data)
2338  WLog_WARN(TAG, "Failed to allocate cache entry");
2339  else
2340  {
2341  if (!HashTable_Insert(clipboard->cachedRawData, (void*)(UINT_PTR)srcFormatId,
2342  cached_raw_data))
2343  {
2344  WLog_WARN(TAG, "Failed to cache clipboard data");
2345  xf_cached_data_free(cached_raw_data);
2346  }
2347  }
2348  }
2349 
2350  // clipboard->cachedRawData owns cached_raw_data
2351  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
2352  xf_cliprdr_provide_data(clipboard, clipboard->respond, pDstData, DstSize);
2353  {
2354  union
2355  {
2356  XEvent* ev;
2357  XSelectionEvent* sev;
2358  } conv;
2359 
2360  conv.sev = clipboard->respond;
2361 
2362  XSendEvent(xfc->display, clipboard->respond->requestor, 0, 0, conv.ev);
2363  XFlush(xfc->display);
2364  }
2365  free(clipboard->respond);
2366  clipboard->respond = NULL;
2367  return CHANNEL_RC_OK;
2368 }
2369 
2370 static BOOL xf_cliprdr_is_valid_unix_filename(LPCWSTR filename)
2371 {
2372  if (!filename)
2373  return FALSE;
2374 
2375  if (filename[0] == L'\0')
2376  return FALSE;
2377 
2378  /* Reserved characters */
2379  for (const WCHAR* c = filename; *c; ++c)
2380  {
2381  if (*c == L'/')
2382  return FALSE;
2383  }
2384 
2385  return TRUE;
2386 }
2387 
2388 xfClipboard* xf_clipboard_new(xfContext* xfc, BOOL relieveFilenameRestriction)
2389 {
2390  int n = 0;
2391  rdpChannels* channels = NULL;
2392  xfClipboard* clipboard = NULL;
2393  const char* selectionAtom = NULL;
2394  xfCliprdrFormat* clientFormat = NULL;
2395  wObject* obj = NULL;
2396 
2397  WINPR_ASSERT(xfc);
2398  WINPR_ASSERT(xfc->common.context.settings);
2399 
2400  if (!(clipboard = (xfClipboard*)calloc(1, sizeof(xfClipboard))))
2401  {
2402  WLog_ERR(TAG, "failed to allocate xfClipboard data");
2403  return NULL;
2404  }
2405 
2406  clipboard->file = cliprdr_file_context_new(clipboard);
2407  if (!clipboard->file)
2408  goto fail;
2409 
2410  xfc->clipboard = clipboard;
2411  clipboard->xfc = xfc;
2412  channels = xfc->common.context.channels;
2413  clipboard->channels = channels;
2414  clipboard->system = ClipboardCreate();
2415  clipboard->requestedFormatId = -1;
2416  clipboard->root_window = DefaultRootWindow(xfc->display);
2417 
2418  selectionAtom =
2419  freerdp_settings_get_string(xfc->common.context.settings, FreeRDP_ClipboardUseSelection);
2420  if (!selectionAtom)
2421  selectionAtom = "CLIPBOARD";
2422 
2423  clipboard->clipboard_atom = Logging_XInternAtom(xfc->log, xfc->display, selectionAtom, FALSE);
2424 
2425  if (clipboard->clipboard_atom == None)
2426  {
2427  WLog_ERR(TAG, "unable to get %s atom", selectionAtom);
2428  goto fail;
2429  }
2430 
2431  clipboard->timestamp_property_atom =
2432  Logging_XInternAtom(xfc->log, xfc->display, "_FREERDP_TIMESTAMP_PROPERTY", FALSE);
2433  clipboard->property_atom =
2434  Logging_XInternAtom(xfc->log, xfc->display, "_FREERDP_CLIPRDR", FALSE);
2435  clipboard->raw_transfer_atom =
2436  Logging_XInternAtom(xfc->log, xfc->display, "_FREERDP_CLIPRDR_RAW", FALSE);
2437  clipboard->raw_format_list_atom =
2438  Logging_XInternAtom(xfc->log, xfc->display, "_FREERDP_CLIPRDR_FORMATS", FALSE);
2439  xf_cliprdr_set_raw_transfer_enabled(clipboard, TRUE);
2440  XSelectInput(xfc->display, clipboard->root_window, PropertyChangeMask);
2441 #ifdef WITH_XFIXES
2442 
2443  if (XFixesQueryExtension(xfc->display, &clipboard->xfixes_event_base,
2444  &clipboard->xfixes_error_base))
2445  {
2446  int xfmajor = 0;
2447  int xfminor = 0;
2448 
2449  if (XFixesQueryVersion(xfc->display, &xfmajor, &xfminor))
2450  {
2451  XFixesSelectSelectionInput(xfc->display, clipboard->root_window,
2452  clipboard->clipboard_atom,
2453  XFixesSetSelectionOwnerNotifyMask);
2454  clipboard->xfixes_supported = TRUE;
2455  }
2456  else
2457  {
2458  WLog_ERR(TAG, "Error querying X Fixes extension version");
2459  }
2460  }
2461  else
2462  {
2463  WLog_ERR(TAG, "Error loading X Fixes extension");
2464  }
2465 
2466 #else
2467  WLog_ERR(
2468  TAG,
2469  "Warning: Using clipboard redirection without XFIXES extension is strongly discouraged!");
2470 #endif
2471  clientFormat = &clipboard->clientFormats[n++];
2472  clientFormat->atom = Logging_XInternAtom(xfc->log, xfc->display, "_FREERDP_RAW", False);
2473  clientFormat->localFormat = clientFormat->formatToRequest = CF_RAW;
2474 
2475  clientFormat = &clipboard->clientFormats[n++];
2476  clientFormat->atom = Logging_XInternAtom(xfc->log, xfc->display, "UTF8_STRING", False);
2477  clientFormat->formatToRequest = CF_UNICODETEXT;
2478  clientFormat->localFormat = ClipboardGetFormatId(xfc->clipboard->system, mime_text_plain);
2479 
2480  clientFormat = &clipboard->clientFormats[n++];
2481  clientFormat->atom = XA_STRING;
2482  clientFormat->formatToRequest = CF_TEXT;
2483  clientFormat->localFormat = ClipboardGetFormatId(xfc->clipboard->system, mime_text_plain);
2484 
2485  clientFormat = &clipboard->clientFormats[n++];
2486  clientFormat->atom = Logging_XInternAtom(xfc->log, xfc->display, mime_tiff, False);
2487  clientFormat->formatToRequest = clientFormat->localFormat = CF_TIFF;
2488 
2489  for (size_t x = 0; x < ARRAYSIZE(mime_bitmap); x++)
2490  {
2491  const char* mime_bmp = mime_bitmap[x];
2492  const DWORD format = ClipboardGetFormatId(xfc->clipboard->system, mime_bmp);
2493  if (format == 0)
2494  {
2495  WLog_DBG(TAG, "skipping local bitmap format %s [NOT SUPPORTED]", mime_bmp);
2496  continue;
2497  }
2498 
2499  WLog_DBG(TAG, "register local bitmap format %s [0x%08" PRIx32 "]", mime_bmp, format);
2500  clientFormat = &clipboard->clientFormats[n++];
2501  clientFormat->localFormat = format;
2502  clientFormat->atom = Logging_XInternAtom(xfc->log, xfc->display, mime_bmp, False);
2503  clientFormat->formatToRequest = CF_DIB;
2504  }
2505 
2506  for (size_t x = 0; x < ARRAYSIZE(mime_images); x++)
2507  {
2508  const char* mime_bmp = mime_images[x];
2509  const DWORD format = ClipboardGetFormatId(xfc->clipboard->system, mime_bmp);
2510  if (format == 0)
2511  {
2512  WLog_DBG(TAG, "skipping local bitmap format %s [NOT SUPPORTED]", mime_bmp);
2513  continue;
2514  }
2515 
2516  WLog_DBG(TAG, "register local bitmap format %s [0x%08" PRIx32 "]", mime_bmp, format);
2517  clientFormat = &clipboard->clientFormats[n++];
2518  clientFormat->localFormat = format;
2519  clientFormat->atom = Logging_XInternAtom(xfc->log, xfc->display, mime_bmp, False);
2520  clientFormat->formatToRequest = CF_DIB;
2521  }
2522 
2523  clientFormat = &clipboard->clientFormats[n++];
2524  clientFormat->atom = Logging_XInternAtom(xfc->log, xfc->display, mime_html, False);
2525  clientFormat->formatToRequest = ClipboardGetFormatId(xfc->clipboard->system, type_HtmlFormat);
2526  clientFormat->localFormat = ClipboardGetFormatId(xfc->clipboard->system, mime_html);
2527  clientFormat->formatName = _strdup(type_HtmlFormat);
2528 
2529  if (!clientFormat->formatName)
2530  goto fail;
2531 
2532  clientFormat = &clipboard->clientFormats[n++];
2533 
2534  /*
2535  * Existence of registered format IDs for file formats does not guarantee that they are
2536  * in fact supported by wClipboard (as further initialization may have failed after format
2537  * registration). However, they are definitely not supported if there are no registered
2538  * formats. In this case we should not list file formats in TARGETS.
2539  */
2540  const UINT32 fgid = ClipboardGetFormatId(clipboard->system, type_FileGroupDescriptorW);
2541  const UINT32 uid = ClipboardGetFormatId(clipboard->system, mime_uri_list);
2542  if (uid)
2543  {
2544  cliprdr_file_context_set_locally_available(clipboard->file, TRUE);
2545  clientFormat->atom = Logging_XInternAtom(xfc->log, xfc->display, mime_uri_list, False);
2546  clientFormat->localFormat = uid;
2547  clientFormat->formatToRequest = fgid;
2548  clientFormat->formatName = _strdup(type_FileGroupDescriptorW);
2549 
2550  if (!clientFormat->formatName)
2551  goto fail;
2552 
2553  clientFormat = &clipboard->clientFormats[n++];
2554  }
2555 
2556  const UINT32 gid = ClipboardGetFormatId(clipboard->system, mime_gnome_copied_files);
2557  if (gid != 0)
2558  {
2559  cliprdr_file_context_set_locally_available(clipboard->file, TRUE);
2560  clientFormat->atom =
2561  Logging_XInternAtom(xfc->log, xfc->display, mime_gnome_copied_files, False);
2562  clientFormat->localFormat = gid;
2563  clientFormat->formatToRequest = fgid;
2564  clientFormat->formatName = _strdup(type_FileGroupDescriptorW);
2565 
2566  if (!clientFormat->formatName)
2567  goto fail;
2568 
2569  clientFormat = &clipboard->clientFormats[n++];
2570  }
2571 
2572  const UINT32 mid = ClipboardGetFormatId(clipboard->system, mime_mate_copied_files);
2573  if (mid != 0)
2574  {
2575  cliprdr_file_context_set_locally_available(clipboard->file, TRUE);
2576  clientFormat->atom =
2577  Logging_XInternAtom(xfc->log, xfc->display, mime_mate_copied_files, False);
2578  clientFormat->localFormat = mid;
2579  clientFormat->formatToRequest = fgid;
2580  clientFormat->formatName = _strdup(type_FileGroupDescriptorW);
2581 
2582  if (!clientFormat->formatName)
2583  goto fail;
2584  }
2585 
2586  clipboard->numClientFormats = n;
2587  clipboard->targets[0] = Logging_XInternAtom(xfc->log, xfc->display, "TIMESTAMP", FALSE);
2588  clipboard->targets[1] = Logging_XInternAtom(xfc->log, xfc->display, "TARGETS", FALSE);
2589  clipboard->numTargets = 2;
2590  clipboard->incr_atom = Logging_XInternAtom(xfc->log, xfc->display, "INCR", FALSE);
2591 
2592  if (relieveFilenameRestriction)
2593  {
2594  WLog_DBG(TAG, "Relieving CLIPRDR filename restriction");
2595  ClipboardGetDelegate(clipboard->system)->IsFileNameComponentValid =
2596  xf_cliprdr_is_valid_unix_filename;
2597  }
2598 
2599  clipboard->cachedData = HashTable_New(TRUE);
2600  if (!clipboard->cachedData)
2601  goto fail;
2602 
2603  obj = HashTable_ValueObject(clipboard->cachedData);
2604  obj->fnObjectFree = xf_cached_data_free;
2605 
2606  clipboard->cachedRawData = HashTable_New(TRUE);
2607  if (!clipboard->cachedRawData)
2608  goto fail;
2609 
2610  obj = HashTable_ValueObject(clipboard->cachedRawData);
2611  obj->fnObjectFree = xf_cached_data_free;
2612 
2613  return clipboard;
2614 
2615 fail:
2616  WINPR_PRAGMA_DIAG_PUSH
2617  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
2618  xf_clipboard_free(clipboard);
2619  WINPR_PRAGMA_DIAG_POP
2620  return NULL;
2621 }
2622 
2623 void xf_clipboard_free(xfClipboard* clipboard)
2624 {
2625  if (!clipboard)
2626  return;
2627 
2628  xf_clipboard_free_server_formats(clipboard);
2629 
2630  if (clipboard->numClientFormats)
2631  {
2632  for (UINT32 i = 0; i < clipboard->numClientFormats; i++)
2633  {
2634  xfCliprdrFormat* format = &clipboard->clientFormats[i];
2635  free(format->formatName);
2636  }
2637  }
2638 
2639  cliprdr_file_context_free(clipboard->file);
2640 
2641  ClipboardDestroy(clipboard->system);
2642  xf_clipboard_formats_free(clipboard);
2643  HashTable_Free(clipboard->cachedRawData);
2644  HashTable_Free(clipboard->cachedData);
2645  requested_format_free(&clipboard->requestedFormat);
2646  free(clipboard->respond);
2647  free(clipboard->incr_data);
2648  free(clipboard);
2649 }
2650 
2651 void xf_cliprdr_init(xfContext* xfc, CliprdrClientContext* cliprdr)
2652 {
2653  WINPR_ASSERT(xfc);
2654  WINPR_ASSERT(cliprdr);
2655 
2656  xfc->cliprdr = cliprdr;
2657  xfc->clipboard->context = cliprdr;
2658 
2659  cliprdr->MonitorReady = xf_cliprdr_monitor_ready;
2660  cliprdr->ServerCapabilities = xf_cliprdr_server_capabilities;
2661  cliprdr->ServerFormatList = xf_cliprdr_server_format_list;
2662  cliprdr->ServerFormatListResponse = xf_cliprdr_server_format_list_response;
2663  cliprdr->ServerFormatDataRequest = xf_cliprdr_server_format_data_request;
2664  cliprdr->ServerFormatDataResponse = xf_cliprdr_server_format_data_response;
2665 
2666  cliprdr_file_context_init(xfc->clipboard->file, cliprdr);
2667 }
2668 
2669 void xf_cliprdr_uninit(xfContext* xfc, CliprdrClientContext* cliprdr)
2670 {
2671  WINPR_ASSERT(xfc);
2672  WINPR_ASSERT(cliprdr);
2673 
2674  xfc->cliprdr = NULL;
2675 
2676  if (xfc->clipboard)
2677  {
2678  cliprdr_file_context_uninit(xfc->clipboard->file, cliprdr);
2679  xfc->clipboard->context = NULL;
2680  }
2681 }
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
This struct contains function pointer to initialize/free objects.
Definition: collections.h:57