mirror of
https://github.com/FreeRDP/FreeRDP.git
synced 2025-06-03 00:00:20 +00:00
x11: add support for remote to local clipboard file copy
This commit is contained in:
parent
6a2ae388f8
commit
d537988f28
@ -226,6 +226,10 @@ if(WITH_XFIXES)
|
|||||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${XFIXES_LIBRARIES})
|
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${XFIXES_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_package(FUSE REQUIRED)
|
||||||
|
include_directories(${FUSE_INCLUDE_DIRS})
|
||||||
|
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${FUSE_LIBRARIES})
|
||||||
|
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/resources)
|
include_directories(${CMAKE_SOURCE_DIR}/resources)
|
||||||
|
|
||||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client freerdp m)
|
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client freerdp m)
|
||||||
|
@ -31,10 +31,18 @@
|
|||||||
#include <X11/extensions/Xfixes.h>
|
#include <X11/extensions/Xfixes.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define FUSE_USE_VERSION 26
|
||||||
|
#include <fuse/fuse_lowlevel.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include <winpr/crt.h>
|
#include <winpr/crt.h>
|
||||||
#include <winpr/image.h>
|
#include <winpr/image.h>
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#include <winpr/clipboard.h>
|
#include <winpr/clipboard.h>
|
||||||
|
#include <winpr/path.h>
|
||||||
|
|
||||||
#include <freerdp/log.h>
|
#include <freerdp/log.h>
|
||||||
#include <freerdp/client/cliprdr.h>
|
#include <freerdp/client/cliprdr.h>
|
||||||
@ -55,6 +63,33 @@ struct xf_cliprdr_format
|
|||||||
};
|
};
|
||||||
typedef struct xf_cliprdr_format xfCliprdrFormat;
|
typedef struct xf_cliprdr_format xfCliprdrFormat;
|
||||||
|
|
||||||
|
struct xf_cliprdr_fuse_stream
|
||||||
|
{
|
||||||
|
UINT32 stream_id;
|
||||||
|
fuse_req_t req;
|
||||||
|
};
|
||||||
|
typedef struct xf_cliprdr_fuse_stream xfCliprdrFuseStream;
|
||||||
|
|
||||||
|
struct xf_cliprdr_fuse_inode
|
||||||
|
{
|
||||||
|
size_t parent_ino;
|
||||||
|
size_t ino;
|
||||||
|
size_t lindex;
|
||||||
|
mode_t st_mode;
|
||||||
|
off_t st_size;
|
||||||
|
struct timespec st_mtim;
|
||||||
|
char* name;
|
||||||
|
wArrayList* child_inos;
|
||||||
|
};
|
||||||
|
typedef struct xf_cliprdr_fuse_inode xfCliprdrFuseInode;
|
||||||
|
|
||||||
|
void xf_cliprdr_fuse_inode_free(void* obj)
|
||||||
|
{
|
||||||
|
xfCliprdrFuseInode* inode = (xfCliprdrFuseInode*)obj;
|
||||||
|
free(inode->name);
|
||||||
|
ArrayList_Free(inode->child_inos);
|
||||||
|
}
|
||||||
|
|
||||||
struct xf_clipboard
|
struct xf_clipboard
|
||||||
{
|
{
|
||||||
xfContext* xfc;
|
xfContext* xfc;
|
||||||
@ -111,6 +146,15 @@ struct xf_clipboard
|
|||||||
/* File clipping */
|
/* File clipping */
|
||||||
BOOL streams_supported;
|
BOOL streams_supported;
|
||||||
BOOL file_formats_registered;
|
BOOL file_formats_registered;
|
||||||
|
|
||||||
|
/* FUSE related**/
|
||||||
|
HANDLE fuse_thread;
|
||||||
|
struct fuse_session* fuse_sess;
|
||||||
|
|
||||||
|
/* fuse reset per copy*/
|
||||||
|
wArrayList* stream_list;
|
||||||
|
UINT32 current_stream_id;
|
||||||
|
wArrayList* ino_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
static UINT xf_cliprdr_send_client_format_list(xfClipboard* clipboard);
|
static UINT xf_cliprdr_send_client_format_list(xfClipboard* clipboard);
|
||||||
@ -824,6 +868,29 @@ static void xf_cliprdr_clear_cached_data(xfClipboard* clipboard)
|
|||||||
}
|
}
|
||||||
|
|
||||||
clipboard->data_raw_length = 0;
|
clipboard->data_raw_length = 0;
|
||||||
|
|
||||||
|
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 */
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard,
|
static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard,
|
||||||
@ -1157,6 +1224,66 @@ static UINT xf_cliprdr_send_client_format_list_response(xfClipboard* clipboard,
|
|||||||
return clipboard->context->ClientFormatListResponse(clipboard->context, &formatListResponse);
|
return clipboard->context->ClientFormatListResponse(clipboard->context, &formatListResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 nPositionLow,
|
||||||
|
UINT32 nPositionHigh, UINT32 cbRequested)
|
||||||
|
{
|
||||||
|
CLIPRDR_FILE_CONTENTS_REQUEST formatFileContentsRequest;
|
||||||
|
formatFileContentsRequest.streamId = streamId;
|
||||||
|
formatFileContentsRequest.listIndex = listIndex;
|
||||||
|
formatFileContentsRequest.dwFlags = FILECONTENTS_RANGE;
|
||||||
|
formatFileContentsRequest.nPositionHigh = nPositionHigh;
|
||||||
|
formatFileContentsRequest.nPositionLow = nPositionLow;
|
||||||
|
formatFileContentsRequest.cbRequested = cbRequested;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
UINT32 count;
|
||||||
|
UINT32 index;
|
||||||
|
BOOL found = FALSE;
|
||||||
|
xfCliprdrFuseStream* stream;
|
||||||
|
xfClipboard* clipboard = (xfClipboard*)context->custom;
|
||||||
|
UINT32 stream_id = fileContentsResponse->streamId;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArrayList_Unlock(clipboard->stream_list);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
fuse_reply_buf(stream->req, (const char*)fileContentsResponse->requestedData,
|
||||||
|
fileContentsResponse->cbRequested);
|
||||||
|
ArrayList_RemoveAt(clipboard->stream_list, index);
|
||||||
|
}
|
||||||
|
return CHANNEL_RC_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function description
|
* Function description
|
||||||
*
|
*
|
||||||
@ -1388,6 +1515,19 @@ xf_cliprdr_server_format_data_request(CliprdrClientContext* context,
|
|||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function description
|
* Function description
|
||||||
*
|
*
|
||||||
@ -1403,6 +1543,7 @@ xf_cliprdr_server_format_data_response(CliprdrClientContext* context,
|
|||||||
UINT32 SrcSize;
|
UINT32 SrcSize;
|
||||||
UINT32 srcFormatId;
|
UINT32 srcFormatId;
|
||||||
UINT32 dstFormatId;
|
UINT32 dstFormatId;
|
||||||
|
xfCliprdrFormat* dstTargetFormat;
|
||||||
BOOL nullTerminated = FALSE;
|
BOOL nullTerminated = FALSE;
|
||||||
UINT32 size = formatDataResponse->dataLen;
|
UINT32 size = formatDataResponse->dataLen;
|
||||||
const BYTE* data = formatDataResponse->requestedFormatData;
|
const BYTE* data = formatDataResponse->requestedFormatData;
|
||||||
@ -1434,9 +1575,133 @@ xf_cliprdr_server_format_data_response(CliprdrClientContext* context,
|
|||||||
|
|
||||||
if (strcmp(clipboard->data_format_name, "FileGroupDescriptorW") == 0)
|
if (strcmp(clipboard->data_format_name, "FileGroupDescriptorW") == 0)
|
||||||
{
|
{
|
||||||
|
/* Build inode table for FILEDESCRIPTORW*/
|
||||||
|
/* Avoid Crash on empty message*/
|
||||||
|
if (size >= 4)
|
||||||
|
{
|
||||||
|
UINT32 nrDescriptors = (UINT32)(data[3] << 24) | (UINT32)(data[2] << 16) |
|
||||||
|
(UINT32)(data[1] << 8) | (data[0] & 0xFF);
|
||||||
|
const FILEDESCRIPTORW* descriptors = (const FILEDESCRIPTORW*)&data[4];
|
||||||
|
size_t count = (size - 4) / sizeof(FILEDESCRIPTORW);
|
||||||
|
|
||||||
|
if (count >= 1 && count == nrDescriptors)
|
||||||
|
{
|
||||||
|
size_t lindex;
|
||||||
|
wHashTable* mapDir;
|
||||||
|
xfCliprdrFuseInode* rootNode =
|
||||||
|
(xfCliprdrFuseInode*)calloc(1, sizeof(xfCliprdrFuseInode));
|
||||||
|
|
||||||
|
rootNode->ino = 1;
|
||||||
|
rootNode->parent_ino = 1;
|
||||||
|
rootNode->name = _strdup("/");
|
||||||
|
rootNode->st_mode = S_IFDIR | 0755;
|
||||||
|
rootNode->child_inos = ArrayList_New(TRUE);
|
||||||
|
rootNode->st_mtim.tv_sec = time(NULL);
|
||||||
|
ArrayList_Add(clipboard->ino_list, rootNode);
|
||||||
|
|
||||||
|
mapDir = HashTable_New(TRUE);
|
||||||
|
mapDir->keyFree = HashTable_StringFree;
|
||||||
|
mapDir->keyClone = HashTable_StringClone;
|
||||||
|
mapDir->keyCompare = HashTable_StringCompare;
|
||||||
|
mapDir->hash = HashTable_StringHash;
|
||||||
|
|
||||||
|
/* here we assume that parent folder always appears before its children */
|
||||||
|
for (lindex = 0; lindex < count; lindex++)
|
||||||
|
{
|
||||||
|
xfCliprdrFuseInode* inode =
|
||||||
|
(xfCliprdrFuseInode*)calloc(1, sizeof(xfCliprdrFuseInode));
|
||||||
|
size_t curLen = _wcsnlen(descriptors[lindex].cFileName,
|
||||||
|
ARRAYSIZE(descriptors[lindex].cFileName));
|
||||||
|
char* curName = NULL;
|
||||||
|
int newLen = ConvertFromUnicode(CP_UTF8, 0, descriptors[lindex].cFileName,
|
||||||
|
(int)curLen, &curName, 0, NULL, NULL);
|
||||||
|
char* split_point = xf_cliprdr_fuse_split_basename(curName, newLen);
|
||||||
|
char* dirName = NULL;
|
||||||
|
char* baseName = NULL;
|
||||||
|
UINT64 ticks;
|
||||||
|
xfCliprdrFuseInode* parent;
|
||||||
|
|
||||||
|
inode->lindex = lindex;
|
||||||
|
inode->ino = lindex + 2;
|
||||||
|
|
||||||
|
if (split_point == NULL)
|
||||||
|
{
|
||||||
|
baseName = _strdup(curName);
|
||||||
|
inode->parent_ino = 1;
|
||||||
|
inode->name = baseName;
|
||||||
|
ArrayList_Add(rootNode->child_inos, (void*)inode->ino);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dirName = calloc(split_point - curName + 1, sizeof(char));
|
||||||
|
_snprintf(dirName, split_point - curName + 1, "%s", curName);
|
||||||
|
/* drop last '\\' */
|
||||||
|
baseName = _strdup(split_point + 1);
|
||||||
|
|
||||||
|
parent = (xfCliprdrFuseInode*)HashTable_GetItemValue(mapDir, dirName);
|
||||||
|
inode->parent_ino = parent->ino;
|
||||||
|
inode->name = baseName;
|
||||||
|
ArrayList_Add(parent->child_inos, (void*)inode->ino);
|
||||||
|
free(dirName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((descriptors[lindex].dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
||||||
|
{
|
||||||
|
inode->st_mode = S_IFDIR | 0755;
|
||||||
|
inode->child_inos = ArrayList_New(TRUE);
|
||||||
|
HashTable_Add(mapDir, _strdup(curName), inode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inode->st_mode = S_IFREG | 0644;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(curName);
|
||||||
|
|
||||||
|
inode->st_size = (((UINT64)descriptors[lindex].nFileSizeHigh) << 32) |
|
||||||
|
((UINT64)descriptors[lindex].nFileSizeLow);
|
||||||
|
ticks =
|
||||||
|
(((UINT64)descriptors[lindex].ftLastWriteTime.dwHighDateTime << 32) |
|
||||||
|
((UINT64)descriptors[lindex].ftLastWriteTime.dwLowDateTime)) -
|
||||||
|
116444736000000000;
|
||||||
|
inode->st_mtim.tv_sec = ticks / 10000000ULL;
|
||||||
|
/* tv_nsec Not used for now */
|
||||||
|
inode->st_mtim.tv_nsec = ticks % 10000000ULL;
|
||||||
|
ArrayList_Add(clipboard->ino_list, inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashTable_Free(mapDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
srcFormatId = ClipboardGetFormatId(clipboard->system, "FileGroupDescriptorW");
|
srcFormatId = ClipboardGetFormatId(clipboard->system, "FileGroupDescriptorW");
|
||||||
dstFormatId = ClipboardGetFormatId(clipboard->system, "text/uri-list");
|
dstTargetFormat =
|
||||||
nullTerminated = FALSE;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1663,6 +1928,334 @@ static UINT xf_cliprdr_clipboard_file_range_failure(wClipboardDelegate* delegate
|
|||||||
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
|
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void xf_cliprdr_fuse_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
double timeout = 0;
|
||||||
|
struct stat stbuf;
|
||||||
|
|
||||||
|
xfCliprdrFuseInode* node;
|
||||||
|
xfClipboard* clipboard = (xfClipboard*)fuse_req_userdata(req);
|
||||||
|
|
||||||
|
ArrayList_Lock(clipboard->ino_list);
|
||||||
|
node = (xfCliprdrFuseInode*)ArrayList_GetItem(clipboard->ino_list, ino - 1);
|
||||||
|
ArrayList_Unlock(clipboard->ino_list);
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
fuse_reply_err(req, ENOENT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memset(&stbuf, 0, sizeof(stbuf));
|
||||||
|
stbuf.st_ino = ino;
|
||||||
|
stbuf.st_mode = node->st_mode;
|
||||||
|
stbuf.st_mtime = node->st_mtim.tv_sec;
|
||||||
|
if (ino == FUSE_ROOT_ID)
|
||||||
|
{
|
||||||
|
stbuf.st_nlink = 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stbuf.st_nlink = 1;
|
||||||
|
}
|
||||||
|
fuse_reply_attr(req, &stbuf, timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = (char*)calloc(size, sizeof(char));
|
||||||
|
size_t pos = 0;
|
||||||
|
xfCliprdrFuseInode* child_node;
|
||||||
|
xfCliprdrFuseInode* node;
|
||||||
|
xfClipboard* clipboard = (xfClipboard*)fuse_req_userdata(req);
|
||||||
|
|
||||||
|
ArrayList_Lock(clipboard->ino_list);
|
||||||
|
node = (xfCliprdrFuseInode*)ArrayList_GetItem(clipboard->ino_list, ino - 1);
|
||||||
|
ArrayList_Unlock(clipboard->ino_list);
|
||||||
|
if (!node || !node->child_inos)
|
||||||
|
{
|
||||||
|
fuse_reply_err(req, ENOENT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ((node->st_mode & S_IFDIR) == 0)
|
||||||
|
{
|
||||||
|
fuse_reply_err(req, ENOTDIR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList_Lock(node->child_inos);
|
||||||
|
count = ArrayList_Count(node->child_inos);
|
||||||
|
if (count == 0 || count + 1 <= off)
|
||||||
|
{
|
||||||
|
fuse_reply_buf(req, NULL, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (index = off; index < count + 2; index++)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
struct stat stbuf;
|
||||||
|
stbuf.st_ino = ino;
|
||||||
|
direntry_len = fuse_add_direntry(req, NULL, 0, ".", NULL, index);
|
||||||
|
if (pos + direntry_len > size)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fuse_add_direntry(req, buf + pos, size - pos, ".", &stbuf, index);
|
||||||
|
pos += direntry_len;
|
||||||
|
}
|
||||||
|
else if (index == 1)
|
||||||
|
{
|
||||||
|
struct stat stbuf;
|
||||||
|
stbuf.st_ino = node->parent_ino;
|
||||||
|
direntry_len = fuse_add_direntry(req, NULL, 0, "..", NULL, index);
|
||||||
|
if (pos + direntry_len > size)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fuse_add_direntry(req, buf + pos, size - pos, "..", &stbuf, index);
|
||||||
|
pos += direntry_len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
child_ino = (size_t)ArrayList_GetItem(node->child_inos, index - 2);
|
||||||
|
ArrayList_Lock(clipboard->ino_list);
|
||||||
|
child_node =
|
||||||
|
(xfCliprdrFuseInode*)ArrayList_GetItem(clipboard->ino_list, child_ino - 1);
|
||||||
|
ArrayList_Unlock(clipboard->ino_list);
|
||||||
|
// child_node must exists?
|
||||||
|
direntry_len = fuse_add_direntry(req, NULL, 0, child_node->name, NULL, index);
|
||||||
|
if (pos + direntry_len > size)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat stbuf;
|
||||||
|
stbuf.st_ino = child_node->ino;
|
||||||
|
fuse_add_direntry(req, buf + pos, size - pos, child_node->name, &stbuf, index);
|
||||||
|
pos += direntry_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fuse_reply_buf(req, buf, pos);
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
ArrayList_Unlock(node->child_inos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xf_cliprdr_fuse_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
xfCliprdrFuseInode* node;
|
||||||
|
xfClipboard* clipboard = (xfClipboard*)fuse_req_userdata(req);
|
||||||
|
|
||||||
|
ArrayList_Lock(clipboard->ino_list);
|
||||||
|
node = (xfCliprdrFuseInode*)ArrayList_GetItem(clipboard->ino_list, ino - 1);
|
||||||
|
ArrayList_Unlock(clipboard->ino_list);
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
fuse_reply_err(req, ENOENT);
|
||||||
|
}
|
||||||
|
else if ((node->st_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, ENONET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
xfCliprdrFuseInode* node;
|
||||||
|
xfClipboard* clipboard = (xfClipboard*)fuse_req_userdata(req);
|
||||||
|
|
||||||
|
ArrayList_Lock(clipboard->ino_list);
|
||||||
|
node = (xfCliprdrFuseInode*)ArrayList_GetItem(clipboard->ino_list, ino - 1);
|
||||||
|
ArrayList_Unlock(clipboard->ino_list);
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
fuse_reply_err(req, ENONET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ((node->st_mode & S_IFDIR) != 0)
|
||||||
|
{
|
||||||
|
fuse_reply_err(req, EISDIR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
xfCliprdrFuseStream* stream = (xfCliprdrFuseStream*)calloc(1, sizeof(xfCliprdrFuseStream));
|
||||||
|
|
||||||
|
ArrayList_Lock(clipboard->stream_list);
|
||||||
|
stream->req = req;
|
||||||
|
stream->stream_id = clipboard->current_stream_id;
|
||||||
|
clipboard->current_stream_id++;
|
||||||
|
ArrayList_Add(clipboard->stream_list, stream);
|
||||||
|
ArrayList_Unlock(clipboard->stream_list);
|
||||||
|
|
||||||
|
UINT32 nPositionLow = (off >> 0) & 0xFFFFFFFF;
|
||||||
|
UINT32 nPositionHigh = (off >> 32) & 0xFFFFFFFF;
|
||||||
|
|
||||||
|
xf_cliprdr_send_client_file_contents(clipboard, stream->stream_id, node->lindex, 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;
|
||||||
|
xfCliprdrFuseInode* child_node;
|
||||||
|
xfClipboard* clipboard = (xfClipboard*)fuse_req_userdata(req);
|
||||||
|
|
||||||
|
ArrayList_Lock(clipboard->ino_list);
|
||||||
|
parent_node = (xfCliprdrFuseInode*)ArrayList_GetItem(clipboard->ino_list, parent - 1);
|
||||||
|
ArrayList_Unlock(clipboard->ino_list);
|
||||||
|
if (!parent_node || !parent_node->child_inos)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
ArrayList_Lock(clipboard->ino_list);
|
||||||
|
child_node = (xfCliprdrFuseInode*)ArrayList_GetItem(clipboard->ino_list, child_ino - 1);
|
||||||
|
ArrayList_Unlock(clipboard->ino_list);
|
||||||
|
if (child_node && strcmp(name, child_node->name) == 0)
|
||||||
|
{
|
||||||
|
found = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArrayList_Unlock(parent_node->child_inos);
|
||||||
|
if (found == TRUE)
|
||||||
|
{
|
||||||
|
memset(&e, 0, sizeof(e));
|
||||||
|
e.ino = child_node->ino;
|
||||||
|
e.attr_timeout = 1.0;
|
||||||
|
e.entry_timeout = 1.0;
|
||||||
|
e.attr.st_ino = child_node->ino;
|
||||||
|
e.attr.st_mode = child_node->st_mode;
|
||||||
|
e.attr.st_nlink = 1;
|
||||||
|
e.attr.st_size = child_node->st_size;
|
||||||
|
e.attr.st_mtime = child_node->st_mtim.tv_sec;
|
||||||
|
fuse_reply_entry(req, &e);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fuse_reply_err(req, ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xf_cliprdr_fuse_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
xfCliprdrFuseInode* node;
|
||||||
|
xfClipboard* clipboard = (xfClipboard*)fuse_req_userdata(req);
|
||||||
|
|
||||||
|
ArrayList_Lock(clipboard->ino_list);
|
||||||
|
node = (xfCliprdrFuseInode*)ArrayList_GetItem(clipboard->ino_list, ino - 1);
|
||||||
|
ArrayList_Unlock(clipboard->ino_list);
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
fuse_reply_err(req, ENOENT);
|
||||||
|
}
|
||||||
|
else if ((node->st_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: set up a filesystem base path for local URI */
|
||||||
|
/* 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));
|
||||||
|
_snprintf(&basePath[0], basePathSize, "%s/.xfreerdp.cliprdr.%d", tmpPath,
|
||||||
|
GetCurrentProcessId());
|
||||||
|
free(tmpPath);
|
||||||
|
|
||||||
|
if (!PathFileExistsA(basePath) && !PathMakePathA(basePath, 0))
|
||||||
|
{
|
||||||
|
WLog_ERR(TAG, "Failed to create directory '%s'", basePath);
|
||||||
|
free(basePath);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
clipboard->delegate->basePath = basePath;
|
||||||
|
|
||||||
|
struct fuse_chan* ch;
|
||||||
|
struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (fuse_set_signal_handlers(clipboard->fuse_sess) != -1)
|
||||||
|
{
|
||||||
|
fuse_session_add_chan(clipboard->fuse_sess, ch);
|
||||||
|
err = fuse_session_loop(clipboard->fuse_sess);
|
||||||
|
fuse_remove_signal_handlers(clipboard->fuse_sess);
|
||||||
|
fuse_session_remove_chan(ch);
|
||||||
|
}
|
||||||
|
fuse_session_destroy(clipboard->fuse_sess);
|
||||||
|
}
|
||||||
|
fuse_unmount(clipboard->delegate->basePath, ch);
|
||||||
|
}
|
||||||
|
fuse_opt_free_args(&args);
|
||||||
|
|
||||||
|
if (PathFileExistsA(clipboard->delegate->basePath))
|
||||||
|
{
|
||||||
|
RemoveDirectoryA(clipboard->delegate->basePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExitThread(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
xfClipboard* xf_clipboard_new(xfContext* xfc)
|
xfClipboard* xf_clipboard_new(xfContext* xfc)
|
||||||
{
|
{
|
||||||
int i, n = 0;
|
int i, n = 0;
|
||||||
@ -1735,6 +2328,8 @@ xfClipboard* xf_clipboard_new(xfContext* xfc)
|
|||||||
n++;
|
n++;
|
||||||
clipboard->clientFormats[n].atom = XInternAtom(xfc->display, "UTF8_STRING", False);
|
clipboard->clientFormats[n].atom = XInternAtom(xfc->display, "UTF8_STRING", False);
|
||||||
clipboard->clientFormats[n].formatId = CF_UNICODETEXT;
|
clipboard->clientFormats[n].formatId = CF_UNICODETEXT;
|
||||||
|
/* This line for nautilus based file managers, beacuse they use UTF8_STRING for file-list */
|
||||||
|
clipboard->clientFormats[n].formatName = _strdup("FileGroupDescriptorW");
|
||||||
n++;
|
n++;
|
||||||
clipboard->clientFormats[n].atom = XA_STRING;
|
clipboard->clientFormats[n].atom = XA_STRING;
|
||||||
clipboard->clientFormats[n].formatId = CF_TEXT;
|
clipboard->clientFormats[n].formatId = CF_TEXT;
|
||||||
@ -1778,6 +2373,33 @@ xfClipboard* xf_clipboard_new(xfContext* xfc)
|
|||||||
|
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
if (ClipboardGetFormatId(clipboard->system, "x-special/gnome-copied-files"))
|
||||||
|
{
|
||||||
|
clipboard->file_formats_registered = TRUE;
|
||||||
|
clipboard->clientFormats[n].atom =
|
||||||
|
XInternAtom(xfc->display, "x-special/gnome-copied-files", False);
|
||||||
|
clipboard->clientFormats[n].formatId = CB_FORMAT_GNOMECOPIEDFILES;
|
||||||
|
clipboard->clientFormats[n].formatName = _strdup("FileGroupDescriptorW");
|
||||||
|
|
||||||
|
if (!clipboard->clientFormats[n].formatName)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ClipboardGetFormatId(clipboard->system, "x-special/mate-copied-files"))
|
||||||
|
{
|
||||||
|
clipboard->file_formats_registered = TRUE;
|
||||||
|
clipboard->clientFormats[n].atom =
|
||||||
|
XInternAtom(xfc->display, "x-special/mate-copied-files", False);
|
||||||
|
clipboard->clientFormats[n].formatId = CB_FORMAT_MATECOPIEDFILES;
|
||||||
|
clipboard->clientFormats[n].formatName = _strdup("FileGroupDescriptorW");
|
||||||
|
|
||||||
|
if (!clipboard->clientFormats[n].formatName)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
clipboard->numClientFormats = n;
|
clipboard->numClientFormats = n;
|
||||||
clipboard->targets[0] = XInternAtom(xfc->display, "TIMESTAMP", FALSE);
|
clipboard->targets[0] = XInternAtom(xfc->display, "TIMESTAMP", FALSE);
|
||||||
@ -1786,8 +2408,22 @@ xfClipboard* xf_clipboard_new(xfContext* xfc)
|
|||||||
clipboard->incr_atom = XInternAtom(xfc->display, "INCR", FALSE);
|
clipboard->incr_atom = XInternAtom(xfc->display, "INCR", FALSE);
|
||||||
clipboard->delegate = ClipboardGetDelegate(clipboard->system);
|
clipboard->delegate = ClipboardGetDelegate(clipboard->system);
|
||||||
clipboard->delegate->custom = clipboard;
|
clipboard->delegate->custom = clipboard;
|
||||||
/* TODO: set up a filesystem base path for local URI */
|
|
||||||
/* clipboard->delegate->basePath = "file:///tmp/foo/bar/gaga"; */
|
clipboard->current_stream_id = 0;
|
||||||
|
clipboard->stream_list = ArrayList_New(TRUE);
|
||||||
|
wObject* obj = ArrayList_Object(clipboard->stream_list);
|
||||||
|
obj->fnObjectFree = free;
|
||||||
|
|
||||||
|
clipboard->ino_list = ArrayList_New(TRUE);
|
||||||
|
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 error;
|
||||||
|
}
|
||||||
|
|
||||||
clipboard->delegate->ClipboardFileSizeSuccess = xf_cliprdr_clipboard_file_size_success;
|
clipboard->delegate->ClipboardFileSizeSuccess = xf_cliprdr_clipboard_file_size_success;
|
||||||
clipboard->delegate->ClipboardFileSizeFailure = xf_cliprdr_clipboard_file_size_failure;
|
clipboard->delegate->ClipboardFileSizeFailure = xf_cliprdr_clipboard_file_size_failure;
|
||||||
clipboard->delegate->ClipboardFileRangeSuccess = xf_cliprdr_clipboard_file_range_success;
|
clipboard->delegate->ClipboardFileRangeSuccess = xf_cliprdr_clipboard_file_range_success;
|
||||||
@ -1825,6 +2461,26 @@ void xf_clipboard_free(xfClipboard* clipboard)
|
|||||||
free(clipboard->clientFormats[i].formatName);
|
free(clipboard->clientFormats[i].formatName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
PathFileExistsA(clipboard->delegate->basePath);
|
||||||
|
}
|
||||||
|
WaitForSingleObject(clipboard->fuse_thread, INFINITE);
|
||||||
|
CloseHandle(clipboard->fuse_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(clipboard->delegate->basePath);
|
||||||
|
|
||||||
|
// fuse related
|
||||||
|
ArrayList_Free(clipboard->stream_list);
|
||||||
|
ArrayList_Free(clipboard->ino_list);
|
||||||
|
|
||||||
ClipboardDestroy(clipboard->system);
|
ClipboardDestroy(clipboard->system);
|
||||||
free(clipboard->data);
|
free(clipboard->data);
|
||||||
free(clipboard->data_raw);
|
free(clipboard->data_raw);
|
||||||
@ -1845,6 +2501,7 @@ void xf_cliprdr_init(xfContext* xfc, CliprdrClientContext* cliprdr)
|
|||||||
cliprdr->ServerFormatDataRequest = xf_cliprdr_server_format_data_request;
|
cliprdr->ServerFormatDataRequest = xf_cliprdr_server_format_data_request;
|
||||||
cliprdr->ServerFormatDataResponse = xf_cliprdr_server_format_data_response;
|
cliprdr->ServerFormatDataResponse = xf_cliprdr_server_format_data_response;
|
||||||
cliprdr->ServerFileContentsRequest = xf_cliprdr_server_file_contents_request;
|
cliprdr->ServerFileContentsRequest = xf_cliprdr_server_file_contents_request;
|
||||||
|
cliprdr->ServerFileContentsResponse = xf_cliprdr_server_file_contents_response;
|
||||||
}
|
}
|
||||||
|
|
||||||
void xf_cliprdr_uninit(xfContext* xfc, CliprdrClientContext* cliprdr)
|
void xf_cliprdr_uninit(xfContext* xfc, CliprdrClientContext* cliprdr)
|
||||||
|
34
cmake/FindFUSE.cmake
Normal file
34
cmake/FindFUSE.cmake
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Find the FUSE includes and library
|
||||||
|
#
|
||||||
|
# FUSE_INCLUDE_DIR - where to find fuse.h, etc.
|
||||||
|
# FUSE_LIBRARIES - List of libraries when using FUSE.
|
||||||
|
# FUSE_FOUND - True if FUSE lib is found.
|
||||||
|
|
||||||
|
# check if already in cache, be silent
|
||||||
|
IF (FUSE_INCLUDE_DIR)
|
||||||
|
SET (FUSE_FIND_QUIETLY TRUE)
|
||||||
|
ENDIF (FUSE_INCLUDE_DIR)
|
||||||
|
|
||||||
|
# find includes
|
||||||
|
FIND_PATH (FUSE_INCLUDE_DIR fuse.h
|
||||||
|
/usr/local/include/osxfuse
|
||||||
|
/usr/local/include
|
||||||
|
/usr/include
|
||||||
|
)
|
||||||
|
|
||||||
|
# find lib
|
||||||
|
if (APPLE)
|
||||||
|
SET(FUSE_NAMES libosxfuse.dylib fuse)
|
||||||
|
else (APPLE)
|
||||||
|
SET(FUSE_NAMES fuse)
|
||||||
|
endif (APPLE)
|
||||||
|
FIND_LIBRARY(FUSE_LIBRARIES
|
||||||
|
NAMES ${FUSE_NAMES}
|
||||||
|
PATHS /lib64 /lib /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib /usr/lib/x86_64-linux-gnu
|
||||||
|
)
|
||||||
|
|
||||||
|
include ("FindPackageHandleStandardArgs")
|
||||||
|
find_package_handle_standard_args ("FUSE" DEFAULT_MSG
|
||||||
|
FUSE_INCLUDE_DIR FUSE_LIBRARIES)
|
||||||
|
|
||||||
|
mark_as_advanced (FUSE_INCLUDE_DIR FUSE_LIBRARIES)
|
@ -36,6 +36,8 @@
|
|||||||
#define CB_FORMAT_JPEG 0xD012
|
#define CB_FORMAT_JPEG 0xD012
|
||||||
#define CB_FORMAT_GIF 0xD013
|
#define CB_FORMAT_GIF 0xD013
|
||||||
#define CB_FORMAT_TEXTURILIST 0xD014
|
#define CB_FORMAT_TEXTURILIST 0xD014
|
||||||
|
#define CB_FORMAT_GNOMECOPIEDFILES 0xD015
|
||||||
|
#define CB_FORMAT_MATECOPIEDFILES 0xD016
|
||||||
|
|
||||||
/* CLIPRDR_HEADER.msgType */
|
/* CLIPRDR_HEADER.msgType */
|
||||||
#define CB_MONITOR_READY 0x0001
|
#define CB_MONITOR_READY 0x0001
|
||||||
|
@ -589,6 +589,7 @@ static void* convert_filedescriptors_to_uri_list(wClipboard* clipboard, UINT32 f
|
|||||||
size_t count, x, alloc, pos, baseLength = 0;
|
size_t count, x, alloc, pos, baseLength = 0;
|
||||||
const char* src = (const char*)data;
|
const char* src = (const char*)data;
|
||||||
char* dst;
|
char* dst;
|
||||||
|
size_t decoration_len;
|
||||||
|
|
||||||
if (!clipboard || !data || !pSize)
|
if (!clipboard || !data || !pSize)
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -616,14 +617,24 @@ static void* convert_filedescriptors_to_uri_list(wClipboard* clipboard, UINT32 f
|
|||||||
if (formatId != ClipboardGetFormatId(clipboard, "FileGroupDescriptorW"))
|
if (formatId != ClipboardGetFormatId(clipboard, "FileGroupDescriptorW"))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
decoration_len = strlen("file:///\n") + baseLength;
|
||||||
alloc = 0;
|
alloc = 0;
|
||||||
|
|
||||||
/* Get total size of file names */
|
/* Get total size of file/folder names under first level folder only */
|
||||||
for (x = 0; x < count; x++)
|
for (x = 0; x < count; x++)
|
||||||
alloc += _wcsnlen(descriptors[x].cFileName, ARRAYSIZE(descriptors[x].cFileName));
|
{
|
||||||
|
if (_wcschr(descriptors[x].cFileName, L'\\') == NULL)
|
||||||
|
{
|
||||||
|
size_t curLen = _wcsnlen(descriptors[x].cFileName, ARRAYSIZE(descriptors[x].cFileName));
|
||||||
|
alloc += WideCharToMultiByte(CP_UTF8, 0, descriptors[x].cFileName, (int)curLen, NULL, 0,
|
||||||
|
NULL, NULL);
|
||||||
|
alloc += decoration_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Append a prefix file:// and postfix \r\n for each file */
|
/* Append a prefix file:// and postfix \n for each file */
|
||||||
alloc += (sizeof("/\r\n") + baseLength) * count;
|
/* We need to keep last \n since snprintf is null terminated!! */
|
||||||
|
alloc++;
|
||||||
dst = calloc(alloc, sizeof(char));
|
dst = calloc(alloc, sizeof(char));
|
||||||
|
|
||||||
if (!dst)
|
if (!dst)
|
||||||
@ -633,20 +644,18 @@ static void* convert_filedescriptors_to_uri_list(wClipboard* clipboard, UINT32 f
|
|||||||
|
|
||||||
for (x = 0; x < count; x++)
|
for (x = 0; x < count; x++)
|
||||||
{
|
{
|
||||||
|
if (_wcschr(descriptors[x].cFileName, L'\\') != NULL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
int rc;
|
int rc;
|
||||||
const FILEDESCRIPTORW* cur = &descriptors[x];
|
const FILEDESCRIPTORW* cur = &descriptors[x];
|
||||||
size_t curLen = _wcsnlen(cur->cFileName, ARRAYSIZE(cur->cFileName));
|
size_t curLen = _wcsnlen(cur->cFileName, ARRAYSIZE(cur->cFileName));
|
||||||
char* curName = NULL;
|
char* curName = NULL;
|
||||||
rc = ConvertFromUnicode(CP_UTF8, 0, cur->cFileName, (int)curLen, &curName, 0, NULL, NULL);
|
rc = ConvertFromUnicode(CP_UTF8, 0, cur->cFileName, (int)curLen, &curName, 0, NULL, NULL);
|
||||||
|
|
||||||
if (rc != (int)curLen)
|
rc = _snprintf(&dst[pos], alloc - pos, "file://%s/%s\n", clipboard->delegate.basePath,
|
||||||
{
|
curName);
|
||||||
free(curName);
|
|
||||||
free(dst);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = _snprintf(&dst[pos], alloc - pos, "%s/%s\r\n", clipboard->delegate.basePath, curName);
|
|
||||||
free(curName);
|
free(curName);
|
||||||
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
@ -663,15 +672,104 @@ static void* convert_filedescriptors_to_uri_list(wClipboard* clipboard, UINT32 f
|
|||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Prepend header of common gnome format to file list*/
|
||||||
|
static void* convert_filedescriptors_to_gnome_copied_files(wClipboard* clipboard, UINT32 formatId,
|
||||||
|
const void* data, UINT32* pSize)
|
||||||
|
{
|
||||||
|
void* pDstData = convert_filedescriptors_to_uri_list(clipboard, formatId, data, pSize);
|
||||||
|
if (!pDstData)
|
||||||
|
{
|
||||||
|
return pDstData;
|
||||||
|
}
|
||||||
|
const char* header = "copy\n";
|
||||||
|
size_t header_len = strlen(header);
|
||||||
|
*pSize = *pSize + header_len;
|
||||||
|
char* pNewDstData = calloc(*pSize, sizeof(char));
|
||||||
|
_snprintf(pNewDstData, *pSize, "%s%s", header, (char*)pDstData);
|
||||||
|
free(pDstData);
|
||||||
|
return pNewDstData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepend header of nautilus based filemanager's format to file list*/
|
||||||
|
static void* convert_filedescriptors_to_nautilus_clipboard(wClipboard* clipboard, UINT32 formatId,
|
||||||
|
const void* data, UINT32* pSize)
|
||||||
|
{
|
||||||
|
void* pDstData = convert_filedescriptors_to_uri_list(clipboard, formatId, data, pSize);
|
||||||
|
if (!pDstData)
|
||||||
|
{
|
||||||
|
return pDstData;
|
||||||
|
}
|
||||||
|
const char* header = "x-special/nautilus-clipboard\ncopy\n";
|
||||||
|
size_t header_len = strlen(header);
|
||||||
|
/* Here Nemo (and Caja) have different behavior. They encounter error with the last \n . but
|
||||||
|
nautilus needs it. So user have to skip Nemo's error dialog to continue. Caja has different
|
||||||
|
TARGET , so it's easy to fix. see convert_filedescriptors_to_mate_copied_files
|
||||||
|
*/
|
||||||
|
/* see nautilus/src/nautilus-clipboard.c:convert_selection_data_to_str_list
|
||||||
|
see nemo/libnemo-private/nemo-clipboard.c:nemo_clipboard_get_uri_list_from_selection_data
|
||||||
|
*/
|
||||||
|
*pSize = *pSize + header_len;
|
||||||
|
char* pNewDstData = calloc(*pSize, sizeof(char));
|
||||||
|
_snprintf(pNewDstData, *pSize, "%s%s", header, (char*)pDstData);
|
||||||
|
free(pDstData);
|
||||||
|
return pNewDstData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* convert_filedescriptors_to_mate_copied_files(wClipboard* clipboard, UINT32 formatId,
|
||||||
|
const void* data, UINT32* pSize)
|
||||||
|
{
|
||||||
|
void* pDstData = convert_filedescriptors_to_uri_list(clipboard, formatId, data, pSize);
|
||||||
|
if (!pDstData)
|
||||||
|
{
|
||||||
|
return pDstData;
|
||||||
|
}
|
||||||
|
const char* header = "copy\n";
|
||||||
|
size_t header_len = strlen(header);
|
||||||
|
/* Drop last \n.
|
||||||
|
see
|
||||||
|
mate-desktop/caja/libcaja-private/caja-clipboard.c:caja_clipboard_get_uri_list_from_selection_data
|
||||||
|
*/
|
||||||
|
*pSize = *pSize + header_len - 1;
|
||||||
|
char* pNewDstData = calloc(*pSize, sizeof(char));
|
||||||
|
_snprintf(pNewDstData, *pSize, "%s%s", header, (char*)pDstData);
|
||||||
|
free(pDstData);
|
||||||
|
return pNewDstData;
|
||||||
|
}
|
||||||
|
|
||||||
static BOOL register_file_formats_and_synthesizers(wClipboard* clipboard)
|
static BOOL register_file_formats_and_synthesizers(wClipboard* clipboard)
|
||||||
{
|
{
|
||||||
wObject* obj;
|
wObject* obj;
|
||||||
UINT32 file_group_format_id;
|
UINT32 file_group_format_id;
|
||||||
UINT32 local_file_format_id;
|
UINT32 local_file_format_id;
|
||||||
|
UINT32 local_gnome_file_format_id;
|
||||||
|
UINT32 local_mate_file_format_id;
|
||||||
|
UINT32 local_nautilus_file_format_id;
|
||||||
file_group_format_id = ClipboardRegisterFormat(clipboard, "FileGroupDescriptorW");
|
file_group_format_id = ClipboardRegisterFormat(clipboard, "FileGroupDescriptorW");
|
||||||
local_file_format_id = ClipboardRegisterFormat(clipboard, "text/uri-list");
|
local_file_format_id = ClipboardRegisterFormat(clipboard, "text/uri-list");
|
||||||
|
|
||||||
if (!file_group_format_id || !local_file_format_id)
|
/*
|
||||||
|
1. Gnome Nautilus based file manager:
|
||||||
|
TARGET: UTF8_STRING
|
||||||
|
format: x-special/nautilus-clipboard\copy\n\file://path\n\0
|
||||||
|
2. Kde Dolpin:
|
||||||
|
TARGET: text/uri-list
|
||||||
|
format: file://path\n\0
|
||||||
|
3. Gnome others (Unity/XFCE):
|
||||||
|
TARGET: x-special/gnome-copied-files
|
||||||
|
format: copy\nfile://path\n\0
|
||||||
|
4. Mate Caja:
|
||||||
|
TARGET: x-special/mate-copied-files
|
||||||
|
format: copy\nfile://path\n
|
||||||
|
|
||||||
|
TODO: other file managers do not use previous targets and formats.
|
||||||
|
*/
|
||||||
|
|
||||||
|
local_gnome_file_format_id = ClipboardRegisterFormat(clipboard, "x-special/gnome-copied-files");
|
||||||
|
local_mate_file_format_id = ClipboardRegisterFormat(clipboard, "x-special/mate-copied-files");
|
||||||
|
local_nautilus_file_format_id = ClipboardRegisterFormat(clipboard, "UTF8_STRING");
|
||||||
|
|
||||||
|
if (!file_group_format_id || !local_file_format_id || !local_gnome_file_format_id ||
|
||||||
|
!local_mate_file_format_id || !local_nautilus_file_format_id)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
clipboard->localFiles = ArrayList_New(FALSE);
|
clipboard->localFiles = ArrayList_New(FALSE);
|
||||||
@ -692,6 +790,18 @@ static BOOL register_file_formats_and_synthesizers(wClipboard* clipboard)
|
|||||||
convert_filedescriptors_to_uri_list))
|
convert_filedescriptors_to_uri_list))
|
||||||
goto error_free_local_files;
|
goto error_free_local_files;
|
||||||
|
|
||||||
|
if (!ClipboardRegisterSynthesizer(clipboard, file_group_format_id, local_gnome_file_format_id,
|
||||||
|
convert_filedescriptors_to_gnome_copied_files))
|
||||||
|
goto error_free_local_files;
|
||||||
|
|
||||||
|
if (!ClipboardRegisterSynthesizer(clipboard, file_group_format_id, local_mate_file_format_id,
|
||||||
|
convert_filedescriptors_to_mate_copied_files))
|
||||||
|
goto error_free_local_files;
|
||||||
|
if (!ClipboardRegisterSynthesizer(clipboard, file_group_format_id,
|
||||||
|
local_nautilus_file_format_id,
|
||||||
|
convert_filedescriptors_to_nautilus_clipboard))
|
||||||
|
goto error_free_local_files;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
error_free_local_files:
|
error_free_local_files:
|
||||||
ArrayList_Free(clipboard->localFiles);
|
ArrayList_Free(clipboard->localFiles);
|
||||||
|
Loading…
Reference in New Issue
Block a user