20#include <winpr/config.h>
23#include <winpr/collections.h>
24#include <winpr/wlog.h>
26#include <winpr/clipboard.h>
30#include "synthetic_file.h"
33#define TAG WINPR_TAG("clipboard")
35const char*
const mime_text_plain =
"text/plain";
36const char*
const mime_text_utf8 =
"text/plain;charset=utf-8";
37const char*
const mime_text_UTF8_STRING =
"UTF8_STRING";
47static const char* CF_STANDARD_STRINGS[] = {
68const char* ClipboardGetFormatIdString(UINT32 formatId)
70 if (formatId < ARRAYSIZE(CF_STANDARD_STRINGS))
71 return CF_STANDARD_STRINGS[formatId];
72 return "CF_REGISTERED_FORMAT";
75static wClipboardFormat* ClipboardFindFormat(wClipboard* clipboard, UINT32 formatId,
85 for (UINT32 index = 0; index < clipboard->numFormats; index++)
88 if (formatId == cformat->formatId)
97 for (UINT32 index = 0; index < clipboard->numFormats; index++)
100 if (!cformat->formatName)
103 if (strcmp(name, cformat->formatName) == 0)
113 if (clipboard->numFormats > 0)
115 format = &clipboard->formats[0];
117 if (format->formatId)
120 if (!format->formatName || (strcmp(format->formatName, CF_STANDARD_STRINGS[0]) == 0))
133 for (UINT32 index = 0; index < format->numSynthesizers; index++)
137 if (formatId == synthesizer->syntheticId)
144void ClipboardLock(wClipboard* clipboard)
149 EnterCriticalSection(&(clipboard->lock));
152void ClipboardUnlock(wClipboard* clipboard)
157 LeaveCriticalSection(&(clipboard->lock));
160BOOL ClipboardEmpty(wClipboard* clipboard)
167 free(clipboard->data);
168 clipboard->data =
nullptr;
172 clipboard->formatId = 0;
173 clipboard->sequenceNumber++;
177UINT32 ClipboardCountRegisteredFormats(wClipboard* clipboard)
182 return clipboard->numFormats;
185UINT32 ClipboardGetRegisteredFormatIds(wClipboard* clipboard, UINT32** ppFormatIds)
187 UINT32* pFormatIds =
nullptr;
196 pFormatIds = *ppFormatIds;
200 pFormatIds = calloc(clipboard->numFormats,
sizeof(UINT32));
205 *ppFormatIds = pFormatIds;
208 for (UINT32 index = 0; index < clipboard->numFormats; index++)
210 format = &(clipboard->formats[index]);
211 pFormatIds[index] = format->formatId;
214 return clipboard->numFormats;
217UINT32 ClipboardRegisterFormat(wClipboard* clipboard,
const char* name)
224 format = ClipboardFindFormat(clipboard, 0, name);
227 return format->formatId;
229 if ((clipboard->numFormats + 1) >= clipboard->maxFormats)
231 UINT32 numFormats = clipboard->maxFormats * 2;
239 clipboard->formats = tmpFormat;
240 clipboard->maxFormats = numFormats;
243 format = &(clipboard->formats[clipboard->numFormats]);
248 format->formatName = _strdup(name);
250 if (!format->formatName)
254 format->formatId = clipboard->nextFormatId++;
255 clipboard->numFormats++;
256 return format->formatId;
266 "Failed to register synthesizer for %s[0x%04" PRIx32
"] to %s [0x%04" PRIx32
268 ClipboardGetFormatName(clipboard, formatId), formatId,
269 ClipboardGetFormatName(clipboard, syntheticId), syntheticId);
273 WLog_DBG(TAG,
"Registering synthesizer for %s[0x%04" PRIx32
"] to %s [0x%04" PRIx32
"]",
274 ClipboardGetFormatName(clipboard, formatId), formatId,
275 ClipboardGetFormatName(clipboard, syntheticId), syntheticId);
277 wClipboardFormat* format = ClipboardFindFormat(clipboard, formatId,
nullptr);
282 "Failed to register synthesizer for %s[0x%04" PRIx32
"] to %s [0x%04" PRIx32
284 ClipboardGetFormatName(clipboard, formatId), formatId,
285 ClipboardGetFormatName(clipboard, syntheticId), syntheticId);
289 if (format->formatId == syntheticId)
292 "Failed to register synthesizer for %s[0x%04" PRIx32
"] to %s [0x%04" PRIx32
294 ClipboardGetFormatName(clipboard, formatId), formatId,
295 ClipboardGetFormatName(clipboard, syntheticId), syntheticId);
303 UINT32 numSynthesizers = format->numSynthesizers + 1;
310 "Failed to register synthesizer for %s[0x%04" PRIx32
"] to %s [0x%04" PRIx32
311 "]: failed to allocate new synthesizer",
312 ClipboardGetFormatName(clipboard, formatId), formatId,
313 ClipboardGetFormatName(clipboard, syntheticId), syntheticId);
317 format->synthesizers = tmpSynthesizer;
318 format->numSynthesizers = numSynthesizers;
319 const UINT32 index = numSynthesizers - 1;
320 synthesizer = &(format->synthesizers[index]);
325BOOL ClipboardRegisterSynthesizer(wClipboard* clipboard, UINT32 formatId, UINT32 syntheticId,
326 CLIPBOARD_SYNTHESIZE_FN pfnSynthesize)
332 synthesizer->syntheticId = syntheticId;
333 synthesizer->pfnSynthesize = pfnSynthesize;
337BOOL ClipboardRegisterSynthesizerEx(wClipboard* clipboard, UINT32 formatId, UINT32 syntheticId,
338 CLIPBOARD_SYNTHESIZE_FN pfnSynthesize)
344 synthesizer->syntheticId = syntheticId;
345 synthesizer->pfnSynthesizeEx = pfnSynthesize;
349UINT32 ClipboardCountFormats(wClipboard* clipboard)
357 format = ClipboardFindFormat(clipboard, clipboard->formatId,
nullptr);
362 count = 1 + format->numSynthesizers;
366UINT32 ClipboardGetFormatIds(wClipboard* clipboard, UINT32** ppFormatIds)
369 UINT32* pFormatIds =
nullptr;
376 format = ClipboardFindFormat(clipboard, clipboard->formatId,
nullptr);
381 count = 1 + format->numSynthesizers;
386 pFormatIds = *ppFormatIds;
390 pFormatIds = calloc(count,
sizeof(UINT32));
395 *ppFormatIds = pFormatIds;
398 pFormatIds[0] = format->formatId;
400 for (UINT32 index = 1; index < count; index++)
402 synthesizer = &(format->synthesizers[index - 1]);
403 pFormatIds[index] = synthesizer->syntheticId;
409static void ClipboardUninitFormats(wClipboard* clipboard)
411 WINPR_ASSERT(clipboard);
412 for (UINT32 formatId = 0; formatId < clipboard->numFormats; formatId++)
415 free(format->formatName);
416 free(format->synthesizers);
417 format->formatName =
nullptr;
418 format->synthesizers =
nullptr;
422static BOOL ClipboardInitFormats(wClipboard* clipboard)
430 for (formatId = 0; formatId < CF_MAX; formatId++, clipboard->numFormats++)
432 format = &(clipboard->formats[clipboard->numFormats]);
434 format->formatId = formatId;
435 format->formatName = _strdup(CF_STANDARD_STRINGS[formatId]);
437 if (!format->formatName)
441 if (!ClipboardInitSynthesizers(clipboard))
447 ClipboardUninitFormats(clipboard);
451UINT32 ClipboardGetFormatId(wClipboard* clipboard,
const char* name)
458 format = ClipboardFindFormat(clipboard, 0, name);
463 return format->formatId;
466const char* ClipboardGetFormatName(wClipboard* clipboard, UINT32 formatId)
473 format = ClipboardFindFormat(clipboard, formatId,
nullptr);
478 return format->formatName;
481void* ClipboardGetData(wClipboard* clipboard, UINT32 formatId, UINT32* pSize)
483 void* pDstData =
nullptr;
485 if (!clipboard || !pSize)
487 WLog_ERR(TAG,
"Invalid parameters clipboard=%p, pSize=%p",
488 WINPR_CXX_COMPAT_CAST(
const void*, clipboard),
489 WINPR_CXX_COMPAT_CAST(
const void*, pSize));
494 wClipboardFormat* format = ClipboardFindFormat(clipboard, clipboard->formatId,
nullptr);
498 WLog_ERR(TAG,
"Format [0x%08" PRIx32
"] not found", clipboard->formatId);
502 const UINT32 SrcSize = clipboard->size;
503 void* pSrcData = clipboard->data;
505 if (formatId == format->formatId)
507 UINT32 DstSize = SrcSize;
508 pDstData = malloc(DstSize);
513 CopyMemory(pDstData, pSrcData, SrcSize);
520 if (!synthesizer || !(synthesizer->pfnSynthesize || synthesizer->pfnSynthesizeEx))
522 WLog_ERR(TAG,
"No synthesizer for format %s [0x%08" PRIx32
"] --> %s [0x%08" PRIx32
"]",
523 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId,
524 ClipboardGetFormatName(clipboard, formatId), formatId);
528 UINT32 DstSize = SrcSize;
529 if (synthesizer->pfnSynthesizeEx)
530 pDstData = synthesizer->pfnSynthesizeEx(clipboard, synthesizer->syntheticId, pSrcData,
533 pDstData = synthesizer->pfnSynthesize(clipboard, format->formatId, pSrcData, &DstSize);
538 WLog_DBG(TAG,
"getting formatId=%s [0x%08" PRIx32
"] data=%p, size=%" PRIu32,
539 ClipboardGetFormatName(clipboard, formatId), formatId, pDstData, *pSize);
543BOOL ClipboardSetData(wClipboard* clipboard, UINT32 formatId,
const void* data, UINT32 size)
547 WLog_DBG(TAG,
"setting formatId=%s [0x%08" PRIx32
"], size=%" PRIu32,
548 ClipboardGetFormatName(clipboard, formatId), formatId, size);
552 format = ClipboardFindFormat(clipboard, formatId,
nullptr);
557 free(clipboard->data);
560 clipboard->data = calloc(size + 2ull *
sizeof(WCHAR),
sizeof(
char));
562 if (!clipboard->data)
565 memcpy(clipboard->data, data, size);
574 clipboard->size = (UINT32)(strnlen(clipboard->data, size) + 1UL);
578 (UINT32)((_wcsnlen(clipboard->data, size /
sizeof(WCHAR)) + 1UL) *
sizeof(WCHAR));
581 clipboard->size = size;
585 clipboard->formatId = formatId;
586 clipboard->sequenceNumber++;
590UINT64 ClipboardGetOwner(wClipboard* clipboard)
595 return clipboard->ownerId;
598void ClipboardSetOwner(wClipboard* clipboard, UINT64 ownerId)
603 clipboard->ownerId = ownerId;
606wClipboardDelegate* ClipboardGetDelegate(wClipboard* clipboard)
611 return &clipboard->delegate;
614static void ClipboardInitLocalFileSubsystem(wClipboard* clipboard)
620 if (ClipboardInitSyntheticFileSubsystem(clipboard))
622 WLog_DBG(TAG,
"initialized synthetic local file subsystem");
627 WLog_WARN(TAG,
"failed to initialize synthetic local file subsystem");
630 WLog_INFO(TAG,
"failed to initialize local file subsystem, file transfer not available");
633wClipboard* ClipboardCreate(
void)
635 wClipboard* clipboard = (wClipboard*)calloc(1,
sizeof(wClipboard));
640 clipboard->nextFormatId = 0xC000;
641 clipboard->sequenceNumber = 0;
643 if (!InitializeCriticalSectionAndSpinCount(&(clipboard->lock), 4000))
646 clipboard->numFormats = 0;
647 clipboard->maxFormats = 64;
650 if (!clipboard->formats)
653 if (!ClipboardInitFormats(clipboard))
656 clipboard->delegate.clipboard = clipboard;
657 ClipboardInitLocalFileSubsystem(clipboard);
660 ClipboardDestroy(clipboard);
664void ClipboardDestroy(wClipboard* clipboard)
669 ArrayList_Free(clipboard->localFiles);
670 clipboard->localFiles =
nullptr;
672 ClipboardUninitFormats(clipboard);
674 free(clipboard->data);
675 clipboard->data =
nullptr;
677 clipboard->numFormats = 0;
678 free(clipboard->formats);
679 DeleteCriticalSection(&(clipboard->lock));
683static BOOL is_dos_drive(
const char* path,
size_t len)
689 if (path[1] ==
':' || path[1] ==
'|')
691 if (((path[0] >=
'A') && (path[0] <=
'Z')) || ((path[0] >=
'a') && (path[0] <=
'z')))
697char* parse_uri_to_local_file(
const char* uri,
size_t uri_len)
700 const char prefix[] =
"file:";
701 const char prefixTraditional[] =
"file://";
702 const char* localName =
nullptr;
704 char* buffer =
nullptr;
705 const size_t prefixLen = strnlen(prefix,
sizeof(prefix));
706 const size_t prefixTraditionalLen = strnlen(prefixTraditional,
sizeof(prefixTraditional));
708 WINPR_ASSERT(uri || (uri_len == 0));
710 WLog_VRB(TAG,
"processing URI: %.*s", WINPR_ASSERTING_INT_CAST(
int, uri_len), uri);
712 if ((uri_len <= prefixLen) || strncmp(uri, prefix, prefixLen) != 0)
714 WLog_ERR(TAG,
"non-'file:' URI schemes are not supported");
733 if (uri[prefixLen] !=
'/')
736 if (is_dos_drive(&uri[prefixLen], uri_len - prefixLen))
739 localName = &uri[prefixLen];
740 localLen = uri_len - prefixLen;
745 WLog_ERR(TAG,
"URI format are not supported: %s", uri);
757 else if ((uri_len > prefixLen + 1) && (uri[prefixLen + 1] !=
'/'))
759 if (is_dos_drive(&uri[prefixLen + 1], uri_len - prefixLen - 1))
762 localName = (uri + prefixLen + 1);
763 localLen = uri_len - prefixLen - 1;
767 localName = &uri[prefixLen];
768 localLen = uri_len - prefixLen;
778 if ((uri_len < prefixTraditionalLen) ||
779 strncmp(uri, prefixTraditional, prefixTraditionalLen) != 0)
781 WLog_ERR(TAG,
"non-'file:' URI schemes are not supported");
785 localName = &uri[prefixTraditionalLen];
786 localLen = uri_len - prefixTraditionalLen;
790 WLog_ERR(TAG,
"empty 'file:' URI schemes are not supported");
798 if (localName[0] !=
'/')
800 WLog_ERR(TAG,
"URI format are not supported: %s", uri);
804 if (is_dos_drive(&localName[1], localLen - 1))
812 buffer = winpr_str_url_decode(localName, localLen);
815 if (buffer[1] ==
'|' &&
816 ((buffer[0] >=
'A' && buffer[0] <=
'Z') || (buffer[0] >=
'a' && buffer[0] <=
'z')))