From a90228241dffb29c947a168feb82c7407dfa52e2 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Wed, 24 Jul 2024 13:27:46 +0100 Subject: [PATCH] Remove hard-coded filename limit for clipboard file lists The limit of 256 characters for clipboard files is limiting for many Asian locales, particularly as '%xx' notation is used to communicate bytes with bit 7 set. --- sesman/chansrv/clipboard_file.c | 199 ++++++++++++++++++++------------ 1 file changed, 122 insertions(+), 77 deletions(-) diff --git a/sesman/chansrv/clipboard_file.c b/sesman/chansrv/clipboard_file.c index 5b428017..d6e02c58 100644 --- a/sesman/chansrv/clipboard_file.c +++ b/sesman/chansrv/clipboard_file.c @@ -26,6 +26,7 @@ #include #endif +#include #include #include #include @@ -52,8 +53,8 @@ extern char g_fuse_clipboard_path[]; struct cb_file_info { - char pathname[256]; - char filename[256]; + char *pathname; + char *filename; int flags; int size; tui64 time; @@ -82,6 +83,103 @@ timeval2wintime(struct timeval *tv) } #endif +/** + * Gets a useable filename from a file specification passed to us + * + * The passed-in specification may contain instances of RFC3986 encoded + * octets '%xx' where 'x' is a hex digit (e.g. %20 == ASCII SPACE). For + * UTF-8, there may be many of these (e.g. %E6%97%A5 maps to the U+65E5 + * Unicode character) + * + * The result must be free'd by the caller. + */ +static char * +decode_rfc3986(const char *rfc3986, int len) +{ + char *result = (char *)malloc(len + 1); + if (result != NULL) + { + int i = 0; + int j = 0; + /* Copy the passed-in filename so we can modify it */ + while (i < len) + { + /* Check for %xx for a character (e.g. %20 == ASCII 32 == SPACE) */ + if (rfc3986[i] == '%' && (len - i) > 2 && + isxdigit(rfc3986[i + 1]) && isxdigit(rfc3986[i + 2])) + { + char jchr[] = { rfc3986[i + 1], rfc3986[i + 2], '\0' }; + result[j++] = g_htoi(jchr); + i += 3; + } + else + { + result[j++] = rfc3986[i++]; + } + } + result[j] = '\0'; + } + + return result; +} + +/** + * Allocates a alloc_cb_file_info struct + * + * The memory for the struct is allocated in such a way that a single + * free() call can be used to de-allocate it + * + * Filename elements are copied into the struct + */ +static struct cb_file_info * +alloc_cb_file_info(const char *full_name) +{ + struct cb_file_info *result = NULL; + + /* Find the last path separator in the string */ + const char *psep = strrchr(full_name, '/'); + + /* Separate the name into a path and an unqualified name */ + const char *path_ptr = "/"; + unsigned int path_len = 1; + const char *name_ptr; + + if (psep == NULL) + { + name_ptr = full_name; + } + else if (psep == full_name) + { + name_ptr = full_name + 1; + } + else + { + path_ptr = full_name; + path_len = psep - full_name; + name_ptr = psep + 1; + } + + /* Allocate a block big enough for the struct, and + * for both the strings */ + unsigned int name_len = strlen(name_ptr); + unsigned int alloc_size = sizeof(struct cb_file_info) + + (path_len + 1) + (name_len + 1); + + result = (struct cb_file_info *)malloc(alloc_size); + if (result != NULL) + { + /* Get a pointer to the first byte past the struct */ + result->pathname = (char *)(result + 1); + result->filename = result->pathname + path_len + 1; + memcpy(result->pathname, path_ptr, path_len); + result->pathname[path_len] = '\0'; + memcpy(result->filename, name_ptr, name_len); + result->filename[name_len] = '\0'; + } + + return result; +} + /*** * See MS-RDPECLIP 3.1.5.4.7 * @@ -113,54 +211,13 @@ clipboard_send_filecontents_response_fail(int streamId) return rv; } -/*****************************************************************************/ -/* this will replace %20 or any hex with the space or correct char - * returns error */ -static int -clipboard_check_file(char *filename) -{ - char lfilename[256]; - char jchr[8]; - int lindex; - int index; - - g_memset(lfilename, 0, 256); - lindex = 0; - index = 0; - while (filename[index] != 0) - { - if (filename[index] == '%') - { - jchr[0] = filename[index + 1]; - jchr[1] = filename[index + 2]; - jchr[2] = 0; - index += 3; - lfilename[lindex] = g_htoi(jchr); - lindex++; - } - else - { - lfilename[lindex] = filename[index]; - lindex++; - index++; - } - } - LOG_DEVEL(LOG_LEVEL_DEBUG, "[%s] [%s]", filename, lfilename); - g_strcpy(filename, lfilename); - return 0; -} - /*****************************************************************************/ static int clipboard_get_file(const char *file, int bytes) { - int sindex; - int pindex; - int flags; - char full_fn[256]; /* /etc/xrdp/xrdp.ini */ - char filename[256]; /* xrdp.ini */ - char pathname[256]; /* /etc/xrdp */ + char *full_fn; struct cb_file_info *cfi; + int result = 1; /* x-special/gnome-copied-files */ if ((g_strncmp(file, "copy", 4) == 0) && (bytes == 4)) @@ -171,35 +228,23 @@ clipboard_get_file(const char *file, int bytes) { return 0; } - sindex = 0; - flags = CB_FILE_ATTRIBUTE_ARCHIVE; + /* text/uri-list */ /* x-special/gnome-copied-files */ - if (g_strncmp(file, "file://", 7) == 0) + if (bytes > 7 && g_strncmp(file, "file://", 7) == 0) { - sindex = 7; + full_fn = decode_rfc3986(file + 7, bytes - 7); } - pindex = bytes; - while (pindex > sindex) + else { - if (file[pindex] == '/') - { - break; - } - pindex--; + full_fn = decode_rfc3986(file, bytes); } - g_memset(pathname, 0, 256); - g_memset(filename, 0, 256); - g_memcpy(pathname, file + sindex, pindex - sindex); - if (pathname[0] == 0) + + if (full_fn == NULL) { - pathname[0] = '/'; + LOG(LOG_LEVEL_ERROR, "clipboard_get_file: Out of memory"); + return 1; } - g_memcpy(filename, file + pindex + 1, (bytes - 1) - pindex); - /* this should replace %20 with space */ - clipboard_check_file(pathname); - clipboard_check_file(filename); - g_snprintf(full_fn, 255, "%s/%s", pathname, filename); /* * Before we look at the file, see if it's in the FUSE filesystem. If it is, @@ -209,34 +254,34 @@ clipboard_get_file(const char *file, int bytes) { LOG(LOG_LEVEL_ERROR, "clipboard_get_file: Can't add client-side file " "%s to clipboard", full_fn); - return 1; } - if (g_directory_exist(full_fn)) + else if (g_directory_exist(full_fn)) { LOG(LOG_LEVEL_ERROR, "clipboard_get_file: file [%s] is a directory, " "not supported", full_fn); - flags |= CB_FILE_ATTRIBUTE_DIRECTORY; - return 1; } - if (!g_file_exist(full_fn)) + else if (!g_file_exist(full_fn)) { LOG(LOG_LEVEL_ERROR, "clipboard_get_file: file [%s] does not exist", full_fn); - return 1; + } + else if ((cfi = alloc_cb_file_info(full_fn)) == NULL) + { + LOG(LOG_LEVEL_ERROR, "clipboard_get_file: Out of memory"); } else { - cfi = (struct cb_file_info *)g_malloc(sizeof(struct cb_file_info), 1); list_add_item(g_files_list, (tintptr)cfi); - g_strcpy(cfi->filename, filename); - g_strcpy(cfi->pathname, pathname); cfi->size = g_file_get_size(full_fn); - cfi->flags = flags; + cfi->flags = CB_FILE_ATTRIBUTE_ARCHIVE; cfi->time = (g_time1() + CB_EPOCH_DIFF) * 10000000LL; LOG_DEVEL(LOG_LEVEL_DEBUG, "ok filename [%s] pathname [%s] size [%d]", cfi->filename, cfi->pathname, cfi->size); + result = 0; } - return 0; + + free(full_fn); + return result; } /*****************************************************************************/