Merge pull request #1996 from matt335672/investigate_paste_hang

Some copy-paste tidyups + Nautilus 3 compatibility (#1996)
This commit is contained in:
matt335672 2021-09-16 10:18:52 +01:00 committed by GitHub
commit 85518a8157
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 210 additions and 53 deletions

View File

@ -290,6 +290,14 @@ features:-
.P
- copying-and-pasting of files
.RE
.TP
\fBUseNautilus3FlistFormat\fR=\fI[false|true]\fR
Defaults to \fIfalse\fR.
Set to \fItrue\fR to make file copy-paste compatible with Nautilus from
GNOME 3 versions later than 3.29.92. Do not use this for any other reason.
This setting will be removed in a later version of xrdp, when GNOME 3 is
no longer supported.
.SH "SESSIONS VARIABLES"
All entries in the \fB[SessionVariables]\fR section are set as

View File

@ -39,7 +39,7 @@
#define DEFAULT_ENABLE_FUSE_MOUNT 1
#define DEFAULT_FUSE_MOUNT_NAME "xrdp-client"
#define DEFAULT_FILE_UMASK 077
#define DEFAULT_USE_NAUTILUS3_FLIST_FORMAT 0
/**
* Type used for passing a logging function about
*/
@ -161,7 +161,7 @@ read_config_chansrv(log_func_t logmsg,
{
cfg->enable_fuse_mount = g_text2bool(value);
}
if (g_strcasecmp(name, "FuseMountName") == 0)
else if (g_strcasecmp(name, "FuseMountName") == 0)
{
g_free(cfg->fuse_mount_name);
cfg->fuse_mount_name = g_strdup(value);
@ -176,6 +176,10 @@ read_config_chansrv(log_func_t logmsg,
{
cfg->file_umask = strtol(value, NULL, 0);
}
else if (g_strcasecmp(name, "UseNautilus3FlistFormat") == 0)
{
cfg->use_nautilus3_flist_format = g_text2bool(value);
}
}
return error;
@ -206,6 +210,7 @@ new_config(void)
cfg->restrict_outbound_clipboard = DEFAULT_RESTRICT_OUTBOUND_CLIPBOARD;
cfg->fuse_mount_name = fuse_mount_name;
cfg->file_umask = DEFAULT_FILE_UMASK;
cfg->use_nautilus3_flist_format = DEFAULT_USE_NAUTILUS3_FLIST_FORMAT;
}
return cfg;
@ -290,6 +295,8 @@ config_dump(struct config_chansrv *config)
g_bool2text(config->enable_fuse_mount));
g_writeln(" FuseMountName: %s", config->fuse_mount_name);
g_writeln(" FileMask: 0%o", config->file_umask);
g_writeln(" Nautilus 3 Flist Format: %s",
g_bool2text(config->use_nautilus3_flist_format));
}
/******************************************************************************/

View File

@ -36,6 +36,9 @@ struct config_chansrv
char *fuse_mount_name;
/** FileUmask from sesman.ini */
mode_t file_umask;
/** Whether to use nautilus3-compatible file lists for the clipboard */
int use_nautilus3_flist_format;
};

View File

