FreeRDP/winpr/libwinpr/clipboard/clipboard.c

614 lines
12 KiB
C
Raw Normal View History

2014-10-17 23:19:05 +04:00
/**
* WinPR: Windows Portable Runtime
* Clipboard Functions
*
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <winpr/crt.h>
#include <winpr/collections.h>
#include <winpr/wlog.h>
2014-10-17 23:19:05 +04:00
#include <winpr/clipboard.h>
#include "clipboard.h"
#ifdef WITH_WCLIPBOARD_POSIX
#include "posix.h"
#endif
#include "../log.h"
#define TAG WINPR_TAG("clipboard")
2014-10-17 23:19:05 +04:00
/**
* Clipboard (Windows):
* msdn.microsoft.com/en-us/library/windows/desktop/ms648709/
*
* W3C Clipboard API and events:
* http://www.w3.org/TR/clipboard-apis/
*/
2019-11-20 13:30:14 +03:00
static const char* CF_STANDARD_STRINGS[CF_MAX] = {
2019-11-06 17:24:51 +03:00
"CF_RAW", /* 0 */
"CF_TEXT", /* 1 */
"CF_BITMAP", /* 2 */
"CF_METAFILEPICT", /* 3 */
"CF_SYLK", /* 4 */
"CF_DIF", /* 5 */
"CF_TIFF", /* 6 */
"CF_OEMTEXT", /* 7 */
"CF_DIB", /* 8 */
"CF_PALETTE", /* 9 */
"CF_PENDATA", /* 10 */
"CF_RIFF", /* 11 */
"CF_WAVE", /* 12 */
"CF_UNICODETEXT", /* 13 */
"CF_ENHMETAFILE", /* 14 */
"CF_HDROP", /* 15 */
"CF_LOCALE", /* 16 */
"CF_DIBV5" /* 17 */
2014-10-17 23:19:05 +04:00
};
2019-11-20 13:30:14 +03:00
static wClipboardFormat* ClipboardFindFormat(wClipboard* clipboard, UINT32 formatId,
const char* name)
2014-10-17 23:19:05 +04:00
{
UINT32 index;
wClipboardFormat* format = NULL;
2014-11-20 20:08:01 +03:00
if (!clipboard)
return NULL;
2016-10-06 14:31:25 +03:00
2014-10-17 23:19:05 +04:00
if (formatId)
{
for (index = 0; index < clipboard->numFormats; index++)
{
if (formatId == clipboard->formats[index].formatId)
{
format = &clipboard->formats[index];
break;
}
}
}
else if (name)
{
for (index = 0; index < clipboard->numFormats; index++)
{
if (!clipboard->formats[index].formatName)
continue;
2014-10-17 23:19:05 +04:00
if (strcmp(name, clipboard->formats[index].formatName) == 0)
{
format = &clipboard->formats[index];
break;
}
}
}
else
{
/* special "CF_RAW" case */
if (clipboard->numFormats > 0)
{
format = &clipboard->formats[0];
if (format->formatId)
return NULL;
2019-11-06 17:24:51 +03:00
if (!format->formatName || (strcmp(format->formatName, CF_STANDARD_STRINGS[0]) == 0))
2014-10-17 23:19:05 +04:00
return format;
}
}
return format;
}
2019-11-20 13:30:14 +03:00
static wClipboardSynthesizer* ClipboardFindSynthesizer(wClipboardFormat* format, UINT32 formatId)
{
UINT32 index;
wClipboardSynthesizer* synthesizer;
if (!format)
return NULL;
if (format->numSynthesizers < 1)
return NULL;
for (index = 0; index < format->numSynthesizers; index++)
{
synthesizer = &(format->synthesizers[index]);
if (formatId == synthesizer->syntheticId)
return synthesizer;
}
return NULL;
}
2014-10-17 23:19:05 +04:00
void ClipboardLock(wClipboard* clipboard)
{
2014-11-20 20:08:01 +03:00
if (!clipboard)
return;
2016-10-06 14:31:25 +03:00
2014-10-17 23:19:05 +04:00
EnterCriticalSection(&(clipboard->lock));
}
void ClipboardUnlock(wClipboard* clipboard)
{
2014-11-20 20:08:01 +03:00
if (!clipboard)
return;
2016-10-06 14:31:25 +03:00
2014-10-17 23:19:05 +04:00
LeaveCriticalSection(&(clipboard->lock));
}
BOOL ClipboardEmpty(wClipboard* clipboard)
{
2014-11-20 20:08:01 +03:00
if (!clipboard)
2014-11-21 23:12:49 +03:00
return FALSE;
2016-10-06 14:31:25 +03:00
2014-10-17 23:19:05 +04:00
if (clipboard->data)
{
2019-11-06 17:24:51 +03:00
free((void*)clipboard->data);
2014-10-17 23:19:05 +04:00
clipboard->data = NULL;
}
clipboard->size = 0;
clipboard->formatId = 0;
2014-10-17 23:19:05 +04:00
clipboard->sequenceNumber++;
return TRUE;
}
UINT32 ClipboardCountRegisteredFormats(wClipboard* clipboard)
2014-10-17 23:19:05 +04:00
{
2014-11-20 20:08:01 +03:00
if (!clipboard)
return 0;
2016-10-06 14:31:25 +03:00
2014-10-17 23:19:05 +04:00
return clipboard->numFormats;
}
2019-11-06 17:24:51 +03:00
UINT32 ClipboardGetRegisteredFormatIds(wClipboard* clipboard, UINT32** ppFormatIds)
2014-10-17 23:19:05 +04:00
{
UINT32 index;
UINT32* pFormatIds;
wClipboardFormat* format;
2016-10-06 14:31:25 +03:00
2014-11-20 20:08:01 +03:00
if (!clipboard)
return 0;
2014-10-17 23:19:05 +04:00
if (!ppFormatIds)
return 0;
pFormatIds = *ppFormatIds;
if (!pFormatIds)
{
pFormatIds = calloc(clipboard->numFormats, sizeof(UINT32));
2014-10-17 23:19:05 +04:00
if (!pFormatIds)
return 0;
*ppFormatIds = pFormatIds;
}
for (index = 0; index < clipboard->numFormats; index++)
{
format = &(clipboard->formats[index]);
pFormatIds[index] = format->formatId;
}
return clipboard->numFormats;
}
UINT32 ClipboardRegisterFormat(wClipboard* clipboard, const char* name)
{
wClipboardFormat* format;
2016-10-06 14:31:25 +03:00
2014-11-20 20:08:01 +03:00
if (!clipboard)
return 0;
2014-10-17 23:19:05 +04:00
format = ClipboardFindFormat(clipboard, 0, name);
if (format)
return format->formatId;
if ((clipboard->numFormats + 1) >= clipboard->maxFormats)
{
UINT32 numFormats = clipboard->maxFormats * 2;
2016-10-06 14:31:25 +03:00
wClipboardFormat* tmpFormat;
2019-11-06 17:24:51 +03:00
tmpFormat =
(wClipboardFormat*)realloc(clipboard->formats, numFormats * sizeof(wClipboardFormat));
2014-10-17 23:19:05 +04:00
if (!tmpFormat)
2014-10-17 23:19:05 +04:00
return 0;
2019-11-06 17:24:51 +03:00
clipboard->formats = tmpFormat;
clipboard->maxFormats = numFormats;
2014-10-17 23:19:05 +04:00
}
format = &(clipboard->formats[clipboard->numFormats]);
ZeroMemory(format, sizeof(wClipboardFormat));
if (name)
{
format->formatName = _strdup(name);
if (!format->formatName)
return 0;
}
format->formatId = clipboard->nextFormatId++;
clipboard->numFormats++;
return format->formatId;
}
2019-11-06 17:24:51 +03:00
BOOL ClipboardRegisterSynthesizer(wClipboard* clipboard, UINT32 formatId, UINT32 syntheticId,
CLIPBOARD_SYNTHESIZE_FN pfnSynthesize)
{
UINT32 index;
wClipboardFormat* format;
wClipboardSynthesizer* synthesizer;
2016-10-06 14:31:25 +03:00
2014-11-20 20:08:01 +03:00
if (!clipboard)
return FALSE;
format = ClipboardFindFormat(clipboard, formatId, NULL);
if (!format)
return FALSE;
if (format->formatId == syntheticId)
return FALSE;
synthesizer = ClipboardFindSynthesizer(format, formatId);
if (!synthesizer)
{
2016-10-06 14:31:25 +03:00
wClipboardSynthesizer* tmpSynthesizer;
UINT32 numSynthesizers = format->numSynthesizers + 1;
2019-11-06 17:24:51 +03:00
tmpSynthesizer = (wClipboardSynthesizer*)realloc(
format->synthesizers, numSynthesizers * sizeof(wClipboardSynthesizer));
if (!tmpSynthesizer)
return FALSE;
2019-11-06 17:24:51 +03:00
format->synthesizers = tmpSynthesizer;
format->numSynthesizers = numSynthesizers;
index = numSynthesizers - 1;
synthesizer = &(format->synthesizers[index]);
}
ZeroMemory(synthesizer, sizeof(wClipboardSynthesizer));
synthesizer->syntheticId = syntheticId;
synthesizer->pfnSynthesize = pfnSynthesize;
return TRUE;
}
UINT32 ClipboardCountFormats(wClipboard* clipboard)
{
UINT32 count;
wClipboardFormat* format;
2016-10-06 14:31:25 +03:00
2014-11-20 20:08:01 +03:00
if (!clipboard)
return 0;
format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL);
if (!format)
return 0;
count = 1 + format->numSynthesizers;
return count;
}
UINT32 ClipboardGetFormatIds(wClipboard* clipboard, UINT32** ppFormatIds)
{
UINT32 index;
UINT32 count;
UINT32* pFormatIds;
wClipboardFormat* format;
wClipboardSynthesizer* synthesizer;
2016-10-06 14:31:25 +03:00
2014-11-20 20:08:01 +03:00
if (!clipboard)
return 0;
format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL);
if (!format)
return 0;
count = 1 + format->numSynthesizers;
if (!ppFormatIds)
return 0;
pFormatIds = *ppFormatIds;
if (!pFormatIds)
{
pFormatIds = calloc(count, sizeof(UINT32));
if (!pFormatIds)
return 0;
*ppFormatIds = pFormatIds;
}
pFormatIds[0] = format->formatId;
for (index = 1; index < count; index++)
{
synthesizer = &(format->synthesizers[index - 1]);
pFormatIds[index] = synthesizer->syntheticId;
}
return count;
}
2019-11-20 13:30:14 +03:00
static BOOL ClipboardInitFormats(wClipboard* clipboard)
2014-10-17 23:19:05 +04:00
{
UINT32 formatId = 0;
wClipboardFormat* format;
2016-10-06 14:31:25 +03:00
2014-11-20 20:08:01 +03:00
if (!clipboard)
return FALSE;
2014-10-17 23:19:05 +04:00
for (formatId = 0; formatId < CF_MAX; formatId++, clipboard->numFormats++)
2014-10-17 23:19:05 +04:00
{
format = &(clipboard->formats[clipboard->numFormats]);
2014-10-17 23:19:05 +04:00
ZeroMemory(format, sizeof(wClipboardFormat));
format->formatId = formatId;
format->formatName = _strdup(CF_STANDARD_STRINGS[formatId]);
2014-10-17 23:19:05 +04:00
if (!format->formatName)
goto error;
2014-10-17 23:19:05 +04:00
}
if (!ClipboardInitSynthesizers(clipboard))
goto error;
2014-10-17 23:19:05 +04:00
return TRUE;
error:
for (formatId = 0; formatId < clipboard->numFormats; formatId++)
{
free(clipboard->formats[formatId].formatName);
free(clipboard->formats[formatId].synthesizers);
}
return FALSE;
2014-10-17 23:19:05 +04:00
}
UINT32 ClipboardGetFormatId(wClipboard* clipboard, const char* name)
{
wClipboardFormat* format;
2014-11-20 20:08:01 +03:00
if (!clipboard)
return 0;
2016-10-06 14:31:25 +03:00
format = ClipboardFindFormat(clipboard, 0, name);
if (!format)
return 0;
return format->formatId;
}
2014-10-17 23:19:05 +04:00
const char* ClipboardGetFormatName(wClipboard* clipboard, UINT32 formatId)
{
wClipboardFormat* format;
2016-10-06 14:31:25 +03:00
2014-11-20 20:08:01 +03:00
if (!clipboard)
return NULL;
2014-10-17 23:19:05 +04:00
format = ClipboardFindFormat(clipboard, formatId, NULL);
if (!format)
return NULL;
return format->formatName;
}
void* ClipboardGetData(wClipboard* clipboard, UINT32 formatId, UINT32* pSize)
2014-10-17 23:19:05 +04:00
{
UINT32 SrcSize = 0;
UINT32 DstSize = 0;
void* pSrcData = NULL;
void* pDstData = NULL;
2014-10-17 23:19:05 +04:00
wClipboardFormat* format;
wClipboardSynthesizer* synthesizer;
2016-10-06 14:31:25 +03:00
2014-11-20 20:08:01 +03:00
if (!clipboard)
return NULL;
2014-10-17 23:19:05 +04:00
if (!pSize)
return NULL;
*pSize = 0;
format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL);
2014-10-17 23:19:05 +04:00
if (!format)
return NULL;
SrcSize = clipboard->size;
2019-11-06 17:24:51 +03:00
pSrcData = (void*)clipboard->data;
if (formatId == format->formatId)
{
DstSize = SrcSize;
pDstData = malloc(DstSize);
2014-10-17 23:19:05 +04:00
if (!pDstData)
return NULL;
CopyMemory(pDstData, pSrcData, SrcSize);
*pSize = DstSize;
}
else
{
synthesizer = ClipboardFindSynthesizer(format, formatId);
if (!synthesizer || !synthesizer->pfnSynthesize)
return NULL;
DstSize = SrcSize;
2019-11-06 17:24:51 +03:00
pDstData = synthesizer->pfnSynthesize(clipboard, format->formatId, pSrcData, &DstSize);
2019-07-17 11:10:04 +03:00
if (pDstData)
*pSize = DstSize;
}
return pDstData;
2014-10-17 23:19:05 +04:00
}
2019-11-06 17:24:51 +03:00
BOOL ClipboardSetData(wClipboard* clipboard, UINT32 formatId, const void* data, UINT32 size)
2014-10-17 23:19:05 +04:00
{
wClipboardFormat* format;
2016-10-06 14:31:25 +03:00
2014-11-20 20:08:01 +03:00
if (!clipboard)
return FALSE;
2014-10-17 23:19:05 +04:00
format = ClipboardFindFormat(clipboard, formatId, NULL);
if (!format)
return FALSE;
2019-11-06 17:24:51 +03:00
free((void*)clipboard->data);
2016-10-06 14:31:25 +03:00
clipboard->data = malloc(size);
2014-10-17 23:19:05 +04:00
2016-10-06 14:31:25 +03:00
if (!clipboard->data)
return FALSE;
memcpy(clipboard->data, data, size);
clipboard->size = size;
2014-10-17 23:19:05 +04:00
clipboard->formatId = formatId;
clipboard->sequenceNumber++;
return TRUE;
}
UINT64 ClipboardGetOwner(wClipboard* clipboard)
{
2014-11-20 20:08:01 +03:00
if (!clipboard)
return 0;
2016-10-06 14:31:25 +03:00
2014-10-17 23:19:05 +04:00
return clipboard->ownerId;
}
void ClipboardSetOwner(wClipboard* clipboard, UINT64 ownerId)
{
2014-11-20 20:08:01 +03:00
if (!clipboard)
return;
2016-10-06 14:31:25 +03:00
2014-10-17 23:19:05 +04:00
clipboard->ownerId = ownerId;
}
wClipboardDelegate* ClipboardGetDelegate(wClipboard* clipboard)
{
if (!clipboard)
return NULL;
return &clipboard->delegate;
}
static void ClipboardInitLocalFileSubsystem(wClipboard* clipboard)
{
/*
* There can be only one local file subsystem active.
* Return as soon as initialization succeeds.
*/
#ifdef WITH_WCLIPBOARD_POSIX
if (ClipboardInitPosixFileSubsystem(clipboard))
{
WLog_INFO(TAG, "initialized POSIX local file subsystem");
return;
}
else
{
WLog_WARN(TAG, "failed to initialize POSIX local file subsystem");
}
#endif
WLog_INFO(TAG, "failed to initialize local file subsystem, file transfer not available");
}
wClipboard* ClipboardCreate(void)
2014-10-17 23:19:05 +04:00
{
wClipboard* clipboard;
2019-11-06 17:24:51 +03:00
clipboard = (wClipboard*)calloc(1, sizeof(wClipboard));
if (!clipboard)
return NULL;
2014-10-17 23:19:05 +04:00
clipboard->nextFormatId = 0xC000;
clipboard->sequenceNumber = 0;
2014-10-17 23:19:05 +04:00
if (!InitializeCriticalSectionAndSpinCount(&(clipboard->lock), 4000))
goto error_free_clipboard;
2014-10-17 23:19:05 +04:00
clipboard->numFormats = 0;
clipboard->maxFormats = 64;
2019-11-06 17:24:51 +03:00
clipboard->formats = (wClipboardFormat*)calloc(clipboard->maxFormats, sizeof(wClipboardFormat));
if (!clipboard->formats)
goto error_free_lock;
2014-10-17 23:19:05 +04:00
if (!ClipboardInitFormats(clipboard))
goto error_free_formats;
2014-10-17 23:19:05 +04:00
clipboard->delegate.clipboard = clipboard;
ClipboardInitLocalFileSubsystem(clipboard);
2014-10-17 23:19:05 +04:00
return clipboard;
error_free_formats:
free(clipboard->formats);
error_free_lock:
DeleteCriticalSection(&(clipboard->lock));
error_free_clipboard:
free(clipboard);
return NULL;
2014-10-17 23:19:05 +04:00
}
void ClipboardDestroy(wClipboard* clipboard)
{
UINT32 index;
wClipboardFormat* format;
if (!clipboard)
return;
wClipboard/posix: basic file list handling Here you can see an outline of our approach to handling file lists put on the clipboard. Typical usage of wClipboard by the clients sums up to doing a ClipboardSetData() call followed by a ClipboardGetData() call with appropriate format ID passed to them. Thus for files we would expect the clients to first set the local format (like "text/uri-list") and then to get the remote format (the "FileGroupDescriptorW"). MS-RDPECLIP has a concept of locally-stored list of files on the clipboard. This is modeled by clipboard->localFiles ArrayList. We need to populate this list before we serialize it into CLIPRDR_FILELIST and send it to the server. The easiest way to achieve this is a bit hacky, but it works: we populate the file list from inside the synthesizer callback registered for text/uri-list -> FileGroupDescriptorW conversion. So the client would first set the data it received from local clipboard as "text/uri-list" format, then it gets a "FileGroupDescriptorW" format, during that conversion we will prepare to serve file content requests, and in the end we provide a FILEDESCRIPTOR array to the client as the conversion result. The client will then serialize the array into CLIPRDR_FILELIST and sent it to the server. (We cannot do serialization in WinPR as WinPR should not know about cliprdr and its data formats.) The subsystems are expected to store their private structures in the clipboard->localFiles array. POSIX subsystem uses struct posix_file which currently has bare minimum of fields: the local file name (for open() and the like) and the remote file name (the one to put into FILEDESCRIPTOR).
2017-04-09 02:29:50 +03:00
ArrayList_Free(clipboard->localFiles);
clipboard->localFiles = NULL;
2014-10-17 23:19:05 +04:00
for (index = 0; index < clipboard->numFormats; index++)
{
format = &(clipboard->formats[index]);
2019-11-06 17:24:51 +03:00
free((void*)format->formatName);
if (format->synthesizers)
{
free(format->synthesizers);
format->synthesizers = NULL;
format->numSynthesizers = 0;
}
2014-10-17 23:19:05 +04:00
}
2019-11-06 17:24:51 +03:00
free((void*)clipboard->data);
clipboard->data = NULL;
clipboard->size = 0;
2014-10-17 23:19:05 +04:00
clipboard->numFormats = 0;
free(clipboard->formats);
DeleteCriticalSection(&(clipboard->lock));
free(clipboard);
}