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";
45static const char* CF_STANDARD_STRINGS[] = {
66const char* ClipboardGetFormatIdString(UINT32 formatId)
68 if (formatId < ARRAYSIZE(CF_STANDARD_STRINGS))
69 return CF_STANDARD_STRINGS[formatId];
70 return "CF_REGISTERED_FORMAT";
73static wClipboardFormat* ClipboardFindFormat(wClipboard* clipboard, UINT32 formatId,
83 for (UINT32 index = 0; index < clipboard->numFormats; index++)
86 if (formatId == cformat->formatId)
95 for (UINT32 index = 0; index < clipboard->numFormats; index++)
98 if (!cformat->formatName)
101 if (strcmp(name, cformat->formatName) == 0)
111 if (clipboard->numFormats > 0)
113 format = &clipboard->formats[0];
115 if (format->formatId)
118 if (!format->formatName || (strcmp(format->formatName, CF_STANDARD_STRINGS[0]) == 0))
131 for (UINT32 index = 0; index < format->numSynthesizers; index++)
135 if (formatId == synthesizer->syntheticId)
142void ClipboardLock(wClipboard* clipboard)
147 EnterCriticalSection(&(clipboard->lock));
150void ClipboardUnlock(wClipboard* clipboard)
155 LeaveCriticalSection(&(clipboard->lock));
158BOOL ClipboardEmpty(wClipboard* clipboard)
165 free(clipboard->data);
166 clipboard->data = NULL;
170 clipboard->formatId = 0;
171 clipboard->sequenceNumber++;
175UINT32 ClipboardCountRegisteredFormats(wClipboard* clipboard)
180 return clipboard->numFormats;
183UINT32 ClipboardGetRegisteredFormatIds(wClipboard* clipboard, UINT32** ppFormatIds)
185 UINT32* pFormatIds = NULL;
194 pFormatIds = *ppFormatIds;
198 pFormatIds = calloc(clipboard->numFormats,
sizeof(UINT32));
203 *ppFormatIds = pFormatIds;
206 for (UINT32 index = 0; index < clipboard->numFormats; index++)
208 format = &(clipboard->formats[index]);
209 pFormatIds[index] = format->formatId;
212 return clipboard->numFormats;
215UINT32 ClipboardRegisterFormat(wClipboard* clipboard,
const char* name)
222 format = ClipboardFindFormat(clipboard, 0, name);
225 return format->formatId;
227 if ((clipboard->numFormats + 1) >= clipboard->maxFormats)
229 UINT32 numFormats = clipboard->maxFormats * 2;
237 clipboard->formats = tmpFormat;
238 clipboard->maxFormats = numFormats;
241 format = &(clipboard->formats[clipboard->numFormats]);
246 format->formatName = _strdup(name);
248 if (!format->formatName)
252 format->formatId = clipboard->nextFormatId++;
253 clipboard->numFormats++;
254 return format->formatId;
257BOOL ClipboardRegisterSynthesizer(wClipboard* clipboard, UINT32 formatId, UINT32 syntheticId,
258 CLIPBOARD_SYNTHESIZE_FN pfnSynthesize)
267 format = ClipboardFindFormat(clipboard, formatId, NULL);
272 if (format->formatId == syntheticId)
275 synthesizer = ClipboardFindSynthesizer(format, formatId);
280 UINT32 numSynthesizers = format->numSynthesizers + 1;
287 format->synthesizers = tmpSynthesizer;
288 format->numSynthesizers = numSynthesizers;
289 index = numSynthesizers - 1;
290 synthesizer = &(format->synthesizers[index]);
293 synthesizer->syntheticId = syntheticId;
294 synthesizer->pfnSynthesize = pfnSynthesize;
298UINT32 ClipboardCountFormats(wClipboard* clipboard)
306 format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL);
311 count = 1 + format->numSynthesizers;
315UINT32 ClipboardGetFormatIds(wClipboard* clipboard, UINT32** ppFormatIds)
318 UINT32* pFormatIds = NULL;
325 format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL);
330 count = 1 + format->numSynthesizers;
335 pFormatIds = *ppFormatIds;
339 pFormatIds = calloc(count,
sizeof(UINT32));
344 *ppFormatIds = pFormatIds;
347 pFormatIds[0] = format->formatId;
349 for (UINT32 index = 1; index < count; index++)
351 synthesizer = &(format->synthesizers[index - 1]);
352 pFormatIds[index] = synthesizer->syntheticId;
358static void ClipboardUninitFormats(wClipboard* clipboard)
360 WINPR_ASSERT(clipboard);
361 for (UINT32 formatId = 0; formatId < clipboard->numFormats; formatId++)
364 free(format->formatName);
365 free(format->synthesizers);
366 format->formatName = NULL;
367 format->synthesizers = NULL;
371static BOOL ClipboardInitFormats(wClipboard* clipboard)
379 for (formatId = 0; formatId < CF_MAX; formatId++, clipboard->numFormats++)
381 format = &(clipboard->formats[clipboard->numFormats]);
383 format->formatId = formatId;
384 format->formatName = _strdup(CF_STANDARD_STRINGS[formatId]);
386 if (!format->formatName)
390 if (!ClipboardInitSynthesizers(clipboard))
396 ClipboardUninitFormats(clipboard);
400UINT32 ClipboardGetFormatId(wClipboard* clipboard,
const char* name)
407 format = ClipboardFindFormat(clipboard, 0, name);
412 return format->formatId;
415const char* ClipboardGetFormatName(wClipboard* clipboard, UINT32 formatId)
422 format = ClipboardFindFormat(clipboard, formatId, NULL);
427 return format->formatName;
430void* ClipboardGetData(wClipboard* clipboard, UINT32 formatId, UINT32* pSize)
434 void* pSrcData = NULL;
435 void* pDstData = NULL;
439 if (!clipboard || !pSize)
441 WLog_ERR(TAG,
"Invalid parameters clipboard=%p, pSize=%p",
442 WINPR_CXX_COMPAT_CAST(
const void*, clipboard),
443 WINPR_CXX_COMPAT_CAST(
const void*, pSize));
448 format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL);
452 WLog_ERR(TAG,
"Format [0x%08" PRIx32
"] not found", clipboard->formatId);
456 SrcSize = clipboard->size;
457 pSrcData = clipboard->data;
459 if (formatId == format->formatId)
462 pDstData = malloc(DstSize);
467 CopyMemory(pDstData, pSrcData, SrcSize);
472 synthesizer = ClipboardFindSynthesizer(format, formatId);
474 if (!synthesizer || !synthesizer->pfnSynthesize)
476 WLog_ERR(TAG,
"No synthesizer for format %s [0x%08" PRIx32
"] --> %s [0x%08" PRIx32
"]",
477 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId,
478 ClipboardGetFormatName(clipboard, formatId), formatId);
483 pDstData = synthesizer->pfnSynthesize(clipboard, format->formatId, pSrcData, &DstSize);
488 WLog_DBG(TAG,
"getting formatId=%s [0x%08" PRIx32
"] data=%p, size=%" PRIu32,
489 ClipboardGetFormatName(clipboard, formatId), formatId, pDstData, *pSize);
493BOOL ClipboardSetData(wClipboard* clipboard, UINT32 formatId,
const void* data, UINT32 size)
497 WLog_DBG(TAG,
"setting formatId=%s [0x%08" PRIx32
"], size=%" PRIu32,
498 ClipboardGetFormatName(clipboard, formatId), formatId, size);
502 format = ClipboardFindFormat(clipboard, formatId, NULL);
507 free(clipboard->data);
509 clipboard->data = calloc(size +
sizeof(WCHAR),
sizeof(
char));
511 if (!clipboard->data)
514 memcpy(clipboard->data, data, size);
523 clipboard->size = (UINT32)(strnlen(clipboard->data, size) + 1UL);
527 (UINT32)((_wcsnlen(clipboard->data, size /
sizeof(WCHAR)) + 1UL) *
sizeof(WCHAR));
530 clipboard->size = size;
534 clipboard->formatId = formatId;
535 clipboard->sequenceNumber++;
539UINT64 ClipboardGetOwner(wClipboard* clipboard)
544 return clipboard->ownerId;
547void ClipboardSetOwner(wClipboard* clipboard, UINT64 ownerId)
552 clipboard->ownerId = ownerId;
555wClipboardDelegate* ClipboardGetDelegate(wClipboard* clipboard)
560 return &clipboard->delegate;
563static void ClipboardInitLocalFileSubsystem(wClipboard* clipboard)
569 if (ClipboardInitSyntheticFileSubsystem(clipboard))
571 WLog_DBG(TAG,
"initialized synthetic local file subsystem");
576 WLog_WARN(TAG,
"failed to initialize synthetic local file subsystem");
579 WLog_INFO(TAG,
"failed to initialize local file subsystem, file transfer not available");
582wClipboard* ClipboardCreate(
void)
584 wClipboard* clipboard = (wClipboard*)calloc(1,
sizeof(wClipboard));
589 clipboard->nextFormatId = 0xC000;
590 clipboard->sequenceNumber = 0;
592 if (!InitializeCriticalSectionAndSpinCount(&(clipboard->lock), 4000))
595 clipboard->numFormats = 0;
596 clipboard->maxFormats = 64;
599 if (!clipboard->formats)
602 if (!ClipboardInitFormats(clipboard))
605 clipboard->delegate.clipboard = clipboard;
606 ClipboardInitLocalFileSubsystem(clipboard);
609 ClipboardDestroy(clipboard);
613void ClipboardDestroy(wClipboard* clipboard)
618 ArrayList_Free(clipboard->localFiles);
619 clipboard->localFiles = NULL;
621 ClipboardUninitFormats(clipboard);
623 free(clipboard->data);
624 clipboard->data = NULL;
626 clipboard->numFormats = 0;
627 free(clipboard->formats);
628 DeleteCriticalSection(&(clipboard->lock));
632static BOOL is_dos_drive(
const char* path,
size_t len)
638 if (path[1] ==
':' || path[1] ==
'|')
640 if (((path[0] >=
'A') && (path[0] <=
'Z')) || ((path[0] >=
'a') && (path[0] <=
'z')))
646char* parse_uri_to_local_file(
const char* uri,
size_t uri_len)
649 const char prefix[] =
"file:";
650 const char prefixTraditional[] =
"file://";
651 const char* localName = NULL;
654 const size_t prefixLen = strnlen(prefix,
sizeof(prefix));
655 const size_t prefixTraditionalLen = strnlen(prefixTraditional,
sizeof(prefixTraditional));
657 WINPR_ASSERT(uri || (uri_len == 0));
659 WLog_VRB(TAG,
"processing URI: %.*s", WINPR_ASSERTING_INT_CAST(
int, uri_len), uri);
661 if ((uri_len <= prefixLen) || strncmp(uri, prefix, prefixLen) != 0)
663 WLog_ERR(TAG,
"non-'file:' URI schemes are not supported");
682 if (uri[prefixLen] !=
'/')
685 if (is_dos_drive(&uri[prefixLen], uri_len - prefixLen))
688 localName = &uri[prefixLen];
689 localLen = uri_len - prefixLen;
694 WLog_ERR(TAG,
"URI format are not supported: %s", uri);
706 else if ((uri_len > prefixLen + 1) && (uri[prefixLen + 1] !=
'/'))
708 if (is_dos_drive(&uri[prefixLen + 1], uri_len - prefixLen - 1))
711 localName = (uri + prefixLen + 1);
712 localLen = uri_len - prefixLen - 1;
716 localName = &uri[prefixLen];
717 localLen = uri_len - prefixLen;
727 if ((uri_len < prefixTraditionalLen) ||
728 strncmp(uri, prefixTraditional, prefixTraditionalLen) != 0)
730 WLog_ERR(TAG,
"non-'file:' URI schemes are not supported");
734 localName = &uri[prefixTraditionalLen];
735 localLen = uri_len - prefixTraditionalLen;
739 WLog_ERR(TAG,
"empty 'file:' URI schemes are not supported");
747 if (localName[0] !=
'/')
749 WLog_ERR(TAG,
"URI format are not supported: %s", uri);
753 if (is_dos_drive(&localName[1], localLen - 1))
761 buffer = winpr_str_url_decode(localName, localLen);
764 if (buffer[1] ==
'|' &&
765 ((buffer[0] >=
'A' && buffer[0] <=
'Z') || (buffer[0] >=
'a' && buffer[0] <=
'z')))