@ -1199,6 +1199,95 @@ clipboard_process_data_response_for_image(struct stream *s,
return 0;
}
/**************************************************************************//**
* Process a CB_FORMAT_DATA_RESPONSE for an X client requesting a file list
*
* @param s Stream containing CLIPRDR_FILELIST ([MS-RDPECLIP])
* @param clip_msg_status msgFlags from Clipboard PDU Header
* @param clip_msg_len dataLen from Clipboard PDU Header
*
* @return Status
*/
static int
clipboard_process_data_response_for_file(struct stream *s,
int clip_msg_status,
int clip_msg_len)
{
XSelectionRequestEvent *lxev;
int rv = 0;
LOG_DEVEL(LOG_LEVEL_TRACE, "clipboard_process_data_response_for_file: ");
lxev = &g_saved_selection_req_event;
const int flist_size = 1024 * 1024;
g_free(g_clip_c2s.data);
g_clip_c2s.data = (char *)g_malloc(flist_size, 0);
if (g_clip_c2s.data == NULL)
{
LOG(LOG_LEVEL_ERROR, "clipboard_process_data_response_for_file: "
"Can't allocate memory");
rv = 1;
}
/* text/uri-list */
else if (g_clip_c2s.type == g_file_atom1)
{
rv = clipboard_c2s_in_files(s, g_clip_c2s.data, flist_size,
"file://");
}
/* x-special/gnome-copied-files */
else if (g_clip_c2s.type == g_file_atom2)
{
g_strcpy(g_clip_c2s.data, "copy\n");
rv = clipboard_c2s_in_files(s, g_clip_c2s.data + 5, flist_size - 5,
"file://");
}
else if ((g_clip_c2s.type == XA_STRING) ||
(g_clip_c2s.type == g_utf8_atom))
{
if (g_cfg->use_nautilus3_flist_format)
{
/*
* This file list format is only used by GNOME 3
* versions >= 3.29.92. It is not used by GNOME 4. Remove
* this workaround when GNOME 3 is no longer supported by
* long-term distros */
#define LIST_PREFIX "x-special/nautilus-clipboard\ncopy\n"
#define LIST_PREFIX_LEN (sizeof(LIST_PREFIX) - 1)
g_strcpy(g_clip_c2s.data, LIST_PREFIX);
rv = clipboard_c2s_in_files(s,
g_clip_c2s.data + LIST_PREFIX_LEN,
flist_size - LIST_PREFIX_LEN - 1,
"file://");
g_strcat(g_clip_c2s.data, "\n");
#undef LIST_PREFIX_LEN
#undef LIST_PREFIX
}
else
{
rv = clipboard_c2s_in_files(s, g_clip_c2s.data, flist_size, "");
}
}
else
{
LOG_DEVEL(LOG_LEVEL_ERROR,
"clipboard_process_data_response_for_file: "
"Unrecognised target");
rv = 1;
}
if (rv != 0 && g_clip_c2s.data != NULL)
{
g_clip_c2s.data[0] = '\0';
}
g_clip_c2s.total_bytes =
(g_clip_c2s.data == NULL) ? 0 : g_strlen(g_clip_c2s.data);
g_clip_c2s.read_bytes_done = g_clip_c2s.total_bytes;
clipboard_provide_selection_c2s(lxev, lxev->target);
return rv;
}
/*****************************************************************************/
/* client to server */
/* sent as a reply to CB_FORMAT_DATA_REQUEST; used to indicate whether
@ -1227,26 +1316,8 @@ clipboard_process_data_response(struct stream *s, int clip_msg_status,
}
if (g_clip_c2s.xrdp_clip_type == XRDP_CB_FILE)
{
g_free(g_clip_c2s.data);
g_clip_c2s.data = (char *)g_malloc(1024 * 1024, 1);
/* text/uri-list */
if (g_clip_c2s.type == g_file_atom1)
{
clipboard_c2s_in_files(s, g_clip_c2s.data);
}
/* x-special/gnome-copied-files */
else if (g_clip_c2s.type == g_file_atom2)
{
g_strcpy(g_clip_c2s.data, "copy\n");
clipboard_c2s_in_files(s, g_clip_c2s.data + 5);
}
else
{
LOG_DEVEL(LOG_LEVEL_ERROR, "clipboard_process_data_response: error");
}
g_clip_c2s.total_bytes = g_strlen(g_clip_c2s.data);
g_clip_c2s.read_bytes_done = g_clip_c2s.total_bytes;
clipboard_provide_selection_c2s(lxev, lxev->target);
clipboard_process_data_response_for_file(s, clip_msg_status,
clip_msg_len);
return 0;
}
LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_process_data_response: "
@ -2168,11 +2239,24 @@ clipboard_event_selection_request(XEvent *xevent)
}
else if ((lxev->target == XA_STRING) || (lxev->target == g_utf8_atom))
{
g_memcpy(&g_saved_selection_req_event, lxev,
sizeof(g_saved_selection_req_event));
g_clip_c2s.type = lxev->target;
g_clip_c2s.xrdp_clip_type = XRDP_CB_TEXT;
clipboard_send_data_request(CB_FORMAT_UNICODETEXT);
if (clipboard_find_format_id(g_file_format_id) >= 0)
{
LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_event_selection_request: "
"text requested when files available");
g_memcpy(&g_saved_selection_req_event, lxev,
sizeof(g_saved_selection_req_event));
g_clip_c2s.type = lxev->target;
g_clip_c2s.xrdp_clip_type = XRDP_CB_FILE;
clipboard_send_data_request(g_file_format_id);
}
else
{
g_memcpy(&g_saved_selection_req_event, lxev,
sizeof(g_saved_selection_req_event));
g_clip_c2s.type = lxev->target;
g_clip_c2s.xrdp_clip_type = XRDP_CB_TEXT;
clipboard_send_data_request(CB_FORMAT_UNICODETEXT);
}
return 0;
}
else if (lxev->target == g_image_bmp_atom)

View File

