FreeRDP/client/X11/xf_cliprdr.c

3119 lines
81 KiB
C
Raw Normal View History

2011-09-23 07:37:17 +04:00
/**
2012-10-09 07:02:04 +04:00
* FreeRDP: A Remote Desktop Protocol Implementation
2011-09-23 07:37:17 +04:00
* X11 Clipboard Redirection
*
* Copyright 2010-2011 Vic Lee
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
2011-09-23 07:37:17 +04:00
*
* 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.
*/
2022-02-16 13:20:38 +03:00
#include <freerdp/config.h>
2011-09-23 07:37:17 +04:00
#include <stdlib.h>
2021-01-18 10:51:51 +03:00
#include <errno.h>
2011-09-23 07:37:17 +04:00
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#ifdef WITH_XFIXES
#include <X11/extensions/Xfixes.h>
#endif
#ifdef WITH_FUSE
#define FUSE_USE_VERSION FUSE_API_VERSION
#if FUSE_USE_VERSION >= 30
#include <fuse3/fuse_lowlevel.h>
#else
#include <fuse/fuse_lowlevel.h>
#endif
#include <sys/mount.h>
#include <sys/stat.h>
#include <errno.h>
#include <time.h>
#endif
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/image.h>
#include <winpr/stream.h>
#include <winpr/clipboard.h>
#include <winpr/path.h>
2014-09-12 19:13:01 +04:00
#include <freerdp/log.h>
#include <freerdp/client/cliprdr.h>
#include <freerdp/channels/channels.h>
#include <freerdp/channels/cliprdr.h>
2011-09-23 07:37:17 +04:00
#include "xf_cliprdr.h"
2014-09-12 19:13:01 +04:00
#define TAG CLIENT_TAG("x11")
2019-11-06 17:24:51 +03:00
#define MAX_CLIPBOARD_FORMATS 255
2021-01-16 10:14:36 +03:00
#define WIN32_FILETIME_TO_UNIX_EPOCH_USEC UINT64_C(116444736000000000)
typedef struct
2011-09-23 07:37:17 +04:00
{
2014-10-15 23:49:57 +04:00
Atom atom;
UINT32 formatId;
char* formatName;
} xfCliprdrFormat;
2011-09-23 07:37:17 +04:00
#ifdef WITH_FUSE
typedef struct
{
UINT32 stream_id;
/* must be one of FILECONTENTS_SIZE or FILECONTENTS_RANGE*/
UINT32 req_type;
fuse_req_t req;
2021-01-16 10:14:36 +03:00
/*for FILECONTENTS_SIZE must be ino number* */
size_t req_ino;
} xfCliprdrFuseStream;
typedef struct
{
size_t parent_ino;
size_t ino;
size_t lindex;
mode_t st_mode;
off_t st_size;
BOOL size_set;
struct timespec st_mtim;
char* name;
wArrayList* child_inos;
} xfCliprdrFuseInode;
2021-06-17 10:21:20 +03:00
static void xf_cliprdr_fuse_inode_free(void* obj)
{
xfCliprdrFuseInode* inode = (xfCliprdrFuseInode*)obj;
if (!inode)
return;
free(inode->name);
ArrayList_Free(inode->child_inos);
inode->name = NULL;
inode->child_inos = NULL;
free(inode);
}
2021-01-16 10:14:36 +03:00
static inline xfCliprdrFuseInode* xf_cliprdr_fuse_util_get_inode(wArrayList* ino_list,
fuse_ino_t ino);
#endif
2014-10-15 06:24:07 +04:00
struct xf_clipboard
2011-09-23 07:37:17 +04:00
{
2014-10-15 06:24:07 +04:00
xfContext* xfc;
rdpChannels* channels;
CliprdrClientContext* context;
wClipboard* system;
wClipboardDelegate* delegate;
2011-09-23 07:37:17 +04:00
Window root_window;
Atom clipboard_atom;
Atom property_atom;
Atom timestamp_property_atom;
Time selection_ownership_timestamp;
Atom raw_transfer_atom;
Atom raw_format_list_atom;
2014-10-15 23:49:57 +04:00
int numClientFormats;
xfCliprdrFormat clientFormats[20];
int numServerFormats;
CLIPRDR_FORMAT* serverFormats;
2011-09-23 07:37:17 +04:00
2014-10-15 23:49:57 +04:00
int numTargets;
2011-09-23 07:37:17 +04:00
Atom targets[20];
2014-10-15 23:49:57 +04:00
int requestedFormatId;
BYTE* data;
BYTE* data_raw;
BOOL data_raw_format;
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
UINT32 data_format_id;
const char* data_format_name;
2011-09-23 07:37:17 +04:00
int data_length;
int data_raw_length;
XSelectionEvent* respond;
2011-09-23 07:37:17 +04:00
Window owner;
BOOL sync;
2011-09-23 07:37:17 +04:00
/* INCR mechanism */
Atom incr_atom;
BOOL incr_starts;
BYTE* incr_data;
2011-09-23 07:37:17 +04:00
int incr_data_length;
/* XFixes extension */
int xfixes_event_base;
int xfixes_error_base;
BOOL xfixes_supported;
/* File clipping */
BOOL streams_supported;
BOOL file_formats_registered;
UINT32 file_capability_flags;
/* last sent data */
CLIPRDR_FORMAT* lastSentFormats;
UINT32 lastSentNumFormats;
#ifdef WITH_FUSE
/* FUSE related**/
HANDLE fuse_thread;
struct fuse_session* fuse_sess;
/* fuse reset per copy*/
wArrayList* stream_list;
UINT32 current_stream_id;
wArrayList* ino_list;
#endif
2011-09-23 07:37:17 +04:00
};
static UINT xf_cliprdr_send_client_format_list(xfClipboard* clipboard);
static void xf_cliprdr_set_selection_owner(xfContext* xfc, xfClipboard* clipboard, Time timestamp);
2014-10-16 06:48:18 +04:00
static void xf_cliprdr_check_owner(xfClipboard* clipboard)
{
Window owner;
xfContext* xfc = clipboard->xfc;
if (clipboard->sync)
{
owner = XGetSelectionOwner(xfc->display, clipboard->clipboard_atom);
if (clipboard->owner != owner)
{
clipboard->owner = owner;
xf_cliprdr_send_client_format_list(clipboard);
}
}
}
2014-10-15 06:24:07 +04:00
static BOOL xf_cliprdr_is_self_owned(xfClipboard* clipboard)
2011-09-23 07:37:17 +04:00
{
2014-10-15 06:24:07 +04:00
xfContext* xfc = clipboard->xfc;
2019-11-06 17:24:51 +03:00
return XGetSelectionOwner(xfc->display, clipboard->clipboard_atom) == xfc->drawable;
2011-09-23 07:37:17 +04:00
}
2019-11-06 17:24:51 +03:00
static void xf_cliprdr_set_raw_transfer_enabled(xfClipboard* clipboard, BOOL enabled)
{
UINT32 data = enabled;
xfContext* xfc = clipboard->xfc;
2019-11-06 17:24:51 +03:00
XChangeProperty(xfc->display, xfc->drawable, clipboard->raw_transfer_atom, XA_INTEGER, 32,
PropModeReplace, (BYTE*)&data, 1);
}
static BOOL xf_cliprdr_is_raw_transfer_available(xfClipboard* clipboard)
{
Atom type;
int format;
int result = 0;
unsigned long length;
unsigned long bytes_left;
UINT32* data = NULL;
UINT32 is_enabled = 0;
Window owner = None;
xfContext* xfc = clipboard->xfc;
owner = XGetSelectionOwner(xfc->display, clipboard->clipboard_atom);
if (owner != None)
{
2019-11-06 17:24:51 +03:00
result =
XGetWindowProperty(xfc->display, owner, clipboard->raw_transfer_atom, 0, 4, 0,
XA_INTEGER, &type, &format, &length, &bytes_left, (BYTE**)&data);
}
if (data)
{
is_enabled = *data;
XFree(data);
}
if ((owner == None) || (owner == xfc->drawable))
return FALSE;
if (result != Success)
return FALSE;
return is_enabled ? TRUE : FALSE;
}
2019-11-06 17:24:51 +03:00
static BOOL xf_cliprdr_formats_equal(const CLIPRDR_FORMAT* server, const xfCliprdrFormat* client)
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
{
if (server->formatName && client->formatName)
{
/* The server may be using short format names while we store them in full form. */
2019-11-06 17:24:51 +03:00
return (0 == strncmp(server->formatName, client->formatName, strlen(server->formatName)));
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
}
if (!server->formatName && !client->formatName)
{
return (server->formatId == client->formatId);
}
return FALSE;
}
2021-03-10 13:36:23 +03:00
static const xfCliprdrFormat* xf_cliprdr_get_client_format_by_id(xfClipboard* clipboard,
UINT32 formatId)
2011-09-23 07:37:17 +04:00
{
int index;
2011-09-23 07:37:17 +04:00
2014-10-15 23:49:57 +04:00
for (index = 0; index < clipboard->numClientFormats; index++)
2011-09-23 07:37:17 +04:00
{
2021-03-10 13:36:23 +03:00
const xfCliprdrFormat* format = &(clipboard->clientFormats[index]);
2014-10-15 23:49:57 +04:00
if (format->formatId == formatId)
return format;
2011-09-23 07:37:17 +04:00
}
2014-10-15 23:49:57 +04:00
return NULL;
2011-09-23 07:37:17 +04:00
}
2021-03-10 13:36:23 +03:00
static const xfCliprdrFormat* xf_cliprdr_get_client_format_by_atom(xfClipboard* clipboard,
Atom atom)
2011-09-23 07:37:17 +04:00
{
int i;
2011-09-23 07:37:17 +04:00
2014-10-15 23:49:57 +04:00
for (i = 0; i < clipboard->numClientFormats; i++)
2011-09-23 07:37:17 +04:00
{
2021-03-10 13:36:23 +03:00
const xfCliprdrFormat* format = &(clipboard->clientFormats[i]);
2014-10-15 23:49:57 +04:00
if (format->atom == atom)
2014-10-15 23:49:57 +04:00
return format;
}
return NULL;
}
2021-03-10 13:36:23 +03:00
static const CLIPRDR_FORMAT* xf_cliprdr_get_server_format_by_atom(xfClipboard* clipboard, Atom atom)
{
2021-03-10 13:36:23 +03:00
int i;
for (i = 0; i < clipboard->numClientFormats; i++)
{
2021-03-10 13:36:23 +03:00
const xfCliprdrFormat* client_format = &(clipboard->clientFormats[i]);
2011-09-23 07:37:17 +04:00
if (client_format->atom == atom)
2011-09-23 07:37:17 +04:00
{
2021-03-10 13:36:23 +03:00
int j;
for (j = 0; j < clipboard->numServerFormats; j++)
{
2021-03-10 13:36:23 +03:00
const CLIPRDR_FORMAT* server_format = &(clipboard->serverFormats[j]);
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
if (xf_cliprdr_formats_equal(server_format, client_format))
return server_format;
}
2011-09-23 07:37:17 +04:00
}
}
2014-10-15 23:49:57 +04:00
return NULL;
2011-09-23 07:37:17 +04:00
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
2019-11-06 17:24:51 +03:00
static UINT xf_cliprdr_send_data_request(xfClipboard* clipboard, UINT32 formatId)
{
CLIPRDR_FORMAT_DATA_REQUEST request = { 0 };
request.requestedFormatId = formatId;
2019-11-06 17:24:51 +03:00
return clipboard->context->ClientFormatDataRequest(clipboard->context, &request);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT xf_cliprdr_send_data_response(xfClipboard* clipboard, const BYTE* data, size_t size)
{
CLIPRDR_FORMAT_DATA_RESPONSE response = { 0 };
/* No request currently pending, do not send a response. */
if (clipboard->requestedFormatId < 0)
return CHANNEL_RC_OK;
/* Request handled, reset to invalid */
clipboard->requestedFormatId = -1;
response.msgFlags = (data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
response.dataLen = size;
response.requestedFormatData = data;
2019-11-06 17:24:51 +03:00
return clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
}
static wStream* xf_cliprdr_serialize_server_format_list(xfClipboard* clipboard)
{
UINT32 i;
UINT32 formatCount;
wStream* s = NULL;
/* Typical MS Word format list is about 80 bytes long. */
if (!(s = Stream_New(NULL, 128)))
{
WLog_ERR(TAG, "failed to allocate serialized format list");
goto error;
}
/* If present, the last format is always synthetic CF_RAW. Do not include it. */
2019-11-06 17:24:51 +03:00
formatCount = (clipboard->numServerFormats > 0) ? clipboard->numServerFormats - 1 : 0;
Stream_Write_UINT32(s, formatCount);
for (i = 0; i < formatCount; i++)
{
CLIPRDR_FORMAT* format = &clipboard->serverFormats[i];
size_t name_length = format->formatName ? strlen(format->formatName) : 0;
if (!Stream_EnsureRemainingCapacity(s, sizeof(UINT32) + name_length + 1))
{
WLog_ERR(TAG, "failed to expand serialized format list");
goto error;
}
Stream_Write_UINT32(s, format->formatId);
if (format->formatName)
Stream_Write(s, format->formatName, name_length);
Stream_Write_UINT8(s, '\0');
}
Stream_SealLength(s);
return s;
error:
Stream_Free(s, TRUE);
return NULL;
}
2019-11-06 17:24:51 +03:00
static CLIPRDR_FORMAT* xf_cliprdr_parse_server_format_list(BYTE* data, size_t length,
UINT32* numFormats)
{
UINT32 i;
wStream* s = NULL;
CLIPRDR_FORMAT* formats = NULL;
if (!(s = Stream_New(data, length)))
{
WLog_ERR(TAG, "failed to allocate stream for parsing serialized format list");
goto error;
}
if (Stream_GetRemainingLength(s) < sizeof(UINT32))
{
WLog_ERR(TAG, "too short serialized format list");
goto error;
}
Stream_Read_UINT32(s, *numFormats);
if (*numFormats > MAX_CLIPBOARD_FORMATS)
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "unexpectedly large number of formats: %" PRIu32 "", *numFormats);
goto error;
}
2019-11-06 17:24:51 +03:00
if (!(formats = (CLIPRDR_FORMAT*)calloc(*numFormats, sizeof(CLIPRDR_FORMAT))))
{
WLog_ERR(TAG, "failed to allocate format list");
goto error;
}
for (i = 0; i < *numFormats; i++)
{
const char* formatName = NULL;
size_t formatNameLength = 0;
if (Stream_GetRemainingLength(s) < sizeof(UINT32))
{
WLog_ERR(TAG, "unexpected end of serialized format list");
goto error;
}
Stream_Read_UINT32(s, formats[i].formatId);
2019-11-06 17:24:51 +03:00
formatName = (const char*)Stream_Pointer(s);
formatNameLength = strnlen(formatName, Stream_GetRemainingLength(s));
if (formatNameLength == Stream_GetRemainingLength(s))
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "missing terminating null byte, %" PRIuz " bytes left to read",
2016-10-06 14:31:25 +03:00
formatNameLength);
goto error;
}
formats[i].formatName = strndup(formatName, formatNameLength);
Stream_Seek(s, formatNameLength + 1);
}
Stream_Free(s, FALSE);
return formats;
error:
Stream_Free(s, FALSE);
free(formats);
*numFormats = 0;
return NULL;
}
static void xf_cliprdr_free_formats(CLIPRDR_FORMAT* formats, UINT32 numFormats)
{
UINT32 i;
for (i = 0; i < numFormats; i++)
{
free(formats[i].formatName);
}
free(formats);
}
2019-11-06 17:24:51 +03:00
static CLIPRDR_FORMAT* xf_cliprdr_get_raw_server_formats(xfClipboard* clipboard, UINT32* numFormats)
{
Atom type = None;
int format = 0;
unsigned long length = 0;
unsigned long remaining;
BYTE* data = NULL;
CLIPRDR_FORMAT* formats = NULL;
xfContext* xfc = clipboard->xfc;
*numFormats = 0;
2019-11-06 17:24:51 +03:00
XGetWindowProperty(xfc->display, clipboard->owner, clipboard->raw_format_list_atom, 0, 4096,
False, clipboard->raw_format_list_atom, &type, &format, &length, &remaining,
&data);
2019-11-06 17:24:51 +03:00
if (data && length > 0 && format == 8 && type == clipboard->raw_format_list_atom)
{
formats = xf_cliprdr_parse_server_format_list(data, length, numFormats);
}
else
{
2016-10-06 14:31:25 +03:00
WLog_ERR(TAG,
2019-11-06 17:24:51 +03:00
"failed to retrieve raw format list: data=%p, length=%lu, format=%d, type=%lu "
"(expected=%lu)",
(void*)data, length, format, (unsigned long)type,
(unsigned long)clipboard->raw_format_list_atom);
}
if (data)
XFree(data);
return formats;
}
2019-11-06 17:24:51 +03:00
static CLIPRDR_FORMAT* xf_cliprdr_get_formats_from_targets(xfClipboard* clipboard,
UINT32* numFormats)
{
2019-02-07 16:40:36 +03:00
unsigned long i;
Atom atom;
BYTE* data = NULL;
int format_property;
unsigned long length;
unsigned long bytes_left;
CLIPRDR_FORMAT* formats = NULL;
xfContext* xfc = clipboard->xfc;
*numFormats = 0;
2019-11-06 17:24:51 +03:00
XGetWindowProperty(xfc->display, xfc->drawable, clipboard->property_atom, 0, 200, 0, XA_ATOM,
&atom, &format_property, &length, &bytes_left, &data);
if (length > 0)
{
if (!data)
{
WLog_ERR(TAG, "XGetWindowProperty set length = %lu but data is NULL", length);
goto out;
}
2016-10-06 14:31:25 +03:00
2019-11-06 17:24:51 +03:00
if (!(formats = (CLIPRDR_FORMAT*)calloc(length, sizeof(CLIPRDR_FORMAT))))
{
WLog_ERR(TAG, "failed to allocate %lu CLIPRDR_FORMAT structs", length);
goto out;
}
}
for (i = 0; i < length; i++)
{
2021-03-10 13:36:23 +03:00
Atom tatom = ((Atom*)data)[i];
const xfCliprdrFormat* format = xf_cliprdr_get_client_format_by_atom(clipboard, tatom);
if (format)
{
formats[*numFormats].formatId = format->formatId;
formats[*numFormats].formatName = _strdup(format->formatName);
*numFormats += 1;
}
}
out:
2016-10-06 14:31:25 +03:00
if (data)
XFree(data);
return formats;
}
2019-11-06 17:24:51 +03:00
static CLIPRDR_FORMAT* xf_cliprdr_get_client_formats(xfClipboard* clipboard, UINT32* numFormats)
{
CLIPRDR_FORMAT* formats = NULL;
*numFormats = 0;
if (xf_cliprdr_is_raw_transfer_available(clipboard))
{
formats = xf_cliprdr_get_raw_server_formats(clipboard, numFormats);
}
if (*numFormats == 0)
{
xf_cliprdr_free_formats(formats, *numFormats);
formats = xf_cliprdr_get_formats_from_targets(clipboard, numFormats);
}
return formats;
}
static void xf_cliprdr_provide_server_format_list(xfClipboard* clipboard)
{
wStream* formats = NULL;
xfContext* xfc = clipboard->xfc;
formats = xf_cliprdr_serialize_server_format_list(clipboard);
if (formats)
{
XChangeProperty(xfc->display, xfc->drawable, clipboard->raw_format_list_atom,
2019-11-06 17:24:51 +03:00
clipboard->raw_format_list_atom, 8, PropModeReplace, Stream_Buffer(formats),
Stream_Length(formats));
}
else
{
XDeleteProperty(xfc->display, xfc->drawable, clipboard->raw_format_list_atom);
}
Stream_Free(formats, TRUE);
}
static BOOL xf_clipboard_format_equal(const CLIPRDR_FORMAT* a, const CLIPRDR_FORMAT* b)
{
if (a->formatId != b->formatId)
return FALSE;
if (!a->formatName && !b->formatName)
return TRUE;
return strcmp(a->formatName, b->formatName) == 0;
}
static BOOL xf_clipboard_changed(xfClipboard* clipboard, const CLIPRDR_FORMAT* formats,
UINT32 numFormats)
{
UINT32 x, y;
if (clipboard->lastSentNumFormats != numFormats)
return TRUE;
for (x = 0; x < numFormats; x++)
{
const CLIPRDR_FORMAT* cur = &clipboard->lastSentFormats[x];
BOOL contained = FALSE;
for (y = 0; y < numFormats; y++)
{
if (xf_clipboard_format_equal(cur, &formats[y]))
{
contained = TRUE;
break;
}
}
if (!contained)
return TRUE;
}
return FALSE;
}
static void xf_clipboard_formats_free(xfClipboard* clipboard)
{
xf_cliprdr_free_formats(clipboard->lastSentFormats, clipboard->lastSentNumFormats);
clipboard->lastSentFormats = NULL;
clipboard->lastSentNumFormats = 0;
}
static BOOL xf_clipboard_copy_formats(xfClipboard* clipboard, const CLIPRDR_FORMAT* formats,
UINT32 numFormats)
{
UINT32 x;
xf_clipboard_formats_free(clipboard);
clipboard->lastSentFormats = calloc(numFormats, sizeof(CLIPRDR_FORMAT));
if (!clipboard->lastSentFormats)
return FALSE;
clipboard->lastSentNumFormats = numFormats;
for (x = 0; x < numFormats; x++)
{
CLIPRDR_FORMAT* lcur = &clipboard->lastSentFormats[x];
const CLIPRDR_FORMAT* cur = &formats[x];
*lcur = *cur;
if (cur->formatName)
lcur->formatName = _strdup(cur->formatName);
}
return FALSE;
}
static UINT xf_cliprdr_send_format_list(xfClipboard* clipboard, const CLIPRDR_FORMAT* formats,
UINT32 numFormats)
{
CLIPRDR_FORMAT_LIST formatList = { 0 };
formatList.msgFlags = CB_RESPONSE_OK;
formatList.numFormats = numFormats;
formatList.formats = (CLIPRDR_FORMAT*)formats;
formatList.msgType = CB_FORMAT_LIST;
if (!xf_clipboard_changed(clipboard, formats, numFormats))
return CHANNEL_RC_OK;
xf_clipboard_copy_formats(clipboard, formats, numFormats);
/* Ensure all pending requests are answered. */
xf_cliprdr_send_data_response(clipboard, NULL, 0);
return clipboard->context->ClientFormatList(clipboard->context, &formatList);
}
static void xf_cliprdr_get_requested_targets(xfClipboard* clipboard)
{
UINT32 numFormats = 0;
CLIPRDR_FORMAT* formats = NULL;
formats = xf_cliprdr_get_client_formats(clipboard, &numFormats);
xf_cliprdr_send_format_list(clipboard, formats, numFormats);
xf_cliprdr_free_formats(formats, numFormats);
}
2019-11-06 17:24:51 +03:00
static void xf_cliprdr_process_requested_data(xfClipboard* clipboard, BOOL hasData, BYTE* data,
int size)
2011-09-23 07:37:17 +04:00
{
BOOL bSuccess;
UINT32 SrcSize;
2021-09-08 16:47:03 +03:00
UINT32 DstSize = 0;
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
UINT32 srcFormatId;
UINT32 dstFormatId;
BYTE* pDstData = NULL;
2021-03-10 13:36:23 +03:00
const xfCliprdrFormat* format;
2011-09-23 07:37:17 +04:00
if (clipboard->incr_starts && hasData)
2011-09-23 07:37:17 +04:00
return;
2019-11-06 17:24:51 +03:00
format = xf_cliprdr_get_client_format_by_id(clipboard, clipboard->requestedFormatId);
2014-10-15 23:49:57 +04:00
if (!hasData || !data || !format)
2011-09-23 07:37:17 +04:00
{
2014-10-15 23:49:57 +04:00
xf_cliprdr_send_data_response(clipboard, NULL, 0);
2011-09-23 07:37:17 +04:00
return;
}
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
srcFormatId = 0;
2014-10-15 23:49:57 +04:00
switch (format->formatId)
2011-09-23 07:37:17 +04:00
{
case CF_RAW:
srcFormatId = CF_RAW;
break;
case CF_TEXT:
case CF_OEMTEXT:
case CF_UNICODETEXT:
2019-11-06 17:24:51 +03:00
size = strlen((char*)data) + 1;
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
srcFormatId = ClipboardGetFormatId(clipboard->system, "UTF8_STRING");
2011-09-23 07:37:17 +04:00
break;
case CF_DIB:
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
srcFormatId = ClipboardGetFormatId(clipboard->system, "image/bmp");
2011-09-23 07:37:17 +04:00
break;
case CB_FORMAT_HTML:
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
srcFormatId = ClipboardGetFormatId(clipboard->system, "text/html");
2011-09-23 07:37:17 +04:00
break;
case CB_FORMAT_TEXTURILIST:
srcFormatId = ClipboardGetFormatId(clipboard->system, "text/uri-list");
break;
}
2019-11-06 17:24:51 +03:00
SrcSize = (UINT32)size;
2016-10-06 14:31:25 +03:00
bSuccess = ClipboardSetData(clipboard->system, srcFormatId, data, SrcSize);
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
if (format->formatName)
dstFormatId = ClipboardGetFormatId(clipboard->system, format->formatName);
else
dstFormatId = format->formatId;
2011-09-23 07:37:17 +04:00
if (bSuccess)
{
DstSize = 0;
2019-11-06 17:24:51 +03:00
pDstData = (BYTE*)ClipboardGetData(clipboard->system, dstFormatId, &DstSize);
}
if (!pDstData)
{
xf_cliprdr_send_data_response(clipboard, NULL, 0);
return;
}
/*
* File lists require a bit of postprocessing to convert them from WinPR's FILDESCRIPTOR
* format to CLIPRDR_FILELIST expected by the server.
*
* We check for "FileGroupDescriptorW" format being registered (i.e., nonzero) in order
* to not process CF_RAW as a file list in case WinPR does not support file transfers.
*/
if (dstFormatId &&
(dstFormatId == ClipboardGetFormatId(clipboard->system, "FileGroupDescriptorW")))
{
UINT error = NO_ERROR;
2020-09-17 16:21:45 +03:00
FILEDESCRIPTORW* file_array = (FILEDESCRIPTORW*)pDstData;
UINT32 file_count = DstSize / sizeof(FILEDESCRIPTORW);
pDstData = NULL;
DstSize = 0;
error = cliprdr_serialize_file_list_ex(clipboard->file_capability_flags, file_array,
file_count, &pDstData, &DstSize);
if (error)
WLog_ERR(TAG, "failed to serialize CLIPRDR_FILELIST: 0x%08X", error);
free(file_array);
}
xf_cliprdr_send_data_response(clipboard, pDstData, DstSize);
free(pDstData);
2011-09-23 07:37:17 +04:00
}
2014-10-15 06:24:07 +04:00
static BOOL xf_cliprdr_get_requested_data(xfClipboard* clipboard, Atom target)
2011-09-23 07:37:17 +04:00
{
Atom type;
BYTE* data = NULL;
BOOL has_data = FALSE;
2014-10-15 23:49:57 +04:00
int format_property;
unsigned long dummy;
unsigned long length;
unsigned long bytes_left;
2021-03-10 13:36:23 +03:00
const xfCliprdrFormat* format;
2014-10-15 06:24:07 +04:00
xfContext* xfc = clipboard->xfc;
2019-11-06 17:24:51 +03:00
format = xf_cliprdr_get_client_format_by_id(clipboard, clipboard->requestedFormatId);
2014-10-15 23:49:57 +04:00
if (!format || (format->atom != target))
2011-09-23 07:37:17 +04:00
{
2014-10-15 23:49:57 +04:00
xf_cliprdr_send_data_response(clipboard, NULL, 0);
return FALSE;
2011-09-23 07:37:17 +04:00
}
2019-11-06 17:24:51 +03:00
XGetWindowProperty(xfc->display, xfc->drawable, clipboard->property_atom, 0, 0, 0, target,
2016-10-06 14:31:25 +03:00
&type, &format_property, &length, &bytes_left, &data);
2011-09-23 07:37:17 +04:00
if (data)
{
XFree(data);
data = NULL;
}
2014-10-15 06:24:07 +04:00
if (bytes_left <= 0 && !clipboard->incr_starts)
2011-09-23 07:37:17 +04:00
{
}
2014-10-15 06:24:07 +04:00
else if (type == clipboard->incr_atom)
2011-09-23 07:37:17 +04:00
{
2014-10-15 06:24:07 +04:00
clipboard->incr_starts = TRUE;
2014-10-15 06:24:07 +04:00
if (clipboard->incr_data)
2011-09-23 07:37:17 +04:00
{
2014-10-15 06:24:07 +04:00
free(clipboard->incr_data);
clipboard->incr_data = NULL;
2011-09-23 07:37:17 +04:00
}
2014-10-15 06:24:07 +04:00
clipboard->incr_data_length = 0;
has_data = TRUE; /* data will be followed in PropertyNotify event */
2011-09-23 07:37:17 +04:00
}
else
{
if (bytes_left <= 0)
{
/* INCR finish */
2014-10-15 06:24:07 +04:00
data = clipboard->incr_data;
clipboard->incr_data = NULL;
bytes_left = clipboard->incr_data_length;
clipboard->incr_data_length = 0;
clipboard->incr_starts = 0;
has_data = TRUE;
2011-09-23 07:37:17 +04:00
}
2019-11-06 17:24:51 +03:00
else if (XGetWindowProperty(xfc->display, xfc->drawable, clipboard->property_atom, 0,
bytes_left, 0, target, &type, &format_property, &length, &dummy,
&data) == Success)
2011-09-23 07:37:17 +04:00
{
2014-10-15 06:24:07 +04:00
if (clipboard->incr_starts)
2011-09-23 07:37:17 +04:00
{
2016-10-06 14:31:25 +03:00
BYTE* new_data;
2014-10-15 23:49:57 +04:00
bytes_left = length * format_property / 8;
2019-11-06 17:24:51 +03:00
new_data =
(BYTE*)realloc(clipboard->incr_data, clipboard->incr_data_length + bytes_left);
2016-10-06 14:31:25 +03:00
if (new_data)
{
2016-10-06 14:31:25 +03:00
clipboard->incr_data = new_data;
CopyMemory(clipboard->incr_data + clipboard->incr_data_length, data,
bytes_left);
clipboard->incr_data_length += bytes_left;
XFree(data);
data = NULL;
}
2011-09-23 07:37:17 +04:00
}
2016-10-06 14:31:25 +03:00
has_data = TRUE;
2011-09-23 07:37:17 +04:00
}
else
{
}
}
2014-10-15 23:49:57 +04:00
2014-10-15 06:24:07 +04:00
XDeleteProperty(xfc->display, xfc->drawable, clipboard->property_atom);
xf_cliprdr_process_requested_data(clipboard, has_data, data, bytes_left);
2011-09-23 07:37:17 +04:00
if (data)
XFree(data);
return TRUE;
2011-09-23 07:37:17 +04:00
}
2014-10-15 06:24:07 +04:00
static void xf_cliprdr_append_target(xfClipboard* clipboard, Atom target)
2011-09-23 07:37:17 +04:00
{
int i;
2019-02-07 16:40:36 +03:00
if (clipboard->numTargets < 0)
return;
if ((size_t)clipboard->numTargets >= ARRAYSIZE(clipboard->targets))
2011-09-23 07:37:17 +04:00
return;
2014-10-15 23:49:57 +04:00
for (i = 0; i < clipboard->numTargets; i++)
2011-09-23 07:37:17 +04:00
{
2014-10-15 06:24:07 +04:00
if (clipboard->targets[i] == target)
2011-09-23 07:37:17 +04:00
return;
}
2014-10-15 23:49:57 +04:00
clipboard->targets[clipboard->numTargets++] = target;
2011-09-23 07:37:17 +04:00
}
static void xf_cliprdr_provide_targets(xfClipboard* clipboard, const XSelectionEvent* respond)
2011-09-23 07:37:17 +04:00
{
2014-10-15 06:24:07 +04:00
xfContext* xfc = clipboard->xfc;
2011-09-23 07:37:17 +04:00
if (respond->property != None)
2011-09-23 07:37:17 +04:00
{
XChangeProperty(xfc->display, respond->requestor, respond->property, XA_ATOM, 32,
PropModeReplace, (BYTE*)clipboard->targets, clipboard->numTargets);
2011-09-23 07:37:17 +04:00
}
}
static void xf_cliprdr_provide_timestamp(xfClipboard* clipboard, const XSelectionEvent* respond)
{
xfContext* xfc = clipboard->xfc;
if (respond->property != None)
{
XChangeProperty(xfc->display, respond->requestor, respond->property, XA_INTEGER, 32,
PropModeReplace, (BYTE*)&clipboard->selection_ownership_timestamp, 1);
}
}
static void xf_cliprdr_provide_data(xfClipboard* clipboard, const XSelectionEvent* respond,
const BYTE* data, UINT32 size)
2011-09-23 07:37:17 +04:00
{
2014-10-15 06:24:07 +04:00
xfContext* xfc = clipboard->xfc;
2011-09-23 07:37:17 +04:00
if (respond->property != None)
2011-09-23 07:37:17 +04:00
{
XChangeProperty(xfc->display, respond->requestor, respond->property, respond->target, 8,
PropModeReplace, data, size);
2011-09-23 07:37:17 +04:00
}
}
static BOOL xf_cliprdr_process_selection_notify(xfClipboard* clipboard,
const XSelectionEvent* xevent)
2011-09-23 07:37:17 +04:00
{
if (xevent->target == clipboard->targets[1])
2011-09-23 07:37:17 +04:00
{
if (xevent->property == None)
2011-09-23 07:37:17 +04:00
{
2014-10-16 23:05:06 +04:00
xf_cliprdr_send_client_format_list(clipboard);
2011-09-23 07:37:17 +04:00
}
else
{
2014-10-15 06:24:07 +04:00
xf_cliprdr_get_requested_targets(clipboard);
2011-09-23 07:37:17 +04:00
}
return TRUE;
2011-09-23 07:37:17 +04:00
}
else
{
return xf_cliprdr_get_requested_data(clipboard, xevent->target);
2011-09-23 07:37:17 +04:00
}
}
static void xf_cliprdr_clear_cached_data(xfClipboard* clipboard)
{
if (clipboard->data)
{
free(clipboard->data);
clipboard->data = NULL;
}
clipboard->data_length = 0;
if (clipboard->data_raw)
{
free(clipboard->data_raw);
clipboard->data_raw = NULL;
}
clipboard->data_raw_length = 0;
#ifdef WITH_FUSE
if (clipboard->stream_list)
{
size_t index;
size_t count;
xfCliprdrFuseStream* stream;
ArrayList_Lock(clipboard->stream_list);
clipboard->current_stream_id = 0;
/* reply error to all req first don't care request type*/
count = ArrayList_Count(clipboard->stream_list);
for (index = 0; index < count; index++)
{
stream = (xfCliprdrFuseStream*)ArrayList_GetItem(clipboard->stream_list, index);
fuse_reply_err(stream->req, EIO);
}
ArrayList_Unlock(clipboard->stream_list);
ArrayList_Clear(clipboard->stream_list);
}
if (clipboard->ino_list)
{
ArrayList_Clear(clipboard->ino_list);
}
#endif
}
static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard,
const XSelectionRequestEvent* xevent)
2011-09-23 07:37:17 +04:00
{
int fmt;
Atom type;
2014-10-15 23:49:57 +04:00
UINT32 formatId;
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
const char* formatName;
XSelectionEvent* respond;
BYTE* data = NULL;
BOOL delayRespond;
BOOL rawTransfer;
BOOL matchingFormat;
2014-10-15 23:49:57 +04:00
unsigned long length;
unsigned long bytes_left;
2014-10-15 06:24:07 +04:00
xfContext* xfc = clipboard->xfc;
2011-09-23 07:37:17 +04:00
if (xevent->owner != xfc->drawable)
return FALSE;
2011-09-23 07:37:17 +04:00
delayRespond = FALSE;
if (!(respond = (XSelectionEvent*)calloc(1, sizeof(XSelectionEvent))))
{
WLog_ERR(TAG, "failed to allocate XEvent data");
return FALSE;
}
respond->property = None;
respond->type = SelectionNotify;
respond->display = xevent->display;
respond->requestor = xevent->requestor;
respond->selection = xevent->selection;
respond->target = xevent->target;
respond->time = xevent->time;
if (xevent->target == clipboard->targets[0]) /* TIMESTAMP */
2011-09-23 07:37:17 +04:00
{
/* Someone else requests the selection's timestamp */
respond->property = xevent->property;
xf_cliprdr_provide_timestamp(clipboard, respond);
2011-09-23 07:37:17 +04:00
}
else if (xevent->target == clipboard->targets[1]) /* TARGETS */
2011-09-23 07:37:17 +04:00
{
/* Someone else requests our available formats */
respond->property = xevent->property;
2014-10-15 06:24:07 +04:00
xf_cliprdr_provide_targets(clipboard, respond);
2011-09-23 07:37:17 +04:00
}
else
{
2021-03-10 13:36:23 +03:00
const CLIPRDR_FORMAT* format =
xf_cliprdr_get_server_format_by_atom(clipboard, xevent->target);
if (format && (xevent->requestor != xfc->drawable))
2011-09-23 07:37:17 +04:00
{
2014-10-15 23:49:57 +04:00
formatId = format->formatId;
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
formatName = format->formatName;
rawTransfer = FALSE;
if (formatId == CF_RAW)
2011-09-23 07:37:17 +04:00
{
if (XGetWindowProperty(xfc->display, xevent->requestor, clipboard->property_atom, 0,
4, 0, XA_INTEGER, &type, &fmt, &length, &bytes_left,
&data) != Success)
2011-09-23 07:37:17 +04:00
{
}
2014-10-16 06:48:18 +04:00
2011-09-23 07:37:17 +04:00
if (data)
{
rawTransfer = TRUE;
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
CopyMemory(&formatId, data, 4);
2011-09-23 07:37:17 +04:00
XFree(data);
}
}
/* We can compare format names by pointer value here as they are both
* taken from the same clipboard->serverFormats array */
2019-11-06 17:24:51 +03:00
matchingFormat = (formatId == clipboard->data_format_id) &&
(formatName == clipboard->data_format_name);
if (matchingFormat && (clipboard->data != 0) && !rawTransfer)
2011-09-23 07:37:17 +04:00
{
/* Cached converted clipboard data available. Send it now */
respond->property = xevent->property;
2016-10-06 14:31:25 +03:00
xf_cliprdr_provide_data(clipboard, respond, clipboard->data,
clipboard->data_length);
2011-09-23 07:37:17 +04:00
}
else if (matchingFormat && (clipboard->data_raw != 0) && rawTransfer)
{
/* Cached raw clipboard data available. Send it now */
respond->property = xevent->property;
2019-11-06 17:24:51 +03:00
xf_cliprdr_provide_data(clipboard, respond, clipboard->data_raw,
clipboard->data_raw_length);
}
2014-10-15 06:24:07 +04:00
else if (clipboard->respond)
2011-09-23 07:37:17 +04:00
{
2014-10-16 06:48:18 +04:00
/* duplicate request */
2011-09-23 07:37:17 +04:00
}
else
{
/**
* Send clipboard data request to the server.
* Response will be postponed after receiving the data
*/
xf_cliprdr_clear_cached_data(clipboard);
respond->property = xevent->property;
2014-10-15 06:24:07 +04:00
clipboard->respond = respond;
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
clipboard->data_format_id = formatId;
clipboard->data_format_name = formatName;
clipboard->data_raw_format = rawTransfer;
delayRespond = TRUE;
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
xf_cliprdr_send_data_request(clipboard, formatId);
2011-09-23 07:37:17 +04:00
}
}
}
if (!delayRespond)
2011-09-23 07:37:17 +04:00
{
union
{
XEvent* ev;
XSelectionEvent* sev;
} conv;
conv.sev = respond;
XSendEvent(xfc->display, xevent->requestor, 0, 0, conv.ev);
XFlush(xfc->display);
free(respond);
2011-09-23 07:37:17 +04:00
}
return TRUE;
2011-09-23 07:37:17 +04:00
}
static BOOL xf_cliprdr_process_selection_clear(xfClipboard* clipboard,
const XSelectionClearEvent* xevent)
2011-09-23 07:37:17 +04:00
{
2014-10-15 06:24:07 +04:00
xfContext* xfc = clipboard->xfc;
2011-09-23 07:37:17 +04:00
2019-02-07 19:53:21 +03:00
WINPR_UNUSED(xevent);
2014-10-15 06:24:07 +04:00
if (xf_cliprdr_is_self_owned(clipboard))
return FALSE;
2011-09-23 07:37:17 +04:00
2014-10-15 06:24:07 +04:00
XDeleteProperty(xfc->display, clipboard->root_window, clipboard->property_atom);
return TRUE;
2011-09-23 07:37:17 +04:00
}
static BOOL xf_cliprdr_process_property_notify(xfClipboard* clipboard, const XPropertyEvent* xevent)
2011-09-23 07:37:17 +04:00
{
2021-03-10 13:36:23 +03:00
const xfCliprdrFormat* format;
make cppcheck even more happier: [channels/tsmf/client/gstreamer/tsmf_X11.c:317] -> [channels/tsmf/client/gstreamer/tsmf_X11.c:322]: (warning) Either the condition '!decoder' is redundant or there is possible null pointer dereference: decoder. [channels/tsmf/client/gstreamer/tsmf_X11.c:470] -> [channels/tsmf/client/gstreamer/tsmf_X11.c:475]: (warning) Either the condition '!decoder' is redundant or there is possible null pointer dereference: decoder. [channels/tsmf/client/gstreamer/tsmf_X11.c:472] -> [channels/tsmf/client/gstreamer/tsmf_X11.c:475]: (warning) Either the condition '!decoder' is redundant or there is possible null pointer dereference: decoder. [channels/tsmf/client/tsmf_media.c:179] -> [channels/tsmf/client/tsmf_media.c:181]: (warning) Either the condition '!stream' is redundant or there is possible null pointer dereference: stream. [client/Windows/wf_cliprdr.c:2219] -> [client/Windows/wf_cliprdr.c:2222]: (warning) Either the condition '!formatDataResponse' is redundant or there is possible null pointer dereference: formatDataResponse [client/Windows/wf_cliprdr.c:2445] -> [client/Windows/wf_cliprdr.c:2448]: (warning) Either the condition '!fileContentsResponse' is redundant or there is possible null pointer dereference: fileContentsResponse. [client/X11/xf_cliprdr.c:911] -> [client/X11/xf_cliprdr.c:913]: (warning) Either the condition '!clipboard' is redundant or there is possible null pointer dereference: clipboard. [client/X11/xf_graphics.c:504] -> [client/X11/xf_graphics.c:506]: (warning) Either the condition '!xfc' is redundant or there is possible null pointer dereference: xfc. [libfreerdp/core/transport.c:861] -> [libfreerdp/core/transport.c:863]: (warning) Either the condition '!transport' is redundant or there is possible null pointer dereference: transport. [server/shadow/shadow_server.c:777] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:778] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:779] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:781] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:782] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:783] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:784] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:785] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:787] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:789] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server.
2017-01-26 12:44:19 +03:00
xfContext* xfc = NULL;
2011-09-23 07:37:17 +04:00
2014-10-15 06:24:07 +04:00
if (!clipboard)
return TRUE;
make cppcheck even more happier: [channels/tsmf/client/gstreamer/tsmf_X11.c:317] -> [channels/tsmf/client/gstreamer/tsmf_X11.c:322]: (warning) Either the condition '!decoder' is redundant or there is possible null pointer dereference: decoder. [channels/tsmf/client/gstreamer/tsmf_X11.c:470] -> [channels/tsmf/client/gstreamer/tsmf_X11.c:475]: (warning) Either the condition '!decoder' is redundant or there is possible null pointer dereference: decoder. [channels/tsmf/client/gstreamer/tsmf_X11.c:472] -> [channels/tsmf/client/gstreamer/tsmf_X11.c:475]: (warning) Either the condition '!decoder' is redundant or there is possible null pointer dereference: decoder. [channels/tsmf/client/tsmf_media.c:179] -> [channels/tsmf/client/tsmf_media.c:181]: (warning) Either the condition '!stream' is redundant or there is possible null pointer dereference: stream. [client/Windows/wf_cliprdr.c:2219] -> [client/Windows/wf_cliprdr.c:2222]: (warning) Either the condition '!formatDataResponse' is redundant or there is possible null pointer dereference: formatDataResponse [client/Windows/wf_cliprdr.c:2445] -> [client/Windows/wf_cliprdr.c:2448]: (warning) Either the condition '!fileContentsResponse' is redundant or there is possible null pointer dereference: fileContentsResponse. [client/X11/xf_cliprdr.c:911] -> [client/X11/xf_cliprdr.c:913]: (warning) Either the condition '!clipboard' is redundant or there is possible null pointer dereference: clipboard. [client/X11/xf_graphics.c:504] -> [client/X11/xf_graphics.c:506]: (warning) Either the condition '!xfc' is redundant or there is possible null pointer dereference: xfc. [libfreerdp/core/transport.c:861] -> [libfreerdp/core/transport.c:863]: (warning) Either the condition '!transport' is redundant or there is possible null pointer dereference: transport. [server/shadow/shadow_server.c:777] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:778] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:779] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:781] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:782] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:783] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:784] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:785] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:787] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server. [server/shadow/shadow_server.c:789] -> [server/shadow/shadow_server.c:791]: (warning) Either the condition '!server' is redundant or there is possible null pointer dereference: server.
2017-01-26 12:44:19 +03:00
xfc = clipboard->xfc;
if (xevent->atom == clipboard->timestamp_property_atom)
{
/* This is the response to the property change we did
* in xf_cliprdr_prepare_to_set_selection_owner. Now
* we can set ourselves as the selection owner. (See
* comments in those functions below.) */
xf_cliprdr_set_selection_owner(xfc, clipboard, xevent->time);
return TRUE;
}
if (xevent->atom != clipboard->property_atom)
return FALSE; /* Not cliprdr-related */
2011-09-23 07:37:17 +04:00
if (xevent->window == clipboard->root_window)
2011-09-23 07:37:17 +04:00
{
2014-10-16 06:48:18 +04:00
xf_cliprdr_send_client_format_list(clipboard);
2011-09-23 07:37:17 +04:00
}
else if ((xevent->window == xfc->drawable) && (xevent->state == PropertyNewValue) &&
clipboard->incr_starts)
2011-09-23 07:37:17 +04:00
{
2019-11-06 17:24:51 +03:00
format = xf_cliprdr_get_client_format_by_id(clipboard, clipboard->requestedFormatId);
2014-10-15 23:49:57 +04:00
if (format)
xf_cliprdr_get_requested_data(clipboard, format->atom);
2011-09-23 07:37:17 +04:00
}
return TRUE;
2011-09-23 07:37:17 +04:00
}
void xf_cliprdr_handle_xevent(xfContext* xfc, const XEvent* event)
{
2014-10-15 06:24:07 +04:00
xfClipboard* clipboard;
if (!xfc || !event)
return;
2014-10-15 23:49:57 +04:00
clipboard = xfc->clipboard;
if (!clipboard)
return;
#ifdef WITH_XFIXES
2016-10-06 14:31:25 +03:00
2019-11-06 17:24:51 +03:00
if (clipboard->xfixes_supported &&
event->type == XFixesSelectionNotify + clipboard->xfixes_event_base)
{
2021-08-02 13:13:34 +03:00
const XFixesSelectionNotifyEvent* se = (const XFixesSelectionNotifyEvent*)event;
2014-10-15 06:24:07 +04:00
if (se->subtype == XFixesSetSelectionOwnerNotify)
{
2014-10-15 06:24:07 +04:00
if (se->selection != clipboard->clipboard_atom)
return;
if (XGetSelectionOwner(xfc->display, se->selection) == xfc->drawable)
return;
2014-10-15 06:24:07 +04:00
clipboard->owner = None;
2014-10-15 23:49:57 +04:00
xf_cliprdr_check_owner(clipboard);
}
2014-10-15 06:24:07 +04:00
return;
}
2016-10-06 14:31:25 +03:00
#endif
2014-10-15 06:24:07 +04:00
switch (event->type)
{
case SelectionNotify:
xf_cliprdr_process_selection_notify(clipboard, &event->xselection);
break;
2014-10-15 06:24:07 +04:00
case SelectionRequest:
xf_cliprdr_process_selection_request(clipboard, &event->xselectionrequest);
break;
2014-10-15 06:24:07 +04:00
case SelectionClear:
xf_cliprdr_process_selection_clear(clipboard, &event->xselectionclear);
break;
2014-10-15 06:24:07 +04:00
case PropertyNotify:
xf_cliprdr_process_property_notify(clipboard, &event->xproperty);
break;
2014-10-15 06:24:07 +04:00
case FocusIn:
2014-10-15 06:24:07 +04:00
if (!clipboard->xfixes_supported)
{
2014-10-15 23:49:57 +04:00
xf_cliprdr_check_owner(clipboard);
}
2016-10-06 14:31:25 +03:00
break;
}
}
2014-10-15 06:24:07 +04:00
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT xf_cliprdr_send_client_capabilities(xfClipboard* clipboard)
2014-10-15 06:24:07 +04:00
{
CLIPRDR_CAPABILITIES capabilities;
CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
capabilities.cCapabilitiesSets = 1;
2019-11-06 17:24:51 +03:00
capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*)&(generalCapabilitySet);
generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
generalCapabilitySet.capabilitySetLength = 12;
generalCapabilitySet.version = CB_CAPS_VERSION_2;
generalCapabilitySet.generalFlags = CB_USE_LONG_FORMAT_NAMES;
if (clipboard->streams_supported && clipboard->file_formats_registered)
generalCapabilitySet.generalFlags |=
CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS | CB_HUGE_FILE_SUPPORT_ENABLED;
clipboard->file_capability_flags = generalCapabilitySet.generalFlags;
2019-11-06 17:24:51 +03:00
return clipboard->context->ClientCapabilities(clipboard->context, &capabilities);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT xf_cliprdr_send_client_format_list(xfClipboard* clipboard)
{
2014-10-16 23:05:06 +04:00
UINT32 i, numFormats;
CLIPRDR_FORMAT* formats = NULL;
2014-10-16 06:48:18 +04:00
xfContext* xfc = clipboard->xfc;
UINT ret;
2014-10-16 23:05:06 +04:00
numFormats = clipboard->numClientFormats;
2016-10-06 14:31:25 +03:00
if (numFormats)
{
2019-11-06 17:24:51 +03:00
if (!(formats = (CLIPRDR_FORMAT*)calloc(numFormats, sizeof(CLIPRDR_FORMAT))))
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "failed to allocate %" PRIu32 " CLIPRDR_FORMAT structs", numFormats);
return CHANNEL_RC_NO_MEMORY;
}
}
2014-10-16 06:48:18 +04:00
2014-10-16 23:05:06 +04:00
for (i = 0; i < numFormats; i++)
{
2021-03-10 13:36:23 +03:00
const xfCliprdrFormat* clientFormat = &clipboard->clientFormats[i];
CLIPRDR_FORMAT* format = &formats[i];
format->formatId = clientFormat->formatId;
format->formatName = clientFormat->formatName;
2014-10-16 06:48:18 +04:00
}
ret = xf_cliprdr_send_format_list(clipboard, formats, numFormats);
2014-10-16 23:05:06 +04:00
free(formats);
2014-10-16 06:48:18 +04:00
if (clipboard->owner && clipboard->owner != xfc->drawable)
2014-10-16 06:48:18 +04:00
{
/* Request the owner for TARGETS, and wait for SelectionNotify event */
2019-11-06 17:24:51 +03:00
XConvertSelection(xfc->display, clipboard->clipboard_atom, clipboard->targets[1],
clipboard->property_atom, xfc->drawable, CurrentTime);
2014-10-16 06:48:18 +04:00
}
return ret;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
2019-11-06 17:24:51 +03:00
static UINT xf_cliprdr_send_client_format_list_response(xfClipboard* clipboard, BOOL status)
{
CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse;
formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE;
formatListResponse.msgFlags = status ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
formatListResponse.dataLen = 0;
2019-11-06 17:24:51 +03:00
return clipboard->context->ClientFormatListResponse(clipboard->context, &formatListResponse);
}
#ifdef WITH_FUSE
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT xf_cliprdr_send_client_file_contents(xfClipboard* clipboard, UINT32 streamId,
UINT32 listIndex, UINT32 dwFlags,
UINT32 nPositionLow, UINT32 nPositionHigh,
UINT32 cbRequested)
{
CLIPRDR_FILE_CONTENTS_REQUEST formatFileContentsRequest;
formatFileContentsRequest.streamId = streamId;
formatFileContentsRequest.listIndex = listIndex;
formatFileContentsRequest.dwFlags = dwFlags;
switch (dwFlags)
{
/*
* [MS-RDPECLIP] 2.2.5.3 File Contents Request PDU (CLIPRDR_FILECONTENTS_REQUEST).
*
* A request for the size of the file identified by the lindex field. The size MUST be
* returned as a 64-bit, unsigned integer. The cbRequested field MUST be set to
* 0x00000008 and both the nPositionLow and nPositionHigh fields MUST be
* set to 0x00000000.
*/
case FILECONTENTS_SIZE:
formatFileContentsRequest.cbRequested = sizeof(UINT64);
formatFileContentsRequest.nPositionHigh = 0;
formatFileContentsRequest.nPositionLow = 0;
break;
case FILECONTENTS_RANGE:
formatFileContentsRequest.cbRequested = cbRequested;
formatFileContentsRequest.nPositionHigh = nPositionHigh;
formatFileContentsRequest.nPositionLow = nPositionLow;
break;
}
formatFileContentsRequest.haveClipDataId = FALSE;
return clipboard->context->ClientFileContentsRequest(clipboard->context,
&formatFileContentsRequest);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT
xf_cliprdr_server_file_contents_response(CliprdrClientContext* context,
const CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse)
{
size_t count;
size_t index;
BOOL found = FALSE;
2021-09-08 16:47:03 +03:00
xfCliprdrFuseStream* stream = NULL;
xfCliprdrFuseInode* ino;
UINT32 stream_id;
const BYTE* data;
size_t data_len;
xfClipboard* clipboard;
WINPR_ASSERT(context);
WINPR_ASSERT(fileContentsResponse);
clipboard = context->custom;
WINPR_ASSERT(clipboard);
stream_id = fileContentsResponse->streamId;
data = fileContentsResponse->requestedData;
data_len = fileContentsResponse->cbRequested;
ArrayList_Lock(clipboard->stream_list);
count = ArrayList_Count(clipboard->stream_list);
for (index = 0; index < count; index++)
{
stream = (xfCliprdrFuseStream*)ArrayList_GetItem(clipboard->stream_list, index);
if (stream->stream_id == stream_id)
{
found = TRUE;
break;
}
}
2021-09-08 16:47:03 +03:00
if (!found || !stream)
2021-01-16 10:14:36 +03:00
{
ArrayList_Unlock(clipboard->stream_list);
return CHANNEL_RC_OK;
}
2021-01-16 10:14:36 +03:00
fuse_req_t req = stream->req;
UINT32 req_type = stream->req_type;
size_t req_ino = stream->req_ino;
ArrayList_RemoveAt(clipboard->stream_list, index);
ArrayList_Unlock(clipboard->stream_list);
switch (req_type)
{
2021-01-16 10:14:36 +03:00
case FILECONTENTS_SIZE:
/* fileContentsResponse->cbRequested should be 64bit*/
if (data_len != sizeof(UINT64))
{
fuse_reply_err(req, EIO);
break;
2021-01-16 10:14:36 +03:00
}
UINT64 size;
wStream* s = Stream_New((BYTE*)data, data_len);
if (!s)
{
fuse_reply_err(req, ENOMEM);
break;
2021-01-16 10:14:36 +03:00
}
Stream_Read_UINT64(s, size);
Stream_Free(s, FALSE);
ArrayList_Lock(clipboard->ino_list);
ino = xf_cliprdr_fuse_util_get_inode(clipboard->ino_list, req_ino);
/* ino must be exists and */
if (!ino)
{
ArrayList_Unlock(clipboard->ino_list);
fuse_reply_err(req, EIO);
break;
}
ino->st_size = size;
ino->size_set = TRUE;
struct fuse_entry_param e;
memset(&e, 0, sizeof(e));
e.ino = ino->ino;
e.attr_timeout = 1.0;
e.entry_timeout = 1.0;
e.attr.st_ino = ino->ino;
e.attr.st_mode = ino->st_mode;
e.attr.st_nlink = 1;
e.attr.st_size = ino->st_size;
e.attr.st_mtime = ino->st_mtim.tv_sec;
ArrayList_Unlock(clipboard->ino_list);
fuse_reply_entry(req, &e);
break;
case FILECONTENTS_RANGE:
fuse_reply_buf(req, (const char*)data, data_len);
break;
}
return CHANNEL_RC_OK;
}
#endif
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
2016-10-06 14:31:25 +03:00
static UINT xf_cliprdr_monitor_ready(CliprdrClientContext* context,
const CLIPRDR_MONITOR_READY* monitorReady)
{
UINT ret;
xfClipboard* clipboard;
WINPR_ASSERT(context);
WINPR_ASSERT(monitorReady);
clipboard = context->custom;
WINPR_ASSERT(clipboard);
2019-02-07 19:53:21 +03:00
WINPR_UNUSED(monitorReady);
if ((ret = xf_cliprdr_send_client_capabilities(clipboard)) != CHANNEL_RC_OK)
return ret;
2016-10-06 14:31:25 +03:00
if ((ret = xf_cliprdr_send_client_format_list(clipboard)) != CHANNEL_RC_OK)
return ret;
2014-10-15 23:49:57 +04:00
clipboard->sync = TRUE;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
2016-10-06 14:31:25 +03:00
static UINT xf_cliprdr_server_capabilities(CliprdrClientContext* context,
2019-11-06 17:24:51 +03:00
const CLIPRDR_CAPABILITIES* capabilities)
2014-10-15 06:24:07 +04:00
{
UINT32 i;
const CLIPRDR_GENERAL_CAPABILITY_SET* generalCaps;
const BYTE* capsPtr;
xfClipboard* clipboard;
WINPR_ASSERT(context);
WINPR_ASSERT(capabilities);
clipboard = context->custom;
WINPR_ASSERT(clipboard);
capsPtr = (const BYTE*)capabilities->capabilitySets;
WINPR_ASSERT(capsPtr);
clipboard->streams_supported = FALSE;
for (i = 0; i < capabilities->cCapabilitiesSets; i++)
{
const CLIPRDR_CAPABILITY_SET* caps = (const CLIPRDR_CAPABILITY_SET*)capsPtr;
if (caps->capabilitySetType == CB_CAPSTYPE_GENERAL)
{
2019-11-06 17:24:51 +03:00
generalCaps = (const CLIPRDR_GENERAL_CAPABILITY_SET*)caps;
if (generalCaps->generalFlags & CB_STREAM_FILECLIP_ENABLED)
{
clipboard->streams_supported = TRUE;
}
}
capsPtr += caps->capabilitySetLength;
}
return CHANNEL_RC_OK;
2014-10-15 06:24:07 +04:00
}
static void xf_cliprdr_prepare_to_set_selection_owner(xfContext* xfc, xfClipboard* clipboard)
{
/*
* When you're writing to the selection in response to a
* normal X event like a mouse click or keyboard action, you
* get the selection timestamp by copying the time field out
* of that X event. Here, we're doing it on our own
* initiative, so we have to _request_ the X server time.
*
* There isn't a GetServerTime request in the X protocol, so I
* work around it by setting a property on our own window, and
* waiting for a PropertyNotify event to come back telling me
* it's been done - which will have a timestamp we can use.
*/
/* We have to set the property to some value, but it doesn't
* matter what. Set it to its own name, which we have here
* anyway! */
Atom value = clipboard->timestamp_property_atom;
XChangeProperty(xfc->display, xfc->drawable, clipboard->timestamp_property_atom, XA_ATOM, 32,
PropModeReplace, (BYTE*)&value, 1);
XFlush(xfc->display);
}
static void xf_cliprdr_set_selection_owner(xfContext* xfc, xfClipboard* clipboard, Time timestamp)
{
/*
* Actually set ourselves up as the selection owner, now that
* we have a timestamp to use.
*/
clipboard->selection_ownership_timestamp = timestamp;
XSetSelectionOwner(xfc->display, clipboard->clipboard_atom, xfc->drawable, timestamp);
XFlush(xfc->display);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
2016-10-06 14:31:25 +03:00
static UINT xf_cliprdr_server_format_list(CliprdrClientContext* context,
2019-11-06 17:24:51 +03:00
const CLIPRDR_FORMAT_LIST* formatList)
2014-10-15 06:24:07 +04:00
{
UINT32 i;
int j;
xfContext* xfc;
UINT ret;
xfClipboard* clipboard;
WINPR_ASSERT(context);
WINPR_ASSERT(formatList);
clipboard = context->custom;
WINPR_ASSERT(clipboard);
xfc = clipboard->xfc;
WINPR_ASSERT(xfc);
/* Clear the active SelectionRequest, as it is now invalid */
free(clipboard->respond);
clipboard->respond = NULL;
xf_clipboard_formats_free(clipboard);
xf_cliprdr_clear_cached_data(clipboard);
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
clipboard->data_format_id = -1;
clipboard->data_format_name = NULL;
2014-10-15 23:49:57 +04:00
if (clipboard->serverFormats)
{
for (j = 0; j < clipboard->numServerFormats; j++)
free(clipboard->serverFormats[j].formatName);
2014-10-16 06:48:18 +04:00
2014-10-15 23:49:57 +04:00
free(clipboard->serverFormats);
clipboard->serverFormats = NULL;
2014-10-16 23:05:06 +04:00
clipboard->numServerFormats = 0;
2014-10-15 23:49:57 +04:00
}
clipboard->numServerFormats = formatList->numFormats + 1; /* +1 for CF_RAW */
2014-10-15 23:49:57 +04:00
2019-11-06 17:24:51 +03:00
if (!(clipboard->serverFormats =
(CLIPRDR_FORMAT*)calloc(clipboard->numServerFormats, sizeof(CLIPRDR_FORMAT))))
2016-10-06 14:31:25 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "failed to allocate %d CLIPRDR_FORMAT structs", clipboard->numServerFormats);
return CHANNEL_RC_NO_MEMORY;
}
2014-10-15 23:49:57 +04:00
for (i = 0; i < formatList->numFormats; i++)
2014-10-15 23:49:57 +04:00
{
CLIPRDR_FORMAT* format = &formatList->formats[i];
2014-10-15 23:49:57 +04:00
clipboard->serverFormats[i].formatId = format->formatId;
2016-10-06 14:31:25 +03:00
if (format->formatName)
{
clipboard->serverFormats[i].formatName = _strdup(format->formatName);
2016-10-06 14:31:25 +03:00
if (!clipboard->serverFormats[i].formatName)
{
UINT32 k;
for (k = 0; k < i; k++)
free(clipboard->serverFormats[k].formatName);
clipboard->numServerFormats = 0;
free(clipboard->serverFormats);
clipboard->serverFormats = NULL;
return CHANNEL_RC_NO_MEMORY;
}
}
2014-10-15 23:49:57 +04:00
}
/* CF_RAW is always implicitly supported by the server */
{
CLIPRDR_FORMAT* format = &clipboard->serverFormats[formatList->numFormats];
format->formatId = CF_RAW;
format->formatName = NULL;
}
xf_cliprdr_provide_server_format_list(clipboard);
2014-10-15 23:49:57 +04:00
clipboard->numTargets = 2;
for (i = 0; i < formatList->numFormats; i++)
2014-10-15 23:49:57 +04:00
{
2021-03-10 13:36:23 +03:00
const CLIPRDR_FORMAT* format = &formatList->formats[i];
2014-10-15 23:49:57 +04:00
for (j = 0; j < clipboard->numClientFormats; j++)
{
2021-03-10 13:36:23 +03:00
const xfCliprdrFormat* clientFormat = &clipboard->clientFormats[j];
if (xf_cliprdr_formats_equal(format, clientFormat))
2014-10-15 23:49:57 +04:00
{
2021-03-10 13:36:23 +03:00
xf_cliprdr_append_target(clipboard, clientFormat->atom);
2014-10-15 23:49:57 +04:00
}
}
}
ret = xf_cliprdr_send_client_format_list_response(clipboard, TRUE);
if (xfc->remote_app)
xf_cliprdr_set_selection_owner(xfc, clipboard, CurrentTime);
else
xf_cliprdr_prepare_to_set_selection_owner(xfc, clipboard);
return ret;
2014-10-15 06:24:07 +04:00
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
2019-11-06 17:24:51 +03:00
static UINT
xf_cliprdr_server_format_list_response(CliprdrClientContext* context,
const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
2014-10-15 06:24:07 +04:00
{
2019-11-06 17:24:51 +03:00
// xfClipboard* clipboard = (xfClipboard*) context->custom;
return CHANNEL_RC_OK;
2014-10-15 06:24:07 +04:00
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
2019-11-06 17:24:51 +03:00
static UINT
xf_cliprdr_server_format_data_request(CliprdrClientContext* context,
const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
2014-10-15 06:24:07 +04:00
{
BOOL rawTransfer;
2021-03-10 13:36:23 +03:00
const xfCliprdrFormat* format = NULL;
UINT32 formatId;
xfContext* xfc;
xfClipboard* clipboard;
WINPR_ASSERT(context);
WINPR_ASSERT(formatDataRequest);
clipboard = context->custom;
WINPR_ASSERT(clipboard);
xfc = clipboard->xfc;
WINPR_ASSERT(xfc);
formatId = formatDataRequest->requestedFormatId;
rawTransfer = xf_cliprdr_is_raw_transfer_available(clipboard);
if (rawTransfer)
{
format = xf_cliprdr_get_client_format_by_id(clipboard, CF_RAW);
2019-11-06 17:24:51 +03:00
XChangeProperty(xfc->display, xfc->drawable, clipboard->property_atom, XA_INTEGER, 32,
PropModeReplace, (BYTE*)&formatId, 1);
}
else
format = xf_cliprdr_get_client_format_by_id(clipboard, formatId);
clipboard->requestedFormatId = rawTransfer ? CF_RAW : formatId;
if (!format)
return xf_cliprdr_send_data_response(clipboard, NULL, 0);
2019-11-06 17:24:51 +03:00
XConvertSelection(xfc->display, clipboard->clipboard_atom, format->atom,
clipboard->property_atom, xfc->drawable, CurrentTime);
XFlush(xfc->display);
/* After this point, we expect a SelectionNotify event from the clipboard owner. */
return CHANNEL_RC_OK;
2014-10-15 06:24:07 +04:00
}
#ifdef WITH_FUSE
static char* xf_cliprdr_fuse_split_basename(char* name, int len)
{
int s = len - 1;
for (; s >= 0; s--)
{
if (*(name + s) == '\\')
{
return name + s;
}
}
return NULL;
}
2021-01-16 10:14:36 +03:00
static xfCliprdrFuseInode* xf_cliprdr_fuse_create_root_node()
{
xfCliprdrFuseInode* rootNode = (xfCliprdrFuseInode*)calloc(1, sizeof(xfCliprdrFuseInode));
if (!rootNode)
2021-01-16 10:14:36 +03:00
return NULL;
rootNode->ino = 1;
rootNode->parent_ino = 1;
rootNode->st_mode = S_IFDIR | 0755;
2021-01-16 10:14:36 +03:00
rootNode->name = _strdup("/");
rootNode->child_inos = ArrayList_New(TRUE);
2021-01-16 10:14:36 +03:00
rootNode->st_mtim.tv_sec = time(NULL);
rootNode->st_size = 0;
rootNode->size_set = TRUE;
if (!rootNode->child_inos || !rootNode->name)
{
xf_cliprdr_fuse_inode_free(rootNode);
WLog_ERR(TAG, "fail to alloc rootNode's member");
2021-01-16 10:14:36 +03:00
return NULL;
}
2021-01-16 10:14:36 +03:00
return rootNode;
}
static BOOL xf_cliprdr_fuse_check_stream(wStream* s, size_t count)
{
UINT32 nrDescriptors;
if (Stream_GetRemainingLength(s) < sizeof(UINT32))
{
2021-01-16 10:14:36 +03:00
WLog_ERR(TAG, "too short serialized format list");
return FALSE;
}
2021-01-16 10:14:36 +03:00
Stream_Read_UINT32(s, nrDescriptors);
if (count != nrDescriptors)
{
WLog_WARN(TAG, "format data response mismatch");
return FALSE;
}
return TRUE;
}
static BOOL xf_cliprdr_fuse_create_nodes(xfClipboard* clipboard, wStream* s, size_t count,
xfCliprdrFuseInode* rootNode)
{
BOOL status = FALSE;
size_t lindex = 0;
char* curName = NULL;
char* dirName = NULL;
char* baseName = NULL;
2021-09-08 16:47:03 +03:00
xfCliprdrFuseInode* inode = NULL;
2021-01-16 10:14:36 +03:00
wHashTable* mapDir = HashTable_New(TRUE);
if (!mapDir)
{
WLog_ERR(TAG, "fail to alloc hashtable");
2021-01-16 10:14:36 +03:00
return FALSE;
}
if (!HashTable_SetupForStringData(mapDir, FALSE))
goto error;
FILEDESCRIPTORW* descriptor = (FILEDESCRIPTORW*)calloc(1, sizeof(FILEDESCRIPTORW));
if (!descriptor)
{
WLog_ERR(TAG, "fail to alloc FILEDESCRIPTORW");
2021-01-16 10:14:36 +03:00
goto error;
}
/* here we assume that parent folder always appears before its children */
2021-01-16 10:14:36 +03:00
for (; lindex < count; lindex++)
{
Stream_Read(s, descriptor, sizeof(FILEDESCRIPTORW));
inode = (xfCliprdrFuseInode*)calloc(1, sizeof(xfCliprdrFuseInode));
if (!inode)
{
WLog_ERR(TAG, "fail to alloc ino");
2021-01-16 10:14:36 +03:00
break;
}
size_t curLen = _wcsnlen(descriptor->cFileName, ARRAYSIZE(descriptor->cFileName));
int newLen = ConvertFromUnicode(CP_UTF8, 0, descriptor->cFileName, (int)curLen, &curName, 0,
NULL, NULL);
if (!curName)
2021-01-16 10:14:36 +03:00
break;
char* split_point = xf_cliprdr_fuse_split_basename(curName, newLen);
UINT64 ticks;
xfCliprdrFuseInode* parent;
inode->lindex = lindex;
inode->ino = lindex + 2;
if (split_point == NULL)
{
baseName = _strdup(curName);
if (!baseName)
2021-01-16 10:14:36 +03:00
break;
inode->parent_ino = 1;
inode->name = baseName;
if (!ArrayList_Append(rootNode->child_inos, (void*)inode->ino))
2021-01-16 10:14:36 +03:00
break;
}
else
{
dirName = calloc(split_point - curName + 1, sizeof(char));
if (!dirName)
2021-01-16 10:14:36 +03:00
break;
_snprintf(dirName, split_point - curName + 1, "%s", curName);
/* drop last '\\' */
baseName = _strdup(split_point + 1);
if (!baseName)
2021-01-16 10:14:36 +03:00
break;
parent = (xfCliprdrFuseInode*)HashTable_GetItemValue(mapDir, dirName);
if (!parent)
2021-01-16 10:14:36 +03:00
break;
inode->parent_ino = parent->ino;
inode->name = baseName;
if (!ArrayList_Append(parent->child_inos, (void*)inode->ino))
2021-01-16 10:14:36 +03:00
break;
free(dirName);
2021-01-16 10:14:36 +03:00
dirName = NULL;
}
/* TODO: check FD_ATTRIBUTES in dwFlags
However if this flag is not valid how can we determine file/folder?
*/
if ((descriptor->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
{
inode->st_mode = S_IFDIR | 0755;
inode->child_inos = ArrayList_New(TRUE);
if (!inode->child_inos)
2021-01-16 10:14:36 +03:00
break;
inode->st_size = 0;
inode->size_set = TRUE;
char* tmpName = _strdup(curName);
if (!tmpName)
2021-01-16 10:14:36 +03:00
break;
if (!HashTable_Insert(mapDir, tmpName, inode))
{
free(tmpName);
2021-01-16 10:14:36 +03:00
break;
}
}
else
{
inode->st_mode = S_IFREG | 0644;
if ((descriptor->dwFlags & FD_FILESIZE) != 0)
{
inode->st_size = (((UINT64)descriptor->nFileSizeHigh) << 32) |
((UINT64)descriptor->nFileSizeLow);
inode->size_set = TRUE;
}
else
{
inode->size_set = FALSE;
}
}
free(curName);
2021-01-16 10:14:36 +03:00
curName = NULL;
if ((descriptor->dwFlags & FD_WRITESTIME) != 0)
{
ticks = (((UINT64)descriptor->ftLastWriteTime.dwHighDateTime << 32) |
((UINT64)descriptor->ftLastWriteTime.dwLowDateTime)) -
WIN32_FILETIME_TO_UNIX_EPOCH_USEC;
inode->st_mtim.tv_sec = ticks / 10000000ULL;
/* tv_nsec Not used for now */
inode->st_mtim.tv_nsec = ticks % 10000000ULL;
}
else
{
inode->st_mtim.tv_sec = time(NULL);
inode->st_mtim.tv_nsec = 0;
}
if (!ArrayList_Append(clipboard->ino_list, inode))
2021-01-16 10:14:36 +03:00
break;
}
/* clean up incomplete ino_list*/
if (lindex != count)
{
free(dirName);
free(curName);
/* baseName is freed in xf_cliprdr_fuse_inode_free*/
xf_cliprdr_fuse_inode_free(inode);
ArrayList_Clear(clipboard->ino_list);
}
else
{
status = TRUE;
}
free(descriptor);
2021-01-16 10:14:36 +03:00
error:
HashTable_Free(mapDir);
2021-01-16 10:14:36 +03:00
return status;
}
/**
* Generate inode list for fuse
*
* @return TRUE on success, FALSE on fail
*/
static BOOL xf_cliprdr_fuse_generate_list(xfClipboard* clipboard, const BYTE* data, UINT32 size)
{
BOOL status = FALSE;
if (size < 4)
{
WLog_ERR(TAG, "size of format data response invalid : %d", size);
return FALSE;
}
size_t count = (size - 4) / sizeof(FILEDESCRIPTORW);
if (count < 1)
return FALSE;
wStream* s = Stream_New((BYTE*)data, size);
if (!s || !xf_cliprdr_fuse_check_stream(s, count))
{
WLog_ERR(TAG, "Stream_New failed");
goto error;
}
/* prevent conflict between fuse_thread and this */
ArrayList_Lock(clipboard->ino_list);
xfCliprdrFuseInode* rootNode = xf_cliprdr_fuse_create_root_node();
if (!rootNode || !ArrayList_Append(clipboard->ino_list, rootNode))
2021-01-16 10:14:36 +03:00
{
xf_cliprdr_fuse_inode_free(rootNode);
WLog_ERR(TAG, "fail to alloc rootNode to ino_list");
goto error2;
}
status = xf_cliprdr_fuse_create_nodes(clipboard, s, count, rootNode);
error2:
ArrayList_Unlock(clipboard->ino_list);
2021-01-16 10:14:36 +03:00
error:
Stream_Free(s, FALSE);
2021-01-16 10:14:36 +03:00
return status;
}
#endif
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
2019-11-06 17:24:51 +03:00
static UINT
xf_cliprdr_server_format_data_response(CliprdrClientContext* context,
const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
2014-10-15 06:24:07 +04:00
{
BOOL bSuccess;
2014-10-16 23:05:06 +04:00
BYTE* pDstData;
UINT32 DstSize;
UINT32 SrcSize;
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
UINT32 srcFormatId;
UINT32 dstFormatId;
2021-03-10 13:36:23 +03:00
const xfCliprdrFormat* dstTargetFormat;
BOOL nullTerminated = FALSE;
UINT32 size;
const BYTE* data;
xfContext* xfc;
xfClipboard* clipboard;
WINPR_ASSERT(context);
WINPR_ASSERT(formatDataResponse);
clipboard = context->custom;
WINPR_ASSERT(clipboard);
xfc = clipboard->xfc;
WINPR_ASSERT(xfc);
size = formatDataResponse->dataLen;
data = formatDataResponse->requestedFormatData;
2021-01-16 10:14:36 +03:00
if (formatDataResponse->msgFlags == CB_RESPONSE_FAIL)
{
WLog_WARN(TAG, "Format Data Response PDU msgFlags is CB_RESPONSE_FAIL");
free(clipboard->respond);
clipboard->respond = NULL;
2021-01-16 10:14:36 +03:00
return CHANNEL_RC_OK;
}
if (!clipboard->respond)
return CHANNEL_RC_OK;
xf_cliprdr_clear_cached_data(clipboard);
pDstData = NULL;
2014-12-07 02:29:28 +03:00
DstSize = 0;
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
srcFormatId = 0;
dstFormatId = 0;
if (clipboard->data_raw_format)
{
srcFormatId = CF_RAW;
dstFormatId = CF_RAW;
}
else if (clipboard->data_format_name)
{
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
if (strcmp(clipboard->data_format_name, "HTML Format") == 0)
{
srcFormatId = ClipboardGetFormatId(clipboard->system, "HTML Format");
dstFormatId = ClipboardGetFormatId(clipboard->system, "text/html");
nullTerminated = TRUE;
}
if (strcmp(clipboard->data_format_name, "FileGroupDescriptorW") == 0)
{
#ifdef WITH_FUSE
/* Build inode table for FILEDESCRIPTORW*/
2021-01-16 10:14:36 +03:00
if (!xf_cliprdr_fuse_generate_list(clipboard, data, size))
{
/* just continue */
WLog_WARN(TAG, "fail to generate list for FILEDESCRIPTOR");
}
#endif
srcFormatId = ClipboardGetFormatId(clipboard->system, "FileGroupDescriptorW");
dstTargetFormat =
xf_cliprdr_get_client_format_by_atom(clipboard, clipboard->respond->target);
if (!dstTargetFormat)
{
dstFormatId = ClipboardGetFormatId(clipboard->system, "text/uri-list");
}
else
{
switch (dstTargetFormat->formatId)
{
case CF_UNICODETEXT:
dstFormatId = ClipboardGetFormatId(clipboard->system, "UTF8_STRING");
break;
case CB_FORMAT_TEXTURILIST:
dstFormatId = ClipboardGetFormatId(clipboard->system, "text/uri-list");
break;
case CB_FORMAT_GNOMECOPIEDFILES:
dstFormatId =
ClipboardGetFormatId(clipboard->system, "x-special/gnome-copied-files");
break;
case CB_FORMAT_MATECOPIEDFILES:
dstFormatId =
ClipboardGetFormatId(clipboard->system, "x-special/mate-copied-files");
}
}
nullTerminated = TRUE;
}
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
}
else
{
switch (clipboard->data_format_id)
{
2016-10-06 14:31:25 +03:00
case CF_TEXT:
srcFormatId = CF_TEXT;
dstFormatId = ClipboardGetFormatId(clipboard->system, "UTF8_STRING");
nullTerminated = TRUE;
break;
case CF_OEMTEXT:
srcFormatId = CF_OEMTEXT;
dstFormatId = ClipboardGetFormatId(clipboard->system, "UTF8_STRING");
nullTerminated = TRUE;
break;
case CF_UNICODETEXT:
srcFormatId = CF_UNICODETEXT;
dstFormatId = ClipboardGetFormatId(clipboard->system, "UTF8_STRING");
nullTerminated = TRUE;
break;
case CF_DIB:
srcFormatId = CF_DIB;
dstFormatId = ClipboardGetFormatId(clipboard->system, "image/bmp");
break;
2018-09-24 10:53:28 +03:00
default:
break;
client/X11: improve named clipboard format support Clipboard formats are identified by numerical IDs and literal names. We can keep using arbitrary defined IDs for local clipboard formats as we are sure that they have some fixed meaning, but the server can and will be using its own IDs, which can be different from ours for the named formats. Therefore: 1) A correct way to compare a local format to a remote one is to check the names first, and only then compare their IDs. (Extra care should be taken to support short format names.) 2) Server IDs cannot be used with wClipboard directly when dealing with named formats. Format name should be used to extract correct local ID for the use with Clipboard{Set,Get}Data(). Also, I find the notion of 'alternate' format IDs to be confusing. We either deal with a fixed ID format (declared in <winpr/user.h>), or a format that was given an arbitrary fixed ID for local use (defined in <freerdp/channels/cliprdr.h>), or a remote format identified by a pair of an ID and a name. Format IDs can be local and remote, but there are no 'alternates'. So now: 1) A new function xf_cliprdr_formats_equal() is used to compare formats correctly in xf_cliprdr_get_server_format_by_atom() when searching for a server format corresponding to a local one, and in xf_cliprdr_server_format_list() when constructing a local TARGETS list from the server format list. 2) Correct local format IDs are used with wClipboard conversions by xf_cliprdr_process_requested_data() and xf_cliprdr_server_format_data_response(). 3) We refer to formatId and formatName when doing requests, and srcFormatId and dstFormatId when doing conversions, instead of using formatId and altFormatId for both purposes. 4) Server format ID and name are used to identify cached clipboard contents. The name is compared directly as a pointer because it will be a pointer from the same clipboard->serverFormats array. Also, the clipboard contents are invalidated when format list arrives, so xf_cliprdr_server_format_list() now also clears the format ID and name together with the data.
2015-08-17 11:35:19 +03:00
}
}
2019-11-06 17:24:51 +03:00
SrcSize = (UINT32)size;
2016-10-06 14:31:25 +03:00
bSuccess = ClipboardSetData(clipboard->system, srcFormatId, data, SrcSize);
2014-10-16 23:05:06 +04:00
if (bSuccess)
{
2018-09-24 10:53:28 +03:00
if (SrcSize == 0)
{
WLog_DBG(TAG, "skipping, empty data detected!");
free(clipboard->respond);
clipboard->respond = NULL;
2018-09-24 10:53:28 +03:00
return CHANNEL_RC_OK;
}
2019-11-06 17:24:51 +03:00
pDstData = (BYTE*)ClipboardGetData(clipboard->system, dstFormatId, &DstSize);
if (!pDstData)
{
WLog_WARN(TAG, "failed to get clipboard data in format %s [source format %s]",
ClipboardGetFormatName(clipboard->system, dstFormatId),
ClipboardGetFormatName(clipboard->system, srcFormatId));
}
2019-09-03 09:56:59 +03:00
if (nullTerminated && pDstData)
{
BYTE* nullTerminator = memchr(pDstData, '\0', DstSize);
if (nullTerminator)
DstSize = nullTerminator - pDstData;
}
}
/* Cache converted and original data to avoid doing a possibly costly
* conversion again on subsequent requests */
clipboard->data = pDstData;
clipboard->data_length = DstSize;
/* We have to copy the original data again, as pSrcData is now owned
* by clipboard->system. Memory allocation failure is not fatal here
* as this is only a cached value. */
2019-11-06 17:24:51 +03:00
clipboard->data_raw = (BYTE*)malloc(size);
if (clipboard->data_raw)
{
CopyMemory(clipboard->data_raw, data, size);
clipboard->data_raw_length = size;
}
else
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "failed to allocate %" PRIu32 " bytes for a copy of raw clipboard data",
size);
}
xf_cliprdr_provide_data(clipboard, clipboard->respond, pDstData, DstSize);
{
union
{
XEvent* ev;
XSelectionEvent* sev;
} conv;
conv.sev = clipboard->respond;
XSendEvent(xfc->display, clipboard->respond->requestor, 0, 0, conv.ev);
XFlush(xfc->display);
}
free(clipboard->respond);
clipboard->respond = NULL;
return CHANNEL_RC_OK;
2014-10-15 06:24:07 +04:00
}
2019-11-06 17:24:51 +03:00
static UINT
xf_cliprdr_server_file_size_request(xfClipboard* clipboard,
const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
{
wClipboardFileSizeRequest request = { 0 };
request.streamId = fileContentsRequest->streamId;
request.listIndex = fileContentsRequest->listIndex;
if (fileContentsRequest->cbRequested != sizeof(UINT64))
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "unexpected FILECONTENTS_SIZE request: %" PRIu32 " bytes",
fileContentsRequest->cbRequested);
}
return clipboard->delegate->ClientRequestFileSize(clipboard->delegate, &request);
}
2019-11-06 17:24:51 +03:00
static UINT
xf_cliprdr_server_file_range_request(xfClipboard* clipboard,
const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
{
wClipboardFileRangeRequest request = { 0 };
request.streamId = fileContentsRequest->streamId;
request.listIndex = fileContentsRequest->listIndex;
request.nPositionLow = fileContentsRequest->nPositionLow;
request.nPositionHigh = fileContentsRequest->nPositionHigh;
request.cbRequested = fileContentsRequest->cbRequested;
return clipboard->delegate->ClientRequestFileRange(clipboard->delegate, &request);
}
2019-11-06 17:24:51 +03:00
static UINT
xf_cliprdr_send_file_contents_failure(CliprdrClientContext* context,
const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
{
CLIPRDR_FILE_CONTENTS_RESPONSE response = { 0 };
response.msgFlags = CB_RESPONSE_FAIL;
response.streamId = fileContentsRequest->streamId;
return context->ClientFileContentsResponse(context, &response);
}
2019-11-06 17:24:51 +03:00
static UINT
xf_cliprdr_server_file_contents_request(CliprdrClientContext* context,
const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
{
UINT error = NO_ERROR;
xfClipboard* clipboard;
WINPR_ASSERT(context);
WINPR_ASSERT(fileContentsRequest);
clipboard = context->custom;
WINPR_ASSERT(clipboard);
/*
* MS-RDPECLIP 2.2.5.3 File Contents Request PDU (CLIPRDR_FILECONTENTS_REQUEST):
* The FILECONTENTS_SIZE and FILECONTENTS_RANGE flags MUST NOT be set at the same time.
*/
if ((fileContentsRequest->dwFlags & (FILECONTENTS_SIZE | FILECONTENTS_RANGE)) ==
(FILECONTENTS_SIZE | FILECONTENTS_RANGE))
{
WLog_ERR(TAG, "invalid CLIPRDR_FILECONTENTS_REQUEST.dwFlags");
return xf_cliprdr_send_file_contents_failure(context, fileContentsRequest);
}
if (fileContentsRequest->dwFlags & FILECONTENTS_SIZE)
error = xf_cliprdr_server_file_size_request(clipboard, fileContentsRequest);
if (fileContentsRequest->dwFlags & FILECONTENTS_RANGE)
error = xf_cliprdr_server_file_range_request(clipboard, fileContentsRequest);
if (error)
{
WLog_ERR(TAG, "failed to handle CLIPRDR_FILECONTENTS_REQUEST: 0x%08X", error);
return xf_cliprdr_send_file_contents_failure(context, fileContentsRequest);
}
return CHANNEL_RC_OK;
}
static UINT xf_cliprdr_clipboard_file_size_success(wClipboardDelegate* delegate,
2019-11-06 17:24:51 +03:00
const wClipboardFileSizeRequest* request,
UINT64 fileSize)
{
CLIPRDR_FILE_CONTENTS_RESPONSE response = { 0 };
xfClipboard* clipboard = delegate->custom;
response.msgFlags = CB_RESPONSE_OK;
response.streamId = request->streamId;
response.cbRequested = sizeof(UINT64);
2019-11-06 17:24:51 +03:00
response.requestedData = (BYTE*)&fileSize;
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
}
static UINT xf_cliprdr_clipboard_file_size_failure(wClipboardDelegate* delegate,
2019-11-06 17:24:51 +03:00
const wClipboardFileSizeRequest* request,
UINT errorCode)
{
CLIPRDR_FILE_CONTENTS_RESPONSE response = { 0 };
xfClipboard* clipboard = delegate->custom;
2019-02-07 19:53:21 +03:00
WINPR_UNUSED(errorCode);
response.msgFlags = CB_RESPONSE_FAIL;
response.streamId = request->streamId;
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
}
static UINT xf_cliprdr_clipboard_file_range_success(wClipboardDelegate* delegate,
2019-11-06 17:24:51 +03:00
const wClipboardFileRangeRequest* request,
const BYTE* data, UINT32 size)
{
CLIPRDR_FILE_CONTENTS_RESPONSE response = { 0 };
xfClipboard* clipboard = delegate->custom;
response.msgFlags = CB_RESPONSE_OK;
response.streamId = request->streamId;
response.cbRequested = size;
2021-08-02 13:13:34 +03:00
response.requestedData = (const BYTE*)data;
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
}
static UINT xf_cliprdr_clipboard_file_range_failure(wClipboardDelegate* delegate,
2019-11-06 17:24:51 +03:00
const wClipboardFileRangeRequest* request,
UINT errorCode)
{
CLIPRDR_FILE_CONTENTS_RESPONSE response = { 0 };
xfClipboard* clipboard = delegate->custom;
2019-02-07 19:53:21 +03:00
WINPR_UNUSED(errorCode);
response.msgFlags = CB_RESPONSE_FAIL;
response.streamId = request->streamId;
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
}
#ifdef WITH_FUSE
/* For better understanding the relationship between ino and index of arraylist*/
2021-01-16 10:14:36 +03:00
static inline xfCliprdrFuseInode* xf_cliprdr_fuse_util_get_inode(wArrayList* ino_list,
fuse_ino_t ino)
{
size_t list_index = ino - 1;
return (xfCliprdrFuseInode*)ArrayList_GetItem(ino_list, list_index);
}
2021-01-16 10:14:36 +03:00
/* fuse helper functions*/
static int xf_cliprdr_fuse_util_stat(xfClipboard* clipboard, fuse_ino_t ino, struct stat* stbuf)
{
2021-01-16 10:14:36 +03:00
int err = 0;
xfCliprdrFuseInode* node;
ArrayList_Lock(clipboard->ino_list);
node = xf_cliprdr_fuse_util_get_inode(clipboard->ino_list, ino);
2021-01-16 10:14:36 +03:00
if (!node)
{
err = ENOENT;
goto error;
}
memset(stbuf, 0, sizeof(*stbuf));
stbuf->st_ino = ino;
stbuf->st_mode = node->st_mode;
stbuf->st_mtime = node->st_mtim.tv_sec;
stbuf->st_nlink = 1;
stbuf->st_size = node->st_size;
2021-01-16 10:14:36 +03:00
error:
ArrayList_Unlock(clipboard->ino_list);
return err;
}
static int xf_cliprdr_fuse_util_stmode(xfClipboard* clipboard, fuse_ino_t ino, mode_t* mode)
{
int err = 0;
xfCliprdrFuseInode* node;
2021-01-16 10:14:36 +03:00
ArrayList_Lock(clipboard->ino_list);
node = xf_cliprdr_fuse_util_get_inode(clipboard->ino_list, ino);
if (!node)
{
err = ENOENT;
goto error;
}
*mode = node->st_mode;
error:
ArrayList_Unlock(clipboard->ino_list);
return err;
}
2021-01-16 10:14:36 +03:00
static int xf_cliprdr_fuse_util_lindex(xfClipboard* clipboard, fuse_ino_t ino, UINT32* lindex)
{
int err = 0;
xfCliprdrFuseInode* node;
ArrayList_Lock(clipboard->ino_list);
2021-01-16 10:14:36 +03:00
node = xf_cliprdr_fuse_util_get_inode(clipboard->ino_list, ino);
if (!node)
{
2021-01-16 10:14:36 +03:00
err = ENOENT;
goto error;
}
2021-01-16 10:14:36 +03:00
if ((node->st_mode & S_IFDIR) != 0)
{
2021-01-16 10:14:36 +03:00
err = EISDIR;
goto error;
}
*lindex = node->lindex;
error:
ArrayList_Unlock(clipboard->ino_list);
return err;
}
static int xf_cliprdr_fuse_util_add_stream_list(xfClipboard* clipboard, fuse_req_t req,
UINT32* stream_id)
{
int err = 0;
xfCliprdrFuseStream* stream = (xfCliprdrFuseStream*)calloc(1, sizeof(xfCliprdrFuseStream));
if (!stream)
{
err = ENOMEM;
return err;
}
ArrayList_Lock(clipboard->stream_list);
stream->req = req;
stream->req_type = FILECONTENTS_RANGE;
stream->stream_id = clipboard->current_stream_id;
*stream_id = stream->stream_id;
stream->req_ino = 0;
clipboard->current_stream_id++;
if (!ArrayList_Append(clipboard->stream_list, stream))
2021-01-16 10:14:36 +03:00
{
err = ENOMEM;
goto error;
}
2021-01-16 10:14:36 +03:00
error:
ArrayList_Unlock(clipboard->stream_list);
return err;
}
static void xf_cliprdr_fuse_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi)
{
int err;
struct stat stbuf;
xfClipboard* clipboard = (xfClipboard*)fuse_req_userdata(req);
err = xf_cliprdr_fuse_util_stat(clipboard, ino, &stbuf);
if (err)
{
fuse_reply_err(req, err);
return;
}
fuse_reply_attr(req, &stbuf, 0);
}
static void xf_cliprdr_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info* fi)
{
size_t count;
size_t index;
size_t child_ino;
size_t direntry_len;
char* buf;
struct stat stbuf;
size_t pos = 0;
xfCliprdrFuseInode* child_node;
xfCliprdrFuseInode* node;
xfClipboard* clipboard = (xfClipboard*)fuse_req_userdata(req);
ArrayList_Lock(clipboard->ino_list);
2021-01-16 10:14:36 +03:00
node = xf_cliprdr_fuse_util_get_inode(clipboard->ino_list, ino);
if (!node || !node->child_inos)
{
ArrayList_Unlock(clipboard->ino_list);
fuse_reply_err(req, ENOENT);
return;
}
else if ((node->st_mode & S_IFDIR) == 0)
{
ArrayList_Unlock(clipboard->ino_list);
fuse_reply_err(req, ENOTDIR);
return;
}
ArrayList_Lock(node->child_inos);
count = ArrayList_Count(node->child_inos);
2021-02-16 18:10:23 +03:00
if ((count == 0) || ((SSIZE_T)(count + 1) <= off))
{
ArrayList_Unlock(node->child_inos);
ArrayList_Unlock(clipboard->ino_list);
fuse_reply_buf(req, NULL, 0);
return;
}
else
{
buf = (char*)calloc(size, sizeof(char));
if (!buf)
{
ArrayList_Unlock(node->child_inos);
ArrayList_Unlock(clipboard->ino_list);
fuse_reply_err(req, ENOMEM);
return;
}
for (index = off; index < count + 2; index++)
{
memset(&stbuf, 0, sizeof(stbuf));
if (index == 0)
{
stbuf.st_ino = ino;
direntry_len = fuse_add_direntry(req, buf + pos, size - pos, ".", &stbuf, index);
if (direntry_len > size - pos)
break;
pos += direntry_len;
}
else if (index == 1)
{
stbuf.st_ino = node->parent_ino;
direntry_len = fuse_add_direntry(req, buf + pos, size - pos, "..", &stbuf, index);
if (direntry_len > size - pos)
break;
pos += direntry_len;
}
else
{
/* execlude . and .. */
child_ino = (size_t)ArrayList_GetItem(node->child_inos, index - 2);
/* previous lock for ino_list still work*/
2021-01-16 10:14:36 +03:00
child_node = xf_cliprdr_fuse_util_get_inode(clipboard->ino_list, child_ino);
if (!child_node)
break;
stbuf.st_ino = child_node->ino;
direntry_len =
fuse_add_direntry(req, buf + pos, size - pos, child_node->name, &stbuf, index);
if (direntry_len > size - pos)
break;
pos += direntry_len;
}
}
ArrayList_Unlock(node->child_inos);
ArrayList_Unlock(clipboard->ino_list);
fuse_reply_buf(req, buf, pos);
free(buf);
return;
}
}
static void xf_cliprdr_fuse_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi)
{
2021-01-16 10:14:36 +03:00
int err;
mode_t mode = 0;
xfClipboard* clipboard = (xfClipboard*)fuse_req_userdata(req);
2021-01-16 10:14:36 +03:00
err = xf_cliprdr_fuse_util_stmode(clipboard, ino, &mode);
if (err)
{
2021-01-16 10:14:36 +03:00
fuse_reply_err(req, err);
return;
}
2021-01-16 10:14:36 +03:00
if ((mode & S_IFDIR) != 0)
{
fuse_reply_err(req, EISDIR);
}
else
{
/* Important for KDE to get file correctly*/
fi->direct_io = 1;
fuse_reply_open(req, fi);
}
}
static void xf_cliprdr_fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info* fi)
{
if (ino < 2)
{
fuse_reply_err(req, ENOENT);
return;
}
2021-01-16 10:14:36 +03:00
int err;
xfClipboard* clipboard = (xfClipboard*)fuse_req_userdata(req);
UINT32 lindex;
UINT32 stream_id;
2021-01-16 10:14:36 +03:00
err = xf_cliprdr_fuse_util_lindex(clipboard, ino, &lindex);
if (err)
{
2021-01-16 10:14:36 +03:00
fuse_reply_err(req, err);
return;
}
2021-01-16 10:14:36 +03:00
err = xf_cliprdr_fuse_util_add_stream_list(clipboard, req, &stream_id);
if (err)
{
2021-01-16 10:14:36 +03:00
fuse_reply_err(req, err);
return;
}
UINT32 nPositionLow = (off >> 0) & 0xFFFFFFFF;
UINT32 nPositionHigh = (off >> 32) & 0xFFFFFFFF;
xf_cliprdr_send_client_file_contents(clipboard, stream_id, lindex, FILECONTENTS_RANGE,
nPositionLow, nPositionHigh, size);
}
static void xf_cliprdr_fuse_lookup(fuse_req_t req, fuse_ino_t parent, const char* name)
{
size_t index;
size_t count;
size_t child_ino;
BOOL found = FALSE;
struct fuse_entry_param e;
xfCliprdrFuseInode* parent_node;
2021-09-08 16:47:03 +03:00
xfCliprdrFuseInode* child_node = NULL;
xfClipboard* clipboard = (xfClipboard*)fuse_req_userdata(req);
ArrayList_Lock(clipboard->ino_list);
2021-01-16 10:14:36 +03:00
parent_node = xf_cliprdr_fuse_util_get_inode(clipboard->ino_list, parent);
if (!parent_node || !parent_node->child_inos)
{
ArrayList_Unlock(clipboard->ino_list);
fuse_reply_err(req, ENOENT);
return;
}
ArrayList_Lock(parent_node->child_inos);
count = ArrayList_Count(parent_node->child_inos);
for (index = 0; index < count; index++)
{
child_ino = (size_t)ArrayList_GetItem(parent_node->child_inos, index);
2021-01-16 10:14:36 +03:00
child_node = xf_cliprdr_fuse_util_get_inode(clipboard->ino_list, child_ino);
if (child_node && strcmp(name, child_node->name) == 0)
{
found = TRUE;
break;
}
}
ArrayList_Unlock(parent_node->child_inos);
2021-09-08 16:47:03 +03:00
if (!found || !child_node)
{
ArrayList_Unlock(clipboard->ino_list);
fuse_reply_err(req, ENOENT);
2021-01-16 10:14:36 +03:00
return;
}
2021-01-16 10:14:36 +03:00
BOOL res;
2021-01-16 10:14:36 +03:00
UINT32 stream_id;
BOOL size_set = child_node->size_set;
size_t lindex = child_node->lindex;
size_t ino = child_node->ino;
mode_t st_mode = child_node->st_mode;
off_t st_size = child_node->st_size;
time_t tv_sec = child_node->st_mtim.tv_sec;
ArrayList_Unlock(clipboard->ino_list);
if (!size_set)
{
xfCliprdrFuseStream* stream = (xfCliprdrFuseStream*)calloc(1, sizeof(xfCliprdrFuseStream));
if (!stream)
{
fuse_reply_err(req, ENOMEM);
return;
}
ArrayList_Lock(clipboard->stream_list);
stream->req = req;
stream->req_type = FILECONTENTS_SIZE;
stream->stream_id = clipboard->current_stream_id;
stream_id = stream->stream_id;
stream->req_ino = ino;
clipboard->current_stream_id++;
res = ArrayList_Append(clipboard->stream_list, stream);
2021-01-16 10:14:36 +03:00
ArrayList_Unlock(clipboard->stream_list);
if (!res)
2021-01-16 10:14:36 +03:00
{
fuse_reply_err(req, ENOMEM);
return;
}
xf_cliprdr_send_client_file_contents(clipboard, stream_id, lindex, FILECONTENTS_SIZE, 0, 0,
0);
return;
}
memset(&e, 0, sizeof(e));
e.ino = ino;
e.attr_timeout = 1.0;
e.entry_timeout = 1.0;
e.attr.st_ino = ino;
e.attr.st_mode = st_mode;
e.attr.st_nlink = 1;
e.attr.st_size = st_size;
e.attr.st_mtime = tv_sec;
fuse_reply_entry(req, &e);
return;
}
static void xf_cliprdr_fuse_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi)
{
2021-01-16 10:14:36 +03:00
int err;
mode_t mode = 0;
xfClipboard* clipboard = (xfClipboard*)fuse_req_userdata(req);
2021-01-16 10:14:36 +03:00
err = xf_cliprdr_fuse_util_stmode(clipboard, ino, &mode);
if (err)
{
2021-01-16 10:14:36 +03:00
fuse_reply_err(req, err);
return;
}
2021-01-16 10:14:36 +03:00
if ((mode & S_IFDIR) == 0)
{
fuse_reply_err(req, ENOTDIR);
}
else
{
fuse_reply_open(req, fi);
}
}
static struct fuse_lowlevel_ops xf_cliprdr_fuse_oper = {
.lookup = xf_cliprdr_fuse_lookup,
.getattr = xf_cliprdr_fuse_getattr,
.readdir = xf_cliprdr_fuse_readdir,
.open = xf_cliprdr_fuse_open,
.read = xf_cliprdr_fuse_read,
.opendir = xf_cliprdr_fuse_opendir,
};
static DWORD WINAPI xf_cliprdr_fuse_thread(LPVOID arg)
{
xfClipboard* clipboard = (xfClipboard*)arg;
/* TODO get basePath from config or use default*/
UINT32 basePathSize;
char* basePath;
char* tmpPath;
tmpPath = GetKnownPath(KNOWN_PATH_TEMP);
/* 10 is max length of DWORD string and 1 for \0 */
basePathSize = strlen(tmpPath) + strlen("/.xfreerdp.cliprdr.") + 11;
basePath = calloc(basePathSize, sizeof(char));
if (!basePath)
{
WLog_ERR(TAG, "Failed to alloc for basepath");
free(tmpPath);
return 0;
}
_snprintf(&basePath[0], basePathSize, "%s/.xfreerdp.cliprdr.%d", tmpPath,
GetCurrentProcessId());
free(tmpPath);
if (!winpr_PathFileExists(basePath) && !winpr_PathMakePath(basePath, 0))
{
WLog_ERR(TAG, "Failed to create directory '%s'", basePath);
free(basePath);
return 0;
}
clipboard->delegate->basePath = basePath;
struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
#if FUSE_USE_VERSION >= 30
fuse_opt_add_arg(&args, clipboard->delegate->basePath);
if ((clipboard->fuse_sess = fuse_session_new(
&args, &xf_cliprdr_fuse_oper, sizeof(xf_cliprdr_fuse_oper), (void*)clipboard)) != NULL)
{
if (0 == fuse_session_mount(clipboard->fuse_sess, clipboard->delegate->basePath))
{
fuse_session_loop(clipboard->fuse_sess);
fuse_session_unmount(clipboard->fuse_sess);
}
fuse_session_destroy(clipboard->fuse_sess);
}
#else
struct fuse_chan* ch;
int err;
if ((ch = fuse_mount(clipboard->delegate->basePath, &args)) != NULL)
{
clipboard->fuse_sess = fuse_lowlevel_new(&args, &xf_cliprdr_fuse_oper,
sizeof(xf_cliprdr_fuse_oper), (void*)clipboard);
if (clipboard->fuse_sess != NULL)
{
fuse_session_add_chan(clipboard->fuse_sess, ch);
err = fuse_session_loop(clipboard->fuse_sess);
fuse_session_remove_chan(ch);
fuse_session_destroy(clipboard->fuse_sess);
}
fuse_unmount(clipboard->delegate->basePath, ch);
}
#endif
fuse_opt_free_args(&args);
winpr_RemoveDirectory(clipboard->delegate->basePath);
ExitThread(0);
return 0;
}
#endif
2014-10-15 06:24:07 +04:00
xfClipboard* xf_clipboard_new(xfContext* xfc)
{
2017-07-13 13:19:10 +03:00
int i, n = 0;
2014-10-15 06:24:07 +04:00
rdpChannels* channels;
xfClipboard* clipboard;
const char* selectionAtom;
2021-03-10 13:36:23 +03:00
xfCliprdrFormat* clientFormat;
2014-10-15 06:24:07 +04:00
WINPR_ASSERT(xfc);
WINPR_ASSERT(xfc->common.context.settings);
2019-11-06 17:24:51 +03:00
if (!(clipboard = (xfClipboard*)calloc(1, sizeof(xfClipboard))))
{
WLog_ERR(TAG, "failed to allocate xfClipboard data");
return NULL;
}
2014-10-15 06:24:07 +04:00
xfc->clipboard = clipboard;
clipboard->xfc = xfc;
2019-11-06 17:24:51 +03:00
channels = ((rdpContext*)xfc)->channels;
2014-10-15 06:24:07 +04:00
clipboard->channels = channels;
clipboard->system = ClipboardCreate();
2014-10-15 23:49:57 +04:00
clipboard->requestedFormatId = -1;
2014-10-15 06:24:07 +04:00
clipboard->root_window = DefaultRootWindow(xfc->display);
selectionAtom = "CLIPBOARD";
if (xfc->common.context.settings->XSelectionAtom)
selectionAtom = xfc->common.context.settings->XSelectionAtom;
clipboard->clipboard_atom = XInternAtom(xfc->display, selectionAtom, FALSE);
2014-10-15 06:24:07 +04:00
if (clipboard->clipboard_atom == None)
{
WLog_ERR(TAG, "unable to get %s atom", selectionAtom);
goto error;
2014-10-15 06:24:07 +04:00
}
clipboard->timestamp_property_atom =
XInternAtom(xfc->display, "_FREERDP_TIMESTAMP_PROPERTY", FALSE);
2014-10-15 06:24:07 +04:00
clipboard->property_atom = XInternAtom(xfc->display, "_FREERDP_CLIPRDR", FALSE);
clipboard->raw_transfer_atom = XInternAtom(xfc->display, "_FREERDP_CLIPRDR_RAW", FALSE);
2019-11-06 17:24:51 +03:00
clipboard->raw_format_list_atom = XInternAtom(xfc->display, "_FREERDP_CLIPRDR_FORMATS", FALSE);
xf_cliprdr_set_raw_transfer_enabled(clipboard, TRUE);
2014-10-15 06:24:07 +04:00
XSelectInput(xfc->display, clipboard->root_window, PropertyChangeMask);
#ifdef WITH_XFIXES
2016-10-06 14:31:25 +03:00
if (XFixesQueryExtension(xfc->display, &clipboard->xfixes_event_base,
&clipboard->xfixes_error_base))
2014-10-15 06:24:07 +04:00
{
int xfmajor, xfminor;
2014-10-16 06:48:18 +04:00
2014-10-15 06:24:07 +04:00
if (XFixesQueryVersion(xfc->display, &xfmajor, &xfminor))
{
XFixesSelectSelectionInput(xfc->display, clipboard->root_window,
2019-11-06 17:24:51 +03:00
clipboard->clipboard_atom,
XFixesSetSelectionOwnerNotifyMask);
2014-10-15 06:24:07 +04:00
clipboard->xfixes_supported = TRUE;
}
else
{
WLog_ERR(TAG, "Error querying X Fixes extension version");
}
}
else
{
WLog_ERR(TAG, "Error loading X Fixes extension");
}
2016-10-06 14:31:25 +03:00
2014-10-15 06:24:07 +04:00
#else
2019-11-06 17:24:51 +03:00
WLog_ERR(
TAG,
"Warning: Using clipboard redirection without XFIXES extension is strongly discouraged!");
2014-10-15 06:24:07 +04:00
#endif
2021-03-10 13:36:23 +03:00
clientFormat = &clipboard->clientFormats[n++];
clientFormat->atom = XInternAtom(xfc->display, "_FREERDP_RAW", False);
clientFormat->formatId = CF_RAW;
clientFormat = &clipboard->clientFormats[n++];
clientFormat->atom = XInternAtom(xfc->display, "UTF8_STRING", False);
clientFormat->formatId = CF_UNICODETEXT;
clientFormat = &clipboard->clientFormats[n++];
clientFormat->atom = XA_STRING;
clientFormat->formatId = CF_TEXT;
clientFormat = &clipboard->clientFormats[n++];
clientFormat->atom = XInternAtom(xfc->display, "image/png", False);
clientFormat->formatId = CB_FORMAT_PNG;
clientFormat = &clipboard->clientFormats[n++];
clientFormat->atom = XInternAtom(xfc->display, "image/jpeg", False);
clientFormat->formatId = CB_FORMAT_JPEG;
clientFormat = &clipboard->clientFormats[n++];
clientFormat->atom = XInternAtom(xfc->display, "image/gif", False);
clientFormat->formatId = CB_FORMAT_GIF;
clientFormat = &clipboard->clientFormats[n++];
clientFormat->atom = XInternAtom(xfc->display, "image/bmp", False);
clientFormat->formatId = CF_DIB;
clientFormat = &clipboard->clientFormats[n++];
clientFormat->atom = XInternAtom(xfc->display, "text/html", False);
clientFormat->formatId = CB_FORMAT_HTML;
clientFormat->formatName = _strdup("HTML Format");
if (!clientFormat->formatName)
goto error;
2021-03-10 13:36:23 +03:00
clientFormat = &clipboard->clientFormats[n++];
/*
* Existence of registered format IDs for file formats does not guarantee that they are
* in fact supported by wClipboard (as further initialization may have failed after format
* registration). However, they are definitely not supported if there are no registered
* formats. In this case we should not list file formats in TARGETS.
*/
if (ClipboardGetFormatId(clipboard->system, "text/uri-list"))
{
clipboard->file_formats_registered = TRUE;
2021-03-10 13:36:23 +03:00
clientFormat->atom = XInternAtom(xfc->display, "text/uri-list", False);
clientFormat->formatId = CB_FORMAT_TEXTURILIST;
clientFormat->formatName = _strdup("FileGroupDescriptorW");
2021-03-10 13:36:23 +03:00
if (!clientFormat->formatName)
goto error;
2021-03-10 13:36:23 +03:00
clientFormat = &clipboard->clientFormats[n++];
}
if (ClipboardGetFormatId(clipboard->system, "x-special/gnome-copied-files"))
{
clipboard->file_formats_registered = TRUE;
2021-03-10 13:36:23 +03:00
clientFormat->atom = XInternAtom(xfc->display, "x-special/gnome-copied-files", False);
clientFormat->formatId = CB_FORMAT_GNOMECOPIEDFILES;
clientFormat->formatName = _strdup("FileGroupDescriptorW");
2021-03-10 13:36:23 +03:00
if (!clientFormat->formatName)
goto error;
2021-03-10 13:36:23 +03:00
clientFormat = &clipboard->clientFormats[n++];
}
if (ClipboardGetFormatId(clipboard->system, "x-special/mate-copied-files"))
{
clipboard->file_formats_registered = TRUE;
2021-03-10 13:36:23 +03:00
clientFormat->atom = XInternAtom(xfc->display, "x-special/mate-copied-files", False);
clientFormat->formatId = CB_FORMAT_MATECOPIEDFILES;
clientFormat->formatName = _strdup("FileGroupDescriptorW");
2021-03-10 13:36:23 +03:00
if (!clientFormat->formatName)
goto error;
2021-03-10 13:36:23 +03:00
clientFormat = &clipboard->clientFormats[n++];
}
2014-10-15 06:24:07 +04:00
2014-10-15 23:49:57 +04:00
clipboard->numClientFormats = n;
2014-10-15 06:24:07 +04:00
clipboard->targets[0] = XInternAtom(xfc->display, "TIMESTAMP", FALSE);
clipboard->targets[1] = XInternAtom(xfc->display, "TARGETS", FALSE);
2014-10-15 23:49:57 +04:00
clipboard->numTargets = 2;
2014-10-15 06:24:07 +04:00
clipboard->incr_atom = XInternAtom(xfc->display, "INCR", FALSE);
clipboard->delegate = ClipboardGetDelegate(clipboard->system);
clipboard->delegate->custom = clipboard;
#ifdef WITH_FUSE
clipboard->current_stream_id = 0;
clipboard->stream_list = ArrayList_New(TRUE);
if (!clipboard->stream_list)
{
WLog_ERR(TAG, "failed to allocate stream_list");
goto error;
}
wObject* obj = ArrayList_Object(clipboard->stream_list);
obj->fnObjectFree = free;
clipboard->ino_list = ArrayList_New(TRUE);
if (!clipboard->ino_list)
{
WLog_ERR(TAG, "failed to allocate stream_list");
goto error2;
}
obj = ArrayList_Object(clipboard->ino_list);
obj->fnObjectFree = xf_cliprdr_fuse_inode_free;
if (!(clipboard->fuse_thread =
CreateThread(NULL, 0, xf_cliprdr_fuse_thread, clipboard, 0, NULL)))
{
goto error3;
}
#endif
clipboard->delegate->ClipboardFileSizeSuccess = xf_cliprdr_clipboard_file_size_success;
clipboard->delegate->ClipboardFileSizeFailure = xf_cliprdr_clipboard_file_size_failure;
clipboard->delegate->ClipboardFileRangeSuccess = xf_cliprdr_clipboard_file_range_success;
clipboard->delegate->ClipboardFileRangeFailure = xf_cliprdr_clipboard_file_range_failure;
2014-10-15 06:24:07 +04:00
return clipboard;
#ifdef WITH_FUSE
error3:
ArrayList_Free(clipboard->ino_list);
error2:
ArrayList_Free(clipboard->stream_list);
#endif
error:
for (i = 0; i < n; i++)
2021-03-10 13:36:23 +03:00
{
xfCliprdrFormat* format = &clipboard->clientFormats[i];
free(format->formatName);
}
ClipboardDestroy(clipboard->system);
free(clipboard);
return NULL;
2014-10-15 06:24:07 +04:00
}
void xf_clipboard_free(xfClipboard* clipboard)
{
2014-10-16 06:56:25 +04:00
int i;
2014-10-15 06:24:07 +04:00
if (!clipboard)
return;
2014-10-16 06:56:25 +04:00
if (clipboard->serverFormats)
{
for (i = 0; i < clipboard->numServerFormats; i++)
free(clipboard->serverFormats[i].formatName);
free(clipboard->serverFormats);
clipboard->serverFormats = NULL;
}
2014-12-03 22:17:27 +03:00
if (clipboard->numClientFormats)
{
for (i = 0; i < clipboard->numClientFormats; i++)
2021-03-10 13:36:23 +03:00
{
xfCliprdrFormat* format = &clipboard->clientFormats[i];
free(format->formatName);
}
2014-12-03 22:17:27 +03:00
}
#ifdef WITH_FUSE
if (clipboard->fuse_thread)
{
if (clipboard->fuse_sess)
{
fuse_session_exit(clipboard->fuse_sess);
/* not elegant but works for umounting FUSE
fuse_chan must receieve a oper buf to unblock fuse_session_receive_buf function.
*/
winpr_PathFileExists(clipboard->delegate->basePath);
}
WaitForSingleObject(clipboard->fuse_thread, INFINITE);
CloseHandle(clipboard->fuse_thread);
}
2021-01-16 10:14:36 +03:00
if (clipboard->delegate)
free(clipboard->delegate->basePath);
// fuse related
ArrayList_Free(clipboard->stream_list);
ArrayList_Free(clipboard->ino_list);
#endif
ClipboardDestroy(clipboard->system);
xf_clipboard_formats_free(clipboard);
2014-10-15 06:24:07 +04:00
free(clipboard->data);
free(clipboard->data_raw);
2014-10-15 06:24:07 +04:00
free(clipboard->respond);
free(clipboard->incr_data);
free(clipboard);
}
void xf_cliprdr_init(xfContext* xfc, CliprdrClientContext* cliprdr)
{
xfc->cliprdr = cliprdr;
xfc->clipboard->context = cliprdr;
2019-11-06 17:24:51 +03:00
cliprdr->custom = (void*)xfc->clipboard;
2014-10-15 06:24:07 +04:00
cliprdr->MonitorReady = xf_cliprdr_monitor_ready;
cliprdr->ServerCapabilities = xf_cliprdr_server_capabilities;
cliprdr->ServerFormatList = xf_cliprdr_server_format_list;
cliprdr->ServerFormatListResponse = xf_cliprdr_server_format_list_response;
cliprdr->ServerFormatDataRequest = xf_cliprdr_server_format_data_request;
cliprdr->ServerFormatDataResponse = xf_cliprdr_server_format_data_response;
cliprdr->ServerFileContentsRequest = xf_cliprdr_server_file_contents_request;
#ifdef WITH_FUSE
cliprdr->ServerFileContentsResponse = xf_cliprdr_server_file_contents_response;
#endif
2014-10-15 06:24:07 +04:00
}
void xf_cliprdr_uninit(xfContext* xfc, CliprdrClientContext* cliprdr)
{
xfc->cliprdr = NULL;
cliprdr->custom = NULL;
if (xfc->clipboard)
xfc->clipboard->context = NULL;
2014-10-15 06:24:07 +04:00
}