Skip to content

Commit

Permalink
Remove hard-coded filename limit for clipboard file lists
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
matt335672 committed Jul 25, 2024
1 parent 69c094b commit e64277f
Showing 1 changed file with 122 additions and 77 deletions.
199 changes: 122 additions & 77 deletions sesman/chansrv/clipboard_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <config_ac.h>
#endif

#include <ctype.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
Expand All @@ -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;
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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))
Expand All @@ -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,
Expand All @@ -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;
}

/*****************************************************************************/
Expand Down

0 comments on commit e64277f

Please sign in to comment.