@ -597,33 +597,41 @@ clipboard_c2s_in_file_info(struct stream *s, struct clip_file_desc *cfd)
}
/*****************************************************************************/
/* See [MS-RDPECLIP] 2.2.5.2.3 */
int
clipboard_c2s_in_files(struct stream *s, char *file_list)
clipboard_c2s_in_files(struct stream *s, char *file_list, int file_list_size,
const char *fprefix)
{
int cItems;
int citems;
int lindex;
int str_len;
int file_count;
struct clip_file_desc cfd;
char *ptr;
char *last; /* Last writeable char in buffer */
int dropped_files = 0; /* # files we can't add to buffer */
if (!s_check_rem(s, 4))
if (file_list_size < 1)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "clipboard_c2s_in_files: parse error");
LOG(LOG_LEVEL_ERROR, "clipboard_c2s_in_files: No space in string");
return 1;
}
in_uint32_le(s, cItems);
if (cItems > 64 * 1024) /* sanity check */
if (!s_check_rem(s, 4))
{
LOG_DEVEL(LOG_LEVEL_ERROR, "clipboard_c2s_in_files: error cItems %d too big", cItems);
LOG(LOG_LEVEL_ERROR, "clipboard_c2s_in_files: parse error");
return 1;
}
in_uint32_le(s, citems);
if (citems < 0 || citems > 64 * 1024) /* sanity check */
{
LOG(LOG_LEVEL_ERROR, "clipboard_c2s_in_files: "
"Bad number of files in list (%d)", citems);
return 1;
}
xfuse_clear_clip_dir();
LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_c2s_in_files: cItems %d", cItems);
file_count = 0;
LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_c2s_in_files: cItems %d", citems);
ptr = file_list;
for (lindex = 0; lindex < cItems; lindex++)
last = file_list + file_list_size - 1;
for (lindex = 0; lindex < citems; lindex++)
{
g_memset(&cfd, 0, sizeof(struct clip_file_desc));
if (clipboard_c2s_in_file_info(s, &cfd) != 0)
@ -633,37 +641,62 @@ clipboard_c2s_in_files(struct stream *s, char *file_list)
if ((g_pos(cfd.cFileName, "\\") >= 0) ||
(cfd.fileAttributes & CB_FILE_ATTRIBUTE_DIRECTORY))
{
LOG_DEVEL(LOG_LEVEL_ERROR, "clipboard_c2s_in_files: skipping directory not "
"supported [%s]", cfd.cFileName);
LOG(LOG_LEVEL_WARNING, "clipboard_c2s_in_files: skipping "
"directory not supported [%s]", cfd.cFileName);
continue;
}
/* Have we already run out of room in the list? */
if (dropped_files > 0)
{
dropped_files += 1;
continue;
}
/* Room for this file? */
str_len = (ptr == file_list) ? 0 : 1; /* Delimiter */
str_len += g_strlen(fprefix); /* e.g. "file://" */
str_len += g_strlen(g_fuse_clipboard_path);
str_len += 1; /* '/' */
str_len += g_strlen(cfd.cFileName);
if (str_len > (last - ptr))
{
dropped_files += 1;
continue;
}
if (xfuse_add_clip_dir_item(cfd.cFileName, 0, cfd.fileSizeLow, lindex) == -1)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "clipboard_c2s_in_files: failed to add clip dir item");
LOG(LOG_LEVEL_WARNING, "clipboard_c2s_in_files: "
"failed to add clip dir item %s", cfd.cFileName);
continue;
}
if (file_count > 0)
if (ptr > file_list)
{
*ptr = '\n';
ptr++;
*ptr++ = '\n';
}
file_count++;
g_strcpy(ptr, "file://");
ptr += 7;
str_len = g_strlen(fprefix);
g_strcpy(ptr, fprefix);
ptr += str_len;
str_len = g_strlen(g_fuse_clipboard_path);
g_strcpy(ptr, g_fuse_clipboard_path);
ptr += str_len;
*ptr = '/';
ptr++;
*ptr++ = '/';
str_len = g_strlen(cfd.cFileName);
g_strcpy(ptr, cfd.cFileName);
ptr += str_len;
}
*ptr = 0;
*ptr = '\0';
if (dropped_files > 0)
{
LOG(LOG_LEVEL_WARNING, "clipboard_c2s_in_files: "
"Dropped %d files from the clip buffer due to insufficient space",
dropped_files);
}
return 0;
}

View File

@ -30,8 +30,26 @@ clipboard_process_file_request(struct stream *s, int clip_msg_status,
int
clipboard_process_file_response(struct stream *s, int clip_msg_status,
int clip_msg_len);
/**
* Process a CLIPRDR_FILELIST - see [MS-RDPECLIP] 2.2.5.2.3
*
* Files in the list are added to the xfs filesystem in the clipboard
* directory. The filenames names are added to the 'file_list' for the user.
* Files are prefixed with '<fprefix><clipboard_dir>/' and separated by '\n'.
*
* If the list is not big enough, whole filenames are omitted, and a warning
* message is logged. This is not an error.
*
* @param s Input stream containing CLIPRDR_FILELIST
* @param file_list Output buffer for filenames
* @param file_list_size Size of buffer, including space for '\0'.
* @param fprefix Prefix for each file in the file list (e.g. "file://")
*
* @return Zero for success.
*/
int
clipboard_c2s_in_files(struct stream *s, char *file_list);
clipboard_c2s_in_files(struct stream *s, char *file_list, int file_list_size,
const char *fprefix);
int
clipboard_request_file_size(int stream_id, int lindex);

View File

@ -125,6 +125,10 @@ FuseMountName=thinclient_drives
FileUmask=077
; Can be used to disable FUSE functionality - see sesman.ini(5)
#EnableFuseMount=false
; Uncomment this line only if you are using GNOME 3 versions 3.29.92
; and up, and you wish to cut-paste files between Nautilus and Windows. Do
; not use this setting for GNOME 4, or other file managers
#UseNautilus3FlistFormat=true
[ChansrvLogging]
; Note: one log file is created per display and the LogFile config value