cliprdr: Fix an issue with file format filtering on Windows

This PR fixes an issue caused by clipboard format filtering which
discarded all formats but `FileGroupDescriptorW` to enable clipboard
file transfer. However at least on windows we also need `FileContents`
to be placed in the clipboard to make file transfer work correctly.

The PR also unifies list filtering into a single functions instead of
having two different functions.
This commit is contained in:
Martin Fleisz 2023-06-14 14:44:13 +02:00 committed by akallabeth
parent 3fd78adaed
commit 3b9a19e993
4 changed files with 56 additions and 106 deletions

View File

@ -35,68 +35,71 @@
#include "cliprdr_format.h"
#include "../cliprdr_common.h"
static BOOL cliprdr_filter_server_format_list(CLIPRDR_FORMAT_LIST* list, const UINT32 mask)
CLIPRDR_FORMAT_LIST cliprdr_filter_format_list(const CLIPRDR_FORMAT_LIST* list, const UINT32 mask,
const UINT32 checkMask)
{
const UINT32 all = CLIPRDR_FLAG_REMOTE_TO_LOCAL | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES;
const UINT32 maskData =
checkMask & (CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_REMOTE_TO_LOCAL);
const UINT32 maskFiles =
checkMask & (CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES);
WINPR_ASSERT(list);
if ((mask & all) == all)
return TRUE;
CLIPRDR_FORMAT_LIST filtered = { 0 };
filtered.common.msgType = CB_FORMAT_LIST;
filtered.numFormats = list->numFormats;
filtered.formats = calloc(filtered.numFormats, sizeof(CLIPRDR_FORMAT_LIST));
if ((mask & CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES) != 0)
size_t wpos = 0;
if ((mask & checkMask) == checkMask)
{
const CLIPRDR_FORMAT* files = NULL;
for (size_t x = 0; x < list->numFormats; x++)
{
CLIPRDR_FORMAT* format = &list->formats[x];
const CLIPRDR_FORMAT* format = &list->formats[x];
CLIPRDR_FORMAT* cur = &filtered.formats[x];
cur->formatId = format->formatId;
if (format->formatName)
cur->formatName = _strdup(format->formatName);
wpos++;
}
}
else if ((mask & maskFiles) != 0)
{
for (size_t x = 0; x < list->numFormats; x++)
{
const CLIPRDR_FORMAT* format = &list->formats[x];
CLIPRDR_FORMAT* cur = &filtered.formats[wpos];
if (!format->formatName)
continue;
if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0)
files = format;
else
if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0 ||
strcmp(format->formatName, type_FileContents) == 0)
{
free(format->formatName);
format->formatName = NULL;
cur->formatId = format->formatId;
cur->formatName = _strdup(format->formatName);
wpos++;
}
}
if (!files)
list->numFormats = 0;
else
}
else if ((mask & maskData) != 0)
{
list->numFormats = 1;
list->formats[0] = *files;
}
return TRUE;
}
if ((mask & CLIPRDR_FLAG_REMOTE_TO_LOCAL) != 0)
{
BOOL move = FALSE;
for (size_t x = 0; x < list->numFormats; x++)
{
CLIPRDR_FORMAT* format = &list->formats[x];
const CLIPRDR_FORMAT* format = &list->formats[x];
CLIPRDR_FORMAT* cur = &filtered.formats[wpos];
if (move)
if (!format->formatName ||
(strcmp(format->formatName, type_FileGroupDescriptorW) != 0 &&
strcmp(format->formatName, type_FileContents) != 0))
{
CLIPRDR_FORMAT* last = &list->formats[x - 1];
*last = *format;
}
else if (!format->formatName)
continue;
else if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0)
{
move = TRUE;
free(format->formatName);
format->formatName = NULL;
cur->formatId = format->formatId;
if (format->formatName)
cur->formatName = _strdup(format->formatName);
wpos++;
}
}
if (move)
list->numFormats -= 1;
return TRUE;
}
return FALSE;
filtered.numFormats = wpos;
return filtered;
}
/**
@ -108,6 +111,7 @@ UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
UINT16 msgFlags)
{
CLIPRDR_FORMAT_LIST formatList = { 0 };
CLIPRDR_FORMAT_LIST filteredFormatList = { 0 };
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
UINT error = CHANNEL_RC_OK;
@ -120,19 +124,22 @@ UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
const UINT32 mask =
freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask);
if (!cliprdr_filter_server_format_list(&formatList, mask))
filteredFormatList = cliprdr_filter_format_list(
&formatList, mask, CLIPRDR_FLAG_REMOTE_TO_LOCAL | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES);
if (filteredFormatList.numFormats == 0)
goto error_out;
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatList: numFormats: %" PRIu32 "",
formatList.numFormats);
filteredFormatList.numFormats);
if (context->ServerFormatList)
{
if ((error = context->ServerFormatList(context, &formatList)))
if ((error = context->ServerFormatList(context, &filteredFormatList)))
WLog_ERR(TAG, "ServerFormatList failed with error %" PRIu32 "", error);
}
error_out:
cliprdr_free_format_list(&filteredFormatList);
cliprdr_free_format_list(&formatList);
return error;
}

View File

@ -31,5 +31,7 @@ UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UIN
UINT16 msgFlags);
UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
UINT16 msgFlags);
CLIPRDR_FORMAT_LIST cliprdr_filter_format_list(const CLIPRDR_FORMAT_LIST* list, const UINT32 mask,
const UINT32 checkMask);
#endif /* FREERDP_CHANNEL_CLIPRDR_CLIENT_FORMAT_H */

