40#include <winpr/assert.h>
41#include <winpr/cast.h>
42#include <winpr/clipboard.h>
43#include <winpr/collections.h>
44#include <winpr/image.h>
45#include <winpr/synch.h>
46#include <winpr/sysinfo.h>
47#include <winpr/user.h>
49#include <freerdp/log.h>
52#include "shadow_cliprdr.h"
54#define TAG SERVER_TAG("shadow.clipboard")
56#if defined(CHANNEL_CLIPRDR_SERVER)
59#define SHADOW_CLIPBOARD_LIST_WAIT_MS 1000
62#define SHADOW_CLIPBOARD_FETCH_TIMEOUT_MS 10000
64#define SHADOW_CLIPBOARD_MIME_HTML "text/html"
65#define SHADOW_CLIPBOARD_FORMAT_HTML "HTML Format"
68#define SHADOW_CLIPBOARD_MAX_OFFER 16
72 rdpShadowClient* client;
79 rdpShadowClient* client;
87 rdpShadowClient* client;
95} ShadowClipboardFetch;
97struct rdp_shadow_clipboard
99 rdpShadowServer* server;
108 size_t nSessionMimes;
111 rdpShadowClient* owner;
113 UINT32 nOwnerFormats;
121static const char*
const shadow_clipboard_text_mimes[] = {
"text/plain;charset=utf-8",
122 "UTF8_STRING",
"text/plain" };
124static const UINT32 shadow_clipboard_image_formats[] = { WINPR_IMAGE_PNG, WINPR_IMAGE_BITMAP,
125 WINPR_IMAGE_WEBP, WINPR_IMAGE_JPEG };
127static void shadow_clipboard_read_free(
void* obj)
129 ShadowClipboardRead* read = obj;
136static void shadow_clipboard_formats_free(
CLIPRDR_FORMAT* formats, UINT32 count)
140 for (UINT32 x = 0; x < count; x++)
141 free(formats[x].formatName);
145static void shadow_clipboard_mimes_free(
char** mimes,
size_t count)
149 for (
size_t x = 0; x < count; x++)
155static rdpShadowSubsystem* shadow_clipboard_subsystem(
const rdpShadowClipboard* clipboard)
157 WINPR_ASSERT(clipboard);
158 WINPR_ASSERT(clipboard->server);
159 return clipboard->server->subsystem;
163static ShadowClipboardPeer* shadow_clipboard_peer(rdpShadowClipboard* clipboard,
164 const rdpShadowClient* client)
166 WINPR_ASSERT(clipboard);
168 const size_t count = ArrayList_Count(clipboard->peers);
169 for (
size_t x = 0; x < count; x++)
171 ShadowClipboardPeer* peer = ArrayList_GetItem(clipboard->peers, x);
172 if (peer && (peer->client == client))
179static BOOL shadow_clipboard_session_has(
const rdpShadowClipboard* clipboard,
const char* mime)
181 WINPR_ASSERT(clipboard);
184 for (
size_t x = 0; x < clipboard->nSessionMimes; x++)
186 if (strcmp(clipboard->sessionMimes[x], mime) == 0)
194static const char* shadow_clipboard_session_mime(
const rdpShadowClipboard* clipboard,
197 WINPR_ASSERT(clipboard);
199 if ((formatId == CF_UNICODETEXT) || (formatId == CF_TEXT) || (formatId == CF_OEMTEXT))
201 for (
size_t x = 0; x < ARRAYSIZE(shadow_clipboard_text_mimes); x++)
203 if (shadow_clipboard_session_has(clipboard, shadow_clipboard_text_mimes[x]))
204 return shadow_clipboard_text_mimes[x];
207 else if ((formatId == CF_DIB) || (formatId == CF_DIBV5))
209 for (
size_t x = 0; x < ARRAYSIZE(shadow_clipboard_image_formats); x++)
211 const UINT32 image = shadow_clipboard_image_formats[x];
212 const char* mime = winpr_image_format_mime(image);
213 if (mime && winpr_image_format_is_supported(image) &&
214 shadow_clipboard_session_has(clipboard, mime))
218 else if ((formatId == clipboard->htmlId) &&
219 shadow_clipboard_session_has(clipboard, SHADOW_CLIPBOARD_MIME_HTML))
220 return SHADOW_CLIPBOARD_MIME_HTML;
226static UINT shadow_clipboard_send_list(
const rdpShadowClipboard* clipboard, rdpShadowClient* client)
228 WINPR_ASSERT(clipboard);
229 WINPR_ASSERT(client);
230 WINPR_ASSERT(client->cliprdr);
232 char htmlName[] = SHADOW_CLIPBOARD_FORMAT_HTML;
236 if (shadow_clipboard_session_mime(clipboard, CF_UNICODETEXT))
237 formats[count++].formatId = CF_UNICODETEXT;
238 if (shadow_clipboard_session_mime(clipboard, clipboard->htmlId))
240 formats[count].formatId = clipboard->htmlId;
241 formats[count++].formatName = htmlName;
243 if (shadow_clipboard_session_mime(clipboard, CF_DIB))
244 formats[count++].formatId = CF_DIB;
247 list.common.msgType = CB_FORMAT_LIST;
248 list.numFormats = count;
249 list.formats = formats;
250 return client->cliprdr->ServerFormatList(client->cliprdr, &list);
254static void shadow_clipboard_announce(rdpShadowClipboard* clipboard,
const rdpShadowClient* except)
256 WINPR_ASSERT(clipboard);
258 const size_t count = ArrayList_Count(clipboard->peers);
259 for (
size_t x = 0; x < count; x++)
261 const ShadowClipboardPeer* peer = ArrayList_GetItem(clipboard->peers, x);
262 if (!peer || !peer->ready || (peer->client == except) || !peer->client->mayInteract)
265 const UINT rc = shadow_clipboard_send_list(clipboard, peer->client);
266 if (rc != CHANNEL_RC_OK)
267 WLog_WARN(TAG,
"ServerFormatList failed with %" PRIu32, rc);
273static BYTE* shadow_clipboard_convert(rdpShadowClipboard* clipboard, UINT32 sourceId,
274 UINT32 targetId,
const BYTE* data,
size_t size,
277 WINPR_ASSERT(clipboard);
278 WINPR_ASSERT(pConverted);
281 if (!data || (size == 0) || (size > SHADOW_CLIPBOARD_MAX_SIZE))
284 if (!ClipboardEmpty(clipboard->convert))
286 const BOOL stored = ClipboardSetData(clipboard->convert, sourceId, data,
287 WINPR_ASSERTING_INT_CAST(UINT32, size));
290 return ClipboardGetData(clipboard->convert, targetId, pConverted);
293static void shadow_clipboard_respond(CliprdrServerContext* cliprdr,
const BYTE* data, UINT32 size)
295 WINPR_ASSERT(cliprdr);
298 response.common.msgType = CB_FORMAT_DATA_RESPONSE;
299 response.common.msgFlags = data ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
300 response.common.dataLen = data ? size : 0;
301 response.requestedFormatData = data;
303 const UINT rc = cliprdr->ServerFormatDataResponse(cliprdr, &response);
304 if (rc != CHANNEL_RC_OK)
305 WLog_WARN(TAG,
"ServerFormatDataResponse failed with %" PRIu32, rc);
309static void shadow_clipboard_read_next(rdpShadowClipboard* clipboard)
311 WINPR_ASSERT(clipboard);
313 while (!clipboard->readBusy && (ArrayList_Count(clipboard->reads) > 0))
315 ShadowClipboardRead* read = ArrayList_GetItem(clipboard->reads, 0);
318 rdpShadowSubsystem* subsystem = shadow_clipboard_subsystem(clipboard);
319 if (read->client && subsystem->ClipboardRead(subsystem, read->mime))
321 clipboard->readBusy = TRUE;
326 shadow_clipboard_respond(read->client->cliprdr,
nullptr, 0);
327 if (!ArrayList_RemoveAt(clipboard->reads, 0))
332static void shadow_clipboard_provide(rdpShadowClipboard* clipboard, UINT32 request,
333 const BYTE* data,
size_t size)
335 WINPR_ASSERT(clipboard);
337 rdpShadowSubsystem* subsystem = shadow_clipboard_subsystem(clipboard);
338 if (!subsystem->ClipboardProvide(subsystem, request, data, size))
339 WLog_WARN(TAG,
"the subsystem did not take the data of request %" PRIu32, request);
343static void shadow_clipboard_fetch_next(rdpShadowClipboard* clipboard)
345 WINPR_ASSERT(clipboard);
347 while (!clipboard->fetchBusy && (ArrayList_Count(clipboard->fetches) > 0))
349 ShadowClipboardFetch* fetch = ArrayList_GetItem(clipboard->fetches, 0);
351 WINPR_ASSERT(fetch->client);
352 WINPR_ASSERT(fetch->client->cliprdr);
355 request.common.msgType = CB_FORMAT_DATA_REQUEST;
356 request.common.dataLen = 4;
357 request.requestedFormatId = fetch->formatId;
359 CliprdrServerContext* cliprdr = fetch->client->cliprdr;
360 const UINT rc = cliprdr->ServerFormatDataRequest(cliprdr, &request);
361 if (rc == CHANNEL_RC_OK)
363 fetch->sent = GetTickCount64();
364 clipboard->fetchBusy = TRUE;
368 WLog_WARN(TAG,
"ServerFormatDataRequest failed with %" PRIu32, rc);
369 shadow_clipboard_provide(clipboard, fetch->request,
nullptr, 0);
370 if (!ArrayList_RemoveAt(clipboard->fetches, 0))
377static void shadow_clipboard_cancel_fetches(rdpShadowClipboard* clipboard,
378 const rdpShadowClient* gone)
380 WINPR_ASSERT(clipboard);
382 for (
size_t x = ArrayList_Count(clipboard->fetches); x > 0; x--)
384 const size_t index = x - 1;
385 ShadowClipboardFetch* fetch = ArrayList_GetItem(clipboard->fetches, index);
388 const BOOL atClient = (index == 0) && clipboard->fetchBusy;
389 if (atClient && (fetch->client != gone))
391 if (!fetch->canceled)
392 shadow_clipboard_provide(clipboard, fetch->request,
nullptr, 0);
393 fetch->canceled = TRUE;
398 clipboard->fetchBusy = FALSE;
399 if (!fetch->canceled)
400 shadow_clipboard_provide(clipboard, fetch->request,
nullptr, 0);
401 if (!ArrayList_RemoveAt(clipboard->fetches, index))
402 WLog_WARN(TAG,
"cannot drop a clipboard request");
406static void shadow_clipboard_clear_owner(rdpShadowClipboard* clipboard)
408 WINPR_ASSERT(clipboard);
410 shadow_clipboard_formats_free(clipboard->ownerFormats, clipboard->nOwnerFormats);
411 clipboard->ownerFormats =
nullptr;
412 clipboard->nOwnerFormats = 0;
413 clipboard->owner =
nullptr;
417static void shadow_clipboard_release_owner(rdpShadowClipboard* clipboard)
419 WINPR_ASSERT(clipboard);
421 shadow_clipboard_clear_owner(clipboard);
423 rdpShadowSubsystem* subsystem = shadow_clipboard_subsystem(clipboard);
424 if (!subsystem->ClipboardOffer(subsystem,
nullptr, 0))
425 WLog_WARN(TAG,
"the session keeps a clipboard nobody serves");
430 UINT32 count, UINT32 formatId,
433 for (UINT32 x = 0; x < count; x++)
438 if (format->formatName && (strcmp(format->formatName, name) == 0))
441 else if (format->formatId == formatId)
451 const UINT32 ids[] = { CF_UNICODETEXT, CF_TEXT, CF_OEMTEXT };
452 for (
size_t x = 0; x < ARRAYSIZE(ids); x++)
455 shadow_clipboard_find_format(formats, count, ids[x],
nullptr);
466 const CLIPRDR_FORMAT* format = shadow_clipboard_find_format(formats, count, CF_DIB,
nullptr);
469 return shadow_clipboard_find_format(formats, count, CF_DIBV5,
nullptr);
474static size_t shadow_clipboard_list_mimes(
const CLIPRDR_FORMAT* formats, UINT32 count,
475 const char* mimes[SHADOW_CLIPBOARD_MAX_OFFER])
479 if (shadow_clipboard_owner_text(formats, count))
481 for (
size_t x = 0; x < ARRAYSIZE(shadow_clipboard_text_mimes); x++)
482 mimes[offered++] = shadow_clipboard_text_mimes[x];
485 if (shadow_clipboard_find_format(formats, count, 0, SHADOW_CLIPBOARD_FORMAT_HTML))
486 mimes[offered++] = SHADOW_CLIPBOARD_MIME_HTML;
488 if (shadow_clipboard_owner_image(formats, count))
490 for (
size_t x = 0; x < ARRAYSIZE(shadow_clipboard_image_formats); x++)
492 const UINT32 image = shadow_clipboard_image_formats[x];
493 const char* mime = winpr_image_format_mime(image);
494 if (mime && winpr_image_format_is_supported(image))
495 mimes[offered++] = mime;
499 WINPR_ASSERT(offered <= SHADOW_CLIPBOARD_MAX_OFFER);
504static BOOL shadow_clipboard_set_owner(rdpShadowClipboard* clipboard, rdpShadowClient* client,
508 WINPR_ASSERT(clipboard);
509 WINPR_ASSERT(client);
516 for (UINT32 x = 0; x < list->numFormats; x++)
519 formats[x].formatId = format->formatId;
520 if (format->formatName)
522 formats[x].formatName = _strdup(format->formatName);
523 if (!formats[x].formatName)
525 shadow_clipboard_formats_free(formats, list->numFormats);
531 shadow_clipboard_cancel_fetches(clipboard,
nullptr);
532 shadow_clipboard_clear_owner(clipboard);
533 clipboard->owner = client;
534 clipboard->ownerFormats = formats;
535 clipboard->nOwnerFormats = list->numFormats;
537 shadow_clipboard_mimes_free(clipboard->sessionMimes, clipboard->nSessionMimes);
538 clipboard->sessionMimes =
nullptr;
539 clipboard->nSessionMimes = 0;
541 shadow_clipboard_announce(clipboard, client);
543 rdpShadowSubsystem* subsystem = shadow_clipboard_subsystem(clipboard);
544 return subsystem->ClipboardOffer(subsystem, mimes, count);
548static UINT shadow_clipboard_client_format_list(CliprdrServerContext* context,
551 WINPR_ASSERT(context);
554 rdpShadowClient* client = context->custom;
555 WINPR_ASSERT(client);
558 response.common.msgType = CB_FORMAT_LIST_RESPONSE;
559 response.common.msgFlags = CB_RESPONSE_OK;
560 const UINT rc = context->ServerFormatListResponse(context, &response);
561 if (rc != CHANNEL_RC_OK)
564 rdpShadowClipboard* clipboard = client->server->clipboard;
565 WINPR_ASSERT(clipboard);
567 EnterCriticalSection(&clipboard->lock);
568 ShadowClipboardPeer* peer = shadow_clipboard_peer(clipboard, client);
569 if (peer && client->mayInteract)
571 const BOOL first = !peer->ready;
574 const char* mimes[SHADOW_CLIPBOARD_MAX_OFFER] = WINPR_C_ARRAY_INIT;
575 const size_t count = shadow_clipboard_list_mimes(list->formats, list->numFormats, mimes);
578 if (!shadow_clipboard_set_owner(clipboard, client, list, mimes, count))
579 WLog_WARN(TAG,
"the session did not take the client's clipboard");
581 else if (clipboard->owner == client)
584 shadow_clipboard_cancel_fetches(clipboard,
nullptr);
585 shadow_clipboard_release_owner(clipboard);
587 else if (first && (clipboard->nSessionMimes > 0))
590 const UINT sent = shadow_clipboard_send_list(clipboard, client);
591 if (sent != CHANNEL_RC_OK)
592 WLog_WARN(TAG,
"ServerFormatList failed with %" PRIu32, sent);
595 LeaveCriticalSection(&clipboard->lock);
596 return CHANNEL_RC_OK;
605static UINT shadow_clipboard_client_capabilities(CliprdrServerContext* context,
608 WINPR_ASSERT(context);
610 rdpShadowClient* client = context->custom;
611 WINPR_ASSERT(client);
612 rdpShadowClipboard* clipboard = client->server->clipboard;
613 WINPR_ASSERT(clipboard);
616 WaitForSingleObject(context->GetEventHandle(context), SHADOW_CLIPBOARD_LIST_WAIT_MS);
617 if (waited != WAIT_TIMEOUT)
618 return CHANNEL_RC_OK;
620 EnterCriticalSection(&clipboard->lock);
621 ShadowClipboardPeer* peer = shadow_clipboard_peer(clipboard, client);
622 if (peer && !peer->ready && client->mayInteract)
625 if (clipboard->nSessionMimes > 0)
627 const UINT sent = shadow_clipboard_send_list(clipboard, client);
628 if (sent != CHANNEL_RC_OK)
629 WLog_WARN(TAG,
"ServerFormatList failed with %" PRIu32, sent);
632 LeaveCriticalSection(&clipboard->lock);
633 return CHANNEL_RC_OK;
637static UINT shadow_clipboard_client_data_request(CliprdrServerContext* context,
640 WINPR_ASSERT(context);
641 WINPR_ASSERT(request);
643 rdpShadowClient* client = context->custom;
644 WINPR_ASSERT(client);
645 rdpShadowClipboard* clipboard = client->server->clipboard;
646 WINPR_ASSERT(clipboard);
649 EnterCriticalSection(&clipboard->lock);
650 const char* mime = shadow_clipboard_session_mime(clipboard, request->requestedFormatId);
651 if (mime && client->mayInteract && shadow_clipboard_peer(clipboard, client))
653 ShadowClipboardRead* read = calloc(1,
sizeof(ShadowClipboardRead));
656 read->client = client;
657 read->formatId = request->requestedFormatId;
658 read->mime = _strdup(mime);
659 queued = read->mime && ArrayList_Append(clipboard->reads, read);
661 shadow_clipboard_read_free(read);
665 shadow_clipboard_read_next(clipboard);
666 LeaveCriticalSection(&clipboard->lock);
669 shadow_clipboard_respond(context,
nullptr, 0);
670 return CHANNEL_RC_OK;
674static UINT shadow_clipboard_client_data_response(CliprdrServerContext* context,
677 WINPR_ASSERT(context);
678 WINPR_ASSERT(response);
680 rdpShadowClient* client = context->custom;
681 WINPR_ASSERT(client);
682 rdpShadowClipboard* clipboard = client->server->clipboard;
683 WINPR_ASSERT(clipboard);
685 EnterCriticalSection(&clipboard->lock);
686 ShadowClipboardFetch* fetch =
687 clipboard->fetchBusy ? ArrayList_GetItem(clipboard->fetches, 0) : nullptr;
688 if (fetch && (fetch->client == client))
690 clipboard->fetchBusy = FALSE;
692 if (!fetch->canceled)
694 const BOOL ok = (response->common.msgFlags & CB_RESPONSE_OK) != 0;
696 BYTE* data =
nullptr;
698 data = shadow_clipboard_convert(clipboard, fetch->sourceId, fetch->mimeId,
699 response->requestedFormatData,
700 response->common.dataLen, &size);
701 WLog_DBG(TAG,
"client data: %" PRIu32
" bytes, %" PRIu32
" for the session",
702 response->common.dataLen, size);
705 if (data && fetch->text)
706 size = WINPR_ASSERTING_INT_CAST(UINT32, strnlen((
const char*)data, size));
707 shadow_clipboard_provide(clipboard, fetch->request, data, size);
711 if (!ArrayList_RemoveAt(clipboard->fetches, 0))
712 WLog_WARN(TAG,
"cannot drop a clipboard request");
713 shadow_clipboard_fetch_next(clipboard);
715 LeaveCriticalSection(&clipboard->lock);
716 return CHANNEL_RC_OK;
721BOOL shadow_clipboard_session_changed(rdpShadowServer* server,
const char*
const* mimeTypes,
724 WINPR_ASSERT(server);
725 WINPR_ASSERT(mimeTypes || (count == 0));
727#if defined(CHANNEL_CLIPRDR_SERVER)
728 rdpShadowClipboard* clipboard = server->clipboard;
732 char** mimes =
nullptr;
735 mimes = (
char**)calloc(count,
sizeof(
char*));
738 for (
size_t x = 0; x < count; x++)
740 mimes[x] = _strdup(mimeTypes[x]);
743 shadow_clipboard_mimes_free(mimes, count);
749 EnterCriticalSection(&clipboard->lock);
750 shadow_clipboard_cancel_fetches(clipboard,
nullptr);
751 shadow_clipboard_clear_owner(clipboard);
752 shadow_clipboard_mimes_free(clipboard->sessionMimes, clipboard->nSessionMimes);
753 clipboard->sessionMimes = mimes;
754 clipboard->nSessionMimes = count;
755 shadow_clipboard_announce(clipboard,
nullptr);
756 LeaveCriticalSection(&clipboard->lock);
763BOOL shadow_clipboard_session_data(rdpShadowServer* server,
const BYTE* data,
size_t size)
765 WINPR_ASSERT(server);
767#if defined(CHANNEL_CLIPRDR_SERVER)
768 rdpShadowClipboard* clipboard = server->clipboard;
773 EnterCriticalSection(&clipboard->lock);
774 const ShadowClipboardRead* read =
775 clipboard->readBusy ? ArrayList_GetItem(clipboard->reads, 0) : nullptr;
779 clipboard->readBusy = FALSE;
783 const UINT32 mimeId = ClipboardRegisterFormat(clipboard->convert, read->mime);
784 UINT32 converted = 0;
786 shadow_clipboard_convert(clipboard, mimeId, read->formatId, data, size, &converted);
787 WLog_DBG(TAG,
"session data: %" PRIuz
" bytes of %s, %" PRIu32
" for the client", size,
788 read->mime, converted);
789 shadow_clipboard_respond(read->client->cliprdr, payload, converted);
793 if (!ArrayList_RemoveAt(clipboard->reads, 0))
794 WLog_WARN(TAG,
"cannot drop a clipboard read");
795 shadow_clipboard_read_next(clipboard);
797 LeaveCriticalSection(&clipboard->lock);
806BOOL shadow_clipboard_session_request(rdpShadowServer* server, UINT32 request,
const char* mimeType)
808 WINPR_ASSERT(server);
809 WINPR_ASSERT(mimeType);
811#if defined(CHANNEL_CLIPRDR_SERVER)
812 rdpShadowClipboard* clipboard = server->clipboard;
816 EnterCriticalSection(&clipboard->lock);
819 ShadowClipboardFetch* first =
820 clipboard->fetchBusy ? ArrayList_GetItem(clipboard->fetches, 0) : nullptr;
821 if (first && (GetTickCount64() - first->sent >= SHADOW_CLIPBOARD_FETCH_TIMEOUT_MS))
823 WLog_WARN(TAG,
"no clipboard data from the client");
824 clipboard->fetchBusy = FALSE;
825 if (!first->canceled)
826 shadow_clipboard_provide(clipboard, first->request,
nullptr, 0);
827 if (!ArrayList_RemoveAt(clipboard->fetches, 0))
828 WLog_WARN(TAG,
"cannot drop a clipboard request");
832 if (clipboard->owner)
835 const UINT32 count = clipboard->nOwnerFormats;
836 if (strcmp(mimeType, SHADOW_CLIPBOARD_MIME_HTML) == 0)
837 source = shadow_clipboard_find_format(formats, count, 0, SHADOW_CLIPBOARD_FORMAT_HTML);
838 else if (strncmp(mimeType,
"image/", 6) == 0)
839 source = shadow_clipboard_owner_image(formats, count);
841 source = shadow_clipboard_owner_text(formats, count);
847 ShadowClipboardFetch* fetch = calloc(1,
sizeof(ShadowClipboardFetch));
850 fetch->client = clipboard->owner;
851 fetch->request = request;
852 fetch->formatId = source->formatId;
853 fetch->sourceId = source->formatName
854 ? ClipboardRegisterFormat(clipboard->convert, source->formatName)
856 fetch->mimeId = ClipboardRegisterFormat(clipboard->convert, mimeType);
857 fetch->text = strncmp(mimeType,
"image/", 6) != 0;
858 queued = ArrayList_Append(clipboard->fetches, fetch);
865 shadow_clipboard_fetch_next(clipboard);
867 shadow_clipboard_provide(clipboard, request,
nullptr, 0);
868 LeaveCriticalSection(&clipboard->lock);
871 WINPR_UNUSED(request);
876#if defined(CHANNEL_CLIPRDR_SERVER)
878rdpShadowClipboard* shadow_clipboard_new(rdpShadowServer* server)
880 WINPR_ASSERT(server);
882 rdpShadowClipboard* clipboard = calloc(1,
sizeof(rdpShadowClipboard));
886 clipboard->server = server;
887 if (!InitializeCriticalSectionAndSpinCount(&clipboard->lock, 4000))
893 clipboard->convert = ClipboardCreate();
894 clipboard->peers = ArrayList_New(FALSE);
895 clipboard->reads = ArrayList_New(FALSE);
896 clipboard->fetches = ArrayList_New(FALSE);
897 if (!clipboard->convert || !clipboard->peers || !clipboard->reads || !clipboard->fetches)
900 wObject* peers = ArrayList_Object(clipboard->peers);
903 wObject* reads = ArrayList_Object(clipboard->reads);
906 wObject* fetches = ArrayList_Object(clipboard->fetches);
907 WINPR_ASSERT(fetches);
910 clipboard->htmlId = ClipboardRegisterFormat(clipboard->convert, SHADOW_CLIPBOARD_FORMAT_HTML);
911 if (clipboard->htmlId == 0)
916 shadow_clipboard_free(clipboard);
920void shadow_clipboard_free(rdpShadowClipboard* clipboard)
925 ArrayList_Free(clipboard->fetches);
926 ArrayList_Free(clipboard->reads);
927 ArrayList_Free(clipboard->peers);
928 shadow_clipboard_formats_free(clipboard->ownerFormats, clipboard->nOwnerFormats);
929 shadow_clipboard_mimes_free(clipboard->sessionMimes, clipboard->nSessionMimes);
930 ClipboardDestroy(clipboard->convert);
931 DeleteCriticalSection(&clipboard->lock);
935int shadow_client_cliprdr_init(rdpShadowClient* client)
937 WINPR_ASSERT(client);
938 WINPR_ASSERT(client->server);
940 rdpShadowClipboard* clipboard = client->server->clipboard;
941 const rdpShadowSubsystem* subsystem = client->subsystem;
942 WINPR_ASSERT(subsystem);
944 if (!clipboard || !subsystem->ClipboardOffer || !subsystem->ClipboardRead ||
945 !subsystem->ClipboardProvide)
948 CliprdrServerContext* cliprdr = cliprdr_server_context_new(client->vcm);
952 cliprdr->custom = client;
953 cliprdr->rdpcontext = &client->context;
954 cliprdr->autoInitializationSequence = TRUE;
955 cliprdr->useLongFormatNames = TRUE;
956 cliprdr->ClientCapabilities = shadow_clipboard_client_capabilities;
957 cliprdr->ClientFormatList = shadow_clipboard_client_format_list;
958 cliprdr->ClientFormatDataRequest = shadow_clipboard_client_data_request;
959 cliprdr->ClientFormatDataResponse = shadow_clipboard_client_data_response;
961 ShadowClipboardPeer* peer = calloc(1,
sizeof(ShadowClipboardPeer));
964 cliprdr_server_context_free(cliprdr);
967 peer->client = client;
968 client->cliprdr = cliprdr;
970 EnterCriticalSection(&clipboard->lock);
971 const BOOL added = ArrayList_Append(clipboard->peers, peer);
972 LeaveCriticalSection(&clipboard->lock);
976 client->cliprdr =
nullptr;
977 cliprdr_server_context_free(cliprdr);
981 const UINT rc = cliprdr->Start(cliprdr);
982 if (rc != CHANNEL_RC_OK)
984 WLog_ERR(TAG,
"cannot start the clipboard channel: %" PRIu32, rc);
990void shadow_client_cliprdr_uninit(rdpShadowClient* client)
992 WINPR_ASSERT(client);
993 WINPR_ASSERT(client->server);
995 CliprdrServerContext* cliprdr = client->cliprdr;
999 rdpShadowClipboard* clipboard = client->server->clipboard;
1000 WINPR_ASSERT(clipboard);
1002 EnterCriticalSection(&clipboard->lock);
1003 ShadowClipboardPeer* peer = shadow_clipboard_peer(clipboard, client);
1004 if (peer && !ArrayList_Remove(clipboard->peers, peer))
1005 WLog_WARN(TAG,
"cannot drop a client of the clipboard");
1007 for (
size_t x = ArrayList_Count(clipboard->reads); x > 0; x--)
1009 const size_t index = x - 1;
1010 ShadowClipboardRead* read = ArrayList_GetItem(clipboard->reads, index);
1012 if (read->client != client)
1016 if ((index == 0) && clipboard->readBusy)
1017 read->client =
nullptr;
1018 else if (!ArrayList_RemoveAt(clipboard->reads, index))
1019 WLog_WARN(TAG,
"cannot drop a clipboard read");
1022 if (clipboard->owner == client)
1024 shadow_clipboard_cancel_fetches(clipboard, client);
1025 shadow_clipboard_release_owner(clipboard);
1027 else if (clipboard->fetchBusy)
1030 const ShadowClipboardFetch* fetch = ArrayList_GetItem(clipboard->fetches, 0);
1031 if (fetch && (fetch->client == client))
1033 clipboard->fetchBusy = FALSE;
1034 if (!ArrayList_RemoveAt(clipboard->fetches, 0))
1035 WLog_WARN(TAG,
"cannot drop a clipboard request");
1036 shadow_clipboard_fetch_next(clipboard);
1039 LeaveCriticalSection(&clipboard->lock);
1042 const UINT rc = cliprdr->Stop(cliprdr);
1043 if (rc != CHANNEL_RC_OK)
1044 WLog_WARN(TAG,
"cannot stop the clipboard channel: %" PRIu32, rc);
1045 cliprdr_server_context_free(cliprdr);
1046 client->cliprdr =
nullptr;
1051rdpShadowClipboard* shadow_clipboard_new(WINPR_ATTR_UNUSED rdpShadowServer* server)
1056void shadow_clipboard_free(WINPR_ATTR_UNUSED rdpShadowClipboard* clipboard)
1060int shadow_client_cliprdr_init(WINPR_ATTR_UNUSED rdpShadowClient* client)
1065void shadow_client_cliprdr_uninit(WINPR_ATTR_UNUSED rdpShadowClient* client)
This struct contains function pointer to initialize/free objects.
OBJECT_FREE_FN fnObjectFree