x11: add support for remote to local clipboard file copy

This commit is contained in:
jackyzy823 2020-12-27 14:08:35 +08:00 committed by akallabeth
parent 6a2ae388f8
commit d537988f28
5 changed files with 824 additions and 17 deletions

View File

@ -226,6 +226,10 @@ if(WITH_XFIXES)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${XFIXES_LIBRARIES})
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)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client freerdp m)

View File

@ -31,10 +31,18 @@
#include <X11/extensions/Xfixes.h>
#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/image.h>
#include <winpr/stream.h>
#include <winpr/clipboard.h>
#include <winpr/path.h>
#include <freerdp/log.h>
#include <freerdp/client/cliprdr.h>
@ -55,6 +63,33 @@ struct xf_cliprdr_format
};
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
{
xfContext* xfc;
@ -111,6 +146,15 @@ struct xf_clipboard
/* File clipping */
BOOL streams_supported;
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);
@ -824,6 +868,29 @@ static void xf_cliprdr_clear_cached_data(xfClipboard* clipboard)
}
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,
@ -1157,6 +1224,66 @@ static UINT xf_cliprdr_send_client_format_list_response(xfClipboard* clipboard,
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
*
@ -1388,6 +1515,19 @@ xf_cliprdr_server_format_data_request(CliprdrClientContext* context,
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
*
@ -1403,6 +1543,7 @@ xf_cliprdr_server_format_data_response(CliprdrClientContext* context,
UINT32 SrcSize;
UINT32 srcFormatId;
UINT32 dstFormatId;
xfCliprdrFormat* dstTargetFormat;
BOOL nullTerminated = FALSE;
UINT32 size = formatDataResponse->dataLen;
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)
{
/* 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");
dstFormatId = ClipboardGetFormatId(clipboard->system, "text/uri-list");
nullTerminated = FALSE;
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;
}
}
else
@ -1663,6 +1928,334 @@ static UINT xf_cliprdr_clipboard_file_range_failure(wClipboardDelegate* delegate
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)
{
int i, n = 0;
@ -1735,6 +2328,8 @@ xfClipboard* xf_clipboard_new(xfContext* xfc)
n++;
clipboard->clientFormats[n].atom = XInternAtom(xfc->display, "UTF8_STRING", False);
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++;
clipboard->clientFormats[n].atom = XA_STRING;
clipboard->clientFormats[n].formatId = CF_TEXT;
@ -1778,6 +2373,33 @@ xfClipboard* xf_clipboard_new(xfContext* xfc)
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->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->delegate = ClipboardGetDelegate(clipboard->system);
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->ClipboardFileSizeFailure = xf_cliprdr_clipboard_file_size_failure;
clipboard->delegate->ClipboardFileRangeSuccess = xf_cliprdr_clipboard_file_range_success;
@ -1825,6 +2461,26 @@ void xf_clipboard_free(xfClipboard* clipboard)
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);
free(clipboard->data);
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->ServerFormatDataResponse = xf_cliprdr_server_format_data_response;
cliprdr->ServerFileContentsRequest = xf_cliprdr_server_file_contents_request;
cliprdr->ServerFileContentsResponse = xf_cliprdr_server_file_contents_response;
}
void xf_cliprdr_uninit(xfContext* xfc, CliprdrClientContext* cliprdr)

34
cmake/FindFUSE.cmake Normal file
View 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)

View File

@ -36,6 +36,8 @@
#define CB_FORMAT_JPEG 0xD012
#define CB_FORMAT_GIF 0xD013
#define CB_FORMAT_TEXTURILIST 0xD014
#define CB_FORMAT_GNOMECOPIEDFILES 0xD015
#define CB_FORMAT_MATECOPIEDFILES 0xD016
/* CLIPRDR_HEADER.msgType */
#define CB_MONITOR_READY 0x0001

View File

@ -589,6 +589,7 @@ static void* convert_filedescriptors_to_uri_list(wClipboard* clipboard, UINT32 f
size_t count, x, alloc, pos, baseLength = 0;
const char* src = (const char*)data;
char* dst;
size_t decoration_len;
if (!clipboard || !data || !pSize)
return NULL;
@ -616,14 +617,24 @@ static void* convert_filedescriptors_to_uri_list(wClipboard* clipboard, UINT32 f
if (formatId != ClipboardGetFormatId(clipboard, "FileGroupDescriptorW"))
return NULL;
decoration_len = strlen("file:///\n") + baseLength;
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++)
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 */
alloc += (sizeof("/\r\n") + baseLength) * count;
/* Append a prefix file:// and postfix \n for each file */
/* We need to keep last \n since snprintf is null terminated!! */
alloc++;
dst = calloc(alloc, sizeof(char));
if (!dst)
@ -633,20 +644,18 @@ static void* convert_filedescriptors_to_uri_list(wClipboard* clipboard, UINT32 f
for (x = 0; x < count; x++)
{
if (_wcschr(descriptors[x].cFileName, L'\\') != NULL)
{
continue;
}
int rc;
const FILEDESCRIPTORW* cur = &descriptors[x];
size_t curLen = _wcsnlen(cur->cFileName, ARRAYSIZE(cur->cFileName));
char* curName = NULL;
rc = ConvertFromUnicode(CP_UTF8, 0, cur->cFileName, (int)curLen, &curName, 0, NULL, NULL);
if (rc != (int)curLen)
{
free(curName);
free(dst);
return NULL;
}
rc = _snprintf(&dst[pos], alloc - pos, "%s/%s\r\n", clipboard->delegate.basePath, curName);
rc = _snprintf(&dst[pos], alloc - pos, "file://%s/%s\n", clipboard->delegate.basePath,
curName);
free(curName);
if (rc < 0)
@ -663,15 +672,104 @@ static void* convert_filedescriptors_to_uri_list(wClipboard* clipboard, UINT32 f
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)
{
wObject* obj;
UINT32 file_group_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");
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;
clipboard->localFiles = ArrayList_New(FALSE);
@ -692,6 +790,18 @@ static BOOL register_file_formats_and_synthesizers(wClipboard* clipboard)
convert_filedescriptors_to_uri_list))
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;
error_free_local_files:
ArrayList_Free(clipboard->localFiles);