View File

@ -39,6 +39,7 @@
#include "../cliprdr_common.h"
const char* type_FileGroupDescriptorW = "FileGroupDescriptorW";
const char* type_FileContents = "FileContents";
static const char* CB_MSG_TYPE_STRINGS(UINT32 type)
{
@ -660,68 +661,6 @@ static UINT cliprdr_temp_directory(CliprdrClientContext* context,
return cliprdr_packet_send(cliprdr, s);
}
static CLIPRDR_FORMAT_LIST cliprdr_filter_local_format_list(const CLIPRDR_FORMAT_LIST* list,
const UINT32 mask)
{
const UINT32 all = CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES;
WINPR_ASSERT(list);
CLIPRDR_FORMAT_LIST filtered = { 0 };
filtered.common.msgType = CB_FORMAT_LIST;
filtered.numFormats = list->numFormats;
filtered.formats = calloc(filtered.numFormats, sizeof(CLIPRDR_FORMAT_LIST));
size_t wpos = 0;
if ((mask & all) == all)
{
for (size_t x = 0; x < list->numFormats; x++)
{
const CLIPRDR_FORMAT* format = &list->formats[x];
CLIPRDR_FORMAT* cur = &filtered.formats[x];
cur->formatId = format->formatId;
if (format->formatName)
cur->formatName = _strdup(format->formatName);
wpos++;
}
}
else if ((mask & CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES) != 0)
{
for (size_t x = 0; x < list->numFormats; x++)
{
const CLIPRDR_FORMAT* format = &list->formats[x];
CLIPRDR_FORMAT* cur = &filtered.formats[wpos];
if (!format->formatName)
continue;
if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0)
{
cur->formatId = format->formatId;
cur->formatName = _strdup(format->formatName);
wpos++;
break;
}
}
}
else if ((mask & CLIPRDR_FLAG_LOCAL_TO_REMOTE) != 0)
{
for (size_t x = 0; x < list->numFormats; x++)
{
const CLIPRDR_FORMAT* format = &list->formats[x];
CLIPRDR_FORMAT* cur = &filtered.formats[wpos];
if (!format->formatName || (strcmp(format->formatName, type_FileGroupDescriptorW) != 0))
{
cur->formatId = format->formatId;
if (format->formatName)
cur->formatName = _strdup(format->formatName);
wpos++;
}
}
}
filtered.numFormats = wpos;
return filtered;
}
/**
* Function description
*
@ -741,7 +680,8 @@ static UINT cliprdr_client_format_list(CliprdrClientContext* context,
const UINT32 mask =
freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask);
CLIPRDR_FORMAT_LIST filterList = cliprdr_filter_local_format_list(formatList, mask);
CLIPRDR_FORMAT_LIST filterList = cliprdr_filter_format_list(
formatList, mask, CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES);
/* Allow initial format list from monitor ready, but ignore later attempts */
if ((filterList.numFormats == 0) && cliprdr->initialFormatListSent)

View File

@ -56,5 +56,6 @@ CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr);
UINT cliprdr_send_error_response(cliprdrPlugin* cliprdr, UINT16 type);
extern const char* type_FileGroupDescriptorW;
extern const char* type_FileContents;
#endif /* FREERDP_CHANNEL_CLIPRDR_CLIENT_MAIN_H */