summaryrefslogtreecommitdiff
path: root/src/rdp/rdp_clipboard.c
diff options
context:
space:
mode:
authorsss <sss@dark-alexandr.net>2023-01-17 00:38:19 +0300
committersss <sss@dark-alexandr.net>2023-01-17 00:38:19 +0300
commitcc3f33db7a8d3c4ad373e646b199808e01bc5d9b (patch)
treeec09d690c7656ab5f2cc72607e05fb359c24d8b2 /src/rdp/rdp_clipboard.c
added webrdp public code
Diffstat (limited to 'src/rdp/rdp_clipboard.c')
-rw-r--r--src/rdp/rdp_clipboard.c1524
1 files changed, 1524 insertions, 0 deletions
diff --git a/src/rdp/rdp_clipboard.c b/src/rdp/rdp_clipboard.c
new file mode 100644
index 0000000..e8ab882
--- /dev/null
+++ b/src/rdp/rdp_clipboard.c
@@ -0,0 +1,1524 @@
+/* BSD-2-Clause license
+ *
+ * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>.
+ *
+ */
+
+/* this code mostly ported from XFreeRDP */
+
+#include <arpa/inet.h>
+#include <iconv.h>
+
+#include "rdp_clipboard.h"
+#include "rdp_ft.h"
+
+static UINT
+rdp_clip_ServerCapabilities(
+ CliprdrClientContext *context, const CLIPRDR_CAPABILITIES *capabilities)
+{
+ UINT32 i;
+ const CLIPRDR_CAPABILITY_SET *caps;
+ const CLIPRDR_GENERAL_CAPABILITY_SET *generalCaps;
+ const BYTE *capsPtr = (const BYTE *)capabilities->capabilitySets;
+ my_rdp_clipboard *clipboard = (my_rdp_clipboard *)context->custom;
+ clipboard->streams_supported = false;
+
+ for (i = 0; i < capabilities->cCapabilitiesSets; i++)
+ {
+ caps = (const CLIPRDR_CAPABILITY_SET *)capsPtr;
+
+ if (caps->capabilitySetType == CB_CAPSTYPE_GENERAL)
+ {
+ generalCaps
+ = (const CLIPRDR_GENERAL_CAPABILITY_SET *)caps;
+
+ if (generalCaps->generalFlags
+ & CB_STREAM_FILECLIP_ENABLED)
+ {
+ clipboard->streams_supported = true;
+ }
+ }
+
+ capsPtr += caps->capabilitySetLength;
+ }
+ {
+ const char *msg
+ = "rdp_module: cliprdr: server capabilities received";
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_trace, 0);
+ }
+
+ return CHANNEL_RC_OK;
+}
+
+/* unused for now
+ * static UINT
+rdp_clip_ClientCapabilities(CliprdrClientContext* context,
+ CLIPRDR_CAPABILITIES* capabilities)
+{
+ return CHANNEL_RC_OK;
+}*/
+
+static UINT
+rdp_cliprdr_send_client_capabilities(my_rdp_clipboard *clipboard)
+{
+ CLIPRDR_CAPABILITIES capabilities;
+ CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
+ capabilities.cCapabilitiesSets = 1;
+ capabilities.capabilitySets
+ = (CLIPRDR_CAPABILITY_SET *)&(generalCapabilitySet);
+ generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
+ generalCapabilitySet.capabilitySetLength = 12;
+ generalCapabilitySet.version = CB_CAPS_VERSION_2;
+ generalCapabilitySet.generalFlags = CB_USE_LONG_FORMAT_NAMES;
+ /*|CB_CAN_LOCK_CLIPDATA; */
+
+ if (clipboard->streams_supported)
+ {
+ generalCapabilitySet.generalFlags |= CB_STREAM_FILECLIP_ENABLED
+ | CB_FILECLIP_NO_FILE_PATHS
+ | 0x00000020;
+ }
+
+ {
+ const char *msg
+ = "rdp_module: cliprdr: client capabilities sent";
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_trace, 0);
+ }
+
+ return clipboard->clip_context->ClientCapabilities(
+ clipboard->clip_context, &capabilities);
+}
+
+static UINT
+rdp_cliprdr_send_client_format_list(my_rdp_clipboard *clipboard)
+{
+ const uint8_t format_count = 2;
+ CLIPRDR_FORMAT formats[format_count];
+ CLIPRDR_FORMAT_LIST formatList;
+ memset(formats, 0, sizeof(CLIPRDR_FORMAT) * format_count);
+ formats[0].formatId = CF_RAW;
+ formats[1].formatId = CF_TEXT;
+ formatList.common.msgFlags = CB_RESPONSE_OK;
+ formatList.numFormats = format_count;
+ formatList.formats = formats;
+
+ {
+ const char *msg
+ = "rdp_module: cliprdr: client format list sent";
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_trace, 0);
+ }
+
+ return clipboard->clip_context->ClientFormatList(
+ clipboard->clip_context, &formatList);
+}
+
+static UINT
+rdp_clip_MonitorReady(
+ CliprdrClientContext *context, const CLIPRDR_MONITOR_READY *monitorReady)
+{
+ my_rdp_clipboard *clipboard = context->custom;
+ UINT ret;
+
+ if ((ret = rdp_cliprdr_send_client_capabilities(clipboard))
+ != CHANNEL_RC_OK)
+ return ret;
+
+ if ((ret = rdp_cliprdr_send_client_format_list(clipboard))
+ != CHANNEL_RC_OK)
+ return ret;
+
+ /* clipboard->sync = true; */
+ {
+ const char *msg
+ = "rdp_module: cliprdr: monitor ready message handled";
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_trace, 0);
+ }
+ return CHANNEL_RC_OK;
+}
+
+/* unused for now
+ * static UINT
+rdp_clip_TempDirectory(CliprdrClientContext* context,
+ CLIPRDR_TEMP_DIRECTORY* tempDirectory)
+{
+ return CHANNEL_RC_OK;
+} */
+
+/* unused for now
+ * static UINT
+rdp_clip_ClientFormatList(CliprdrClientContext* context,
+ CLIPRDR_FORMAT_LIST* formatList)
+{
+ return CHANNEL_RC_OK;
+}*/
+
+static UINT
+rdp_cliprdr_send_client_format_list_response(
+ my_rdp_clipboard *clipboard, BOOL status)
+{
+ CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse;
+ formatListResponse.common.msgType = CB_FORMAT_LIST_RESPONSE;
+ formatListResponse.common.msgFlags
+ = status ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
+ formatListResponse.common.dataLen = 0;
+ return clipboard->clip_context->ClientFormatListResponse(
+ clipboard->clip_context, &formatListResponse);
+}
+
+static wrdp_enum_clip_format
+clip_format_from_string(const char *fmt)
+{
+ if (!strcmp(fmt, "FileGroupDescriptorW"))
+ {
+ return clip_format_file_list;
+ }
+ /*
+ * unused
+ else if (!strcmp(fmt, "FileContents"))
+ {
+ }
+ else if (!strcmp(fmt, "Preffered DropEffect"))
+ {
+ }
+ */
+ return clip_format_unsupported;
+}
+
+static wrdp_enum_clip_format
+clip_format_from_id(UINT32 id, my_rdp_clipboard *c)
+{
+ if (id == CF_TEXT || id == CF_OEMTEXT)
+ {
+ return clip_format_text;
+ }
+ else if (id == CF_RAW)
+ {
+ return clip_format_raw;
+ }
+ else if (id == CF_UNICODETEXT)
+ {
+ return clip_format_unicode;
+ }
+ else if (id == CB_FORMAT_TEXTURILIST)
+ {
+ return clip_format_file_list;
+ }
+ return clip_format_unsupported;
+}
+
+static UINT
+rdp_clip_ServerFormatList(
+ CliprdrClientContext *context, const CLIPRDR_FORMAT_LIST *formatList)
+{
+ UINT32 i;
+ uint8_t num_supported_fmts = 0, *out_fmts = 0, num_server_formats;
+ my_rdp_clipboard *clipboard = context->custom;
+ my_clip_format *server_formats = 0;
+ UINT ret;
+
+ if (clipboard->my_rdp_context->ft_to_server->is_runing)
+ {
+ const char *msg
+ = "rdp_module: clipboard: new clipboard formats list "
+ "during runing filetransfer";
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_warning, 0);
+ /* return CHANNEL_RC_OK; */
+ }
+
+ num_server_formats = formatList->numFormats + 1; /* +1 for CF_RAW */
+
+ if (!(server_formats
+ = calloc(num_server_formats, sizeof(my_clip_format))))
+ {
+ char buf[128];
+ snprintf(buf, 127,
+ "failed to allocate %d my_clip_format structs",
+ num_server_formats);
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)buf, strlen(buf),
+ wrdp_log_level_error, 0);
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ if (clipboard->srv_fmts && clipboard->srv_fmts_count)
+ {
+ for (i = 0; i < clipboard->srv_fmts_count; ++i)
+ {
+ if (clipboard->srv_fmts[i].rdp_fmt.formatName)
+ {
+ free(clipboard->srv_fmts[i].rdp_fmt.formatName);
+ }
+ }
+ free(clipboard->srv_fmts);
+ }
+
+ clipboard->srv_fmts_count = formatList->numFormats;
+ for (i = 0; i < formatList->numFormats; i++)
+ {
+ CLIPRDR_FORMAT *format = &formatList->formats[i];
+ server_formats[i].rdp_fmt.formatId = format->formatId;
+ wrdp_enum_clip_format fmt
+ = clip_format_from_id(format->formatId, clipboard);
+ if (format->formatName)
+ {
+ server_formats[i].rdp_fmt.formatName
+ = strdup(format->formatName);
+ }
+ if (fmt == clip_format_unsupported && format->formatName)
+ {
+ fmt = clip_format_from_string(format->formatName);
+ }
+ if (fmt != clip_format_unsupported)
+ {
+ num_supported_fmts++;
+ }
+ server_formats[i].my_fmt = fmt;
+ }
+ /* CF_RAW is always implicitly supported by the server */
+ {
+ my_clip_format *format
+ = &server_formats[formatList->numFormats];
+ format->rdp_fmt.formatId = CF_RAW;
+ format->rdp_fmt.formatName = NULL;
+ format->my_fmt = clip_format_raw;
+ num_supported_fmts++;
+ }
+ {
+ out_fmts = calloc(num_supported_fmts, sizeof(uint8_t));
+ if (!out_fmts)
+ {
+ perror("calloc");
+ if (server_formats)
+ {
+ free(server_formats);
+ }
+ return CHANNEL_RC_NO_MEMORY;
+ }
+ uint8_t pos = 0;
+ for (i = 0; i < num_supported_fmts; ++i)
+ {
+ if (server_formats[i].my_fmt == clip_format_unsupported)
+ {
+ continue;
+ }
+ out_fmts[pos] = server_formats[i].my_fmt;
+ pos++;
+ }
+ clipboard->my_rdp_context->my_internals->core->api_clipboard
+ ->clipboard_changed(out_fmts, num_supported_fmts,
+ clipboard->my_rdp_context->my_internals->task_info);
+ free(out_fmts);
+ clipboard->srv_fmts = server_formats;
+ }
+
+ {
+ const char *msg = "rdp_module: cliprdr: server format list"
+ " changed message received";
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_trace, 0);
+ }
+
+ ret = rdp_cliprdr_send_client_format_list_response(clipboard, TRUE);
+ return ret;
+}
+
+/* unused for now
+ * static UINT
+rdp_clip_ClientFormatListResponse(CliprdrClientContext* context,
+ CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
+{
+ return CHANNEL_RC_OK;
+}*/
+
+static UINT
+rdp_clip_ServerFormatListResponse(CliprdrClientContext *context,
+ const CLIPRDR_FORMAT_LIST_RESPONSE *formatListResponse)
+{
+ return CHANNEL_RC_OK;
+}
+
+/* unused for now
+ * static UINT
+rdp_clip_ClientLockClipboardData(CliprdrClientContext* context,
+ CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
+{
+ return CHANNEL_RC_OK;
+}*/
+
+static UINT
+rdp_clip_ServerLockClipboardData(CliprdrClientContext *context,
+ const CLIPRDR_LOCK_CLIPBOARD_DATA *lockClipboardData)
+{
+ /* my_rdp_clipboard* clipboard = context->custom;
+ clipboard->my_rdp_context->ft_to_server->clip_data_id =
+ lockClipboardData->clipDataId; */
+ return CHANNEL_RC_OK;
+}
+
+/*
+ * unused
+static UINT
+rdp_clip_ClientUnlockClipboardData(CliprdrClientContext* context,
+ CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
+{
+ return CHANNEL_RC_OK;
+}
+*/
+
+static UINT
+rdp_clip_ServerUnlockClipboardData(CliprdrClientContext *context,
+ const CLIPRDR_UNLOCK_CLIPBOARD_DATA *unlockClipboardData)
+{
+ /* TODO: */
+ return CHANNEL_RC_OK;
+}
+
+/* unused for now
+ * static UINT
+rdp_clip_ClientFormatDataRequest(CliprdrClientContext* context,
+ CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
+{
+ return CHANNEL_RC_OK;
+}*/
+
+static UINT
+rdp_clip_ServerFormatDataRequest(CliprdrClientContext *context,
+ const CLIPRDR_FORMAT_DATA_REQUEST *formatDataRequest)
+{
+ my_rdp_clipboard *clipboard = context->custom;
+ wrdp_enum_clip_format fmt = clip_format_from_id(
+ formatDataRequest->requestedFormatId, clipboard);
+ clipboard->my_rdp_context->my_internals->core->api_clipboard
+ ->request_data(
+ fmt, clipboard->my_rdp_context->my_internals->task_info);
+
+ return CHANNEL_RC_OK;
+}
+
+/* unused for now
+ * static UINT
+rdp_clip_ClientFormatDataResponse(CliprdrClientContext* context,
+ CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
+{
+ return CHANNEL_RC_OK;
+}*/
+
+/*static UINT rdp_cliprdr_send_data_response(my_rdp_clipboard* clipboard,
+ BYTE* data, int size)
+{
+ CLIPRDR_FORMAT_DATA_RESPONSE response;
+ ZeroMemory(&response, sizeof(CLIPRDR_FORMAT_DATA_RESPONSE));
+ response.msgFlags = (data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
+ response.dataLen = size;
+ response.requestedFormatData = data;
+ return clipboard->clip_context->ClientFormatDataResponse(
+ clipboard->clip_context, &response);
+} */
+
+static UINT
+rdp_clip_ServerFormatDataResponse(CliprdrClientContext *context,
+ const CLIPRDR_FORMAT_DATA_RESPONSE *formatDataResponse)
+{
+ UINT32 size = formatDataResponse->common.dataLen;
+ const BYTE *data = formatDataResponse->requestedFormatData;
+ my_rdp_clipboard *clipboard = context->custom;
+
+ if (!data)
+ {
+ const char *msg
+ = "rdp_module: cliprdr: failed to get clipboard data";
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_error, 0);
+ return CHANNEL_RC_OK;
+ }
+
+ switch (clipboard->cli_req_fmt_id.my_fmt)
+ {
+ case clip_format_file_list:
+ {
+ /* TODO: check this */
+ UINT error = NO_ERROR;
+ FILEDESCRIPTORW *file_array = 0;
+ UINT32 file_count = 0, pos = 0;
+ uint8_t *msg_struct = 0, *msg = 0;
+ wrdp_backend_ft_list_entry *list = 0;
+ size_t msg_size_result = 0;
+ error = cliprdr_parse_file_list(
+ data, size, &file_array, &file_count);
+ if (error)
+ {
+ char buf[128];
+ snprintf(buf, 127,
+ "failed to deserialize"
+ " CLIPRDR_FILELIST: 0x%08X",
+ error);
+ clipboard->my_rdp_context->my_internals->core
+ ->api_utils->log_msg((const uint8_t *)buf,
+ strlen(buf), wrdp_log_level_error, 0);
+ return CHANNEL_RC_OK;
+ }
+ msg_struct = calloc(
+ file_count, sizeof(wrdp_backend_ft_list_entry));
+ if (!msg_struct)
+ {
+ perror("calloc");
+ if (file_count && file_array)
+ {
+ free(file_array);
+ }
+ return CHANNEL_RC_OK;
+ }
+ msg_size_result
+ = (file_count * sizeof(wrdp_backend_ft_list_entry))
+ + 3;
+ char *filename_array[file_count];
+ {
+ int i = 0;
+ for (; i < file_count; ++i)
+ {
+ filename_array[i] = 0;
+ }
+ }
+ list = (wrdp_backend_ft_list_entry *)msg_struct;
+ for (; file_array && pos < file_count; ++pos)
+ {
+ list[pos].file_id = pos;
+ if (file_array[pos].cFileName[0])
+ {
+ iconv_t utf16_to_utf8
+ = iconv_open("UTF-8", "UTF-16LE");
+ char *ptr_w, *ptr_r,
+ *tmp_buf
+ = calloc(520, sizeof(char));
+ size_t name_len_utf16
+ = 260 * sizeof(WCHAR),
+ name_len_utf8 = 520,
+ name_len_result = 0;
+ if (!tmp_buf)
+ {
+ perror("calloc");
+ iconv_close(utf16_to_utf8);
+ free(msg_struct);
+ if (file_count)
+ {
+ int i = 0;
+ for (; (i < file_count)
+ && filename_array
+ [i];
+ ++i)
+ {
+ free(
+ filename_array
+ [i]);
+ }
+ free(file_array);
+ }
+ return CHANNEL_RC_OK;
+ }
+ ptr_w = tmp_buf;
+ ptr_r
+ = (char *)file_array[pos].cFileName;
+ while (name_len_utf16 && name_len_utf8)
+ {
+ iconv(utf16_to_utf8, &ptr_r,
+ &name_len_utf16, &ptr_w,
+ &name_len_utf8);
+ }
+#ifdef DEBUG
+ if (!name_len_utf8 && name_len_utf16)
+ {
+ log_msg_info i = {0};
+ i.level = wrdp_log_level_trace;
+ i.buf = (const uint8_t
+ *)"rdp_module: "
+ "cliprdr: iconv: not "
+ "sufficient space in "
+ "output buffer";
+ i.task_info
+ = clipboard->my_internals
+ ->task_info;
+ clipboard->my_internals->core
+ ->api_utils->log_msg_ex(&i);
+ }
+#endif
+ iconv(utf16_to_utf8, 0, 0, &ptr_w,
+ &name_len_utf8);
+ name_len_result = strlen(tmp_buf);
+ msg_size_result += name_len_result;
+ list[pos].filename_len
+ = name_len_result;
+ filename_array[pos] = strdup(tmp_buf);
+ iconv_close(utf16_to_utf8);
+ free(tmp_buf);
+ }
+ /* TODO: check/change endianness */
+ memcpy(&(list[pos].file_size),
+ &file_array[pos].nFileSizeLow, 4);
+ memcpy(((uint8_t *)&(list[pos].file_size)) + 4,
+ &file_array[pos].nFileSizeHigh, 4);
+ }
+ msg = malloc(msg_size_result);
+ if (!msg)
+ {
+ perror("malloc");
+ for (pos = 0; pos < file_count; ++pos)
+ {
+ free(filename_array[pos]);
+ }
+ free(msg_struct);
+ return CHANNEL_RC_OK;
+ }
+ memset(msg, 0, msg_size_result);
+ *((uint8_t *)msg) = clip_format_file_list;
+ memcpy(msg + 1, &file_count, 2);
+ {
+ size_t msg_pos = 3;
+ for (pos = 0; pos < file_count; ++pos)
+ {
+ memcpy(msg + msg_pos,
+ &list[pos].filename_len, 2);
+ msg_pos += 2;
+ memcpy(msg + msg_pos,
+ &list[pos].file_id, 4);
+ msg_pos += 4;
+ memcpy(msg + msg_pos,
+ &list[pos].file_size, 8);
+ msg_pos += 8;
+ /* TODO: looks like i have missed
+ * something ... "Null pointer passed as
+ * an argument to a 'nonnull' parameter"
+ * just added null ptr check for now */
+ if (filename_array[pos])
+ {
+ memcpy(msg + msg_pos,
+ filename_array[pos],
+ list[pos].filename_len);
+ free(filename_array[pos]);
+ }
+ msg_pos += list[pos].filename_len;
+ }
+ }
+ free(msg_struct);
+ clipboard->my_rdp_context->my_internals->core
+ ->api_clipboard->send_data((uint8_t *)msg,
+ msg_size_result,
+ clipboard->my_rdp_context->my_internals
+ ->task_info);
+ free(msg);
+ if (file_count && file_array)
+ {
+ free(file_array);
+ }
+ return CHANNEL_RC_OK;
+ }
+ break;
+ case clip_format_raw:
+ case clip_format_text:
+ {
+ size_t msg_len = size + 1;
+ uint8_t *msg = malloc(msg_len);
+ if (!msg)
+ {
+ perror("malloc");
+ return CHANNEL_RC_OK;
+ }
+ *(uint8_t *)msg = clip_format_text;
+ memcpy(msg + 1, data, size);
+ clipboard->my_rdp_context->my_internals->core
+ ->api_clipboard->send_data(msg, msg_len,
+ clipboard->my_rdp_context->my_internals
+ ->task_info);
+ free(msg);
+ }
+ break;
+ case clip_format_unicode:
+ {
+ iconv_t utf16_to_utf8 = iconv_open("UTF-8", "UTF-16LE");
+ size_t in_left = size, out_left = size;
+ uint8_t *tmp_buf = malloc(out_left), *tmp_buf_ptr;
+ uint8_t *msg = 0;
+ if (!tmp_buf)
+ {
+ perror("malloc");
+ return CHANNEL_RC_OK;
+ }
+ tmp_buf_ptr = tmp_buf;
+ /* wcstombs((char*)msg + 1, tmp_buf, text_len); */
+ while (in_left && out_left)
+ {
+ if (iconv(utf16_to_utf8, (char **)&data,
+ &in_left, (char **)&tmp_buf_ptr,
+ &out_left)
+ == (size_t)-1)
+ {
+ log_msg_info mi = {0};
+ mi.level = wrdp_log_level_warning;
+ mi.buf = (const uint8_t
+ *)"rdp_module: cliprdr: iconv: "
+ "failed to encode outgoing "
+ "buffer from utf8 to utf16le";
+ mi.task_info = clipboard->my_internals
+ ->task_info;
+ clipboard->my_internals->core->api_utils
+ ->log_msg_ex(&mi);
+ free(tmp_buf);
+ iconv_close(utf16_to_utf8);
+ return CHANNEL_RC_NO_BUFFER;
+ }
+ }
+#ifdef DEBUG
+ if (!out_left && in_left)
+ {
+ log_msg_info i = {0};
+ i.level = wrdp_log_level_trace;
+ i.buf = (const uint8_t
+ *)"rdp_module: cliprdr: iconv: not "
+ "sufficient space in output buffer";
+ i.task_info
+ = clipboard->my_internals->task_info;
+ clipboard->my_internals->core->api_utils
+ ->log_msg_ex(&i);
+ }
+#endif
+ iconv(utf16_to_utf8, 0, 0, (char **)&tmp_buf_ptr,
+ &out_left);
+ iconv_close(utf16_to_utf8);
+ msg = malloc(size - out_left + 1);
+ if (!msg)
+ {
+ perror("malloc");
+ return CHANNEL_RC_OK;
+ }
+ *(uint8_t *)msg = clip_format_unicode;
+ memcpy(msg + 1, tmp_buf, size - out_left);
+#ifdef DEBUG
+ {
+ log_msg_info i = {0};
+ size_t len = 1024;
+ char msg_buf[len];
+ snprintf(msg_buf, len - 1,
+ "rdp_module: cliprdr: sending encoded from "
+ "utf16le to utf8 text: %s",
+ tmp_buf);
+ i.level = wrdp_log_level_trace;
+ i.buf = (const uint8_t *)msg_buf;
+ i.task_info
+ = clipboard->my_internals->task_info;
+ clipboard->my_internals->core->api_utils
+ ->log_msg_ex(&i);
+ }
+#endif
+ free(tmp_buf);
+ clipboard->my_rdp_context->my_internals->core
+ ->api_clipboard->send_data(msg, size - out_left + 1,
+ clipboard->my_rdp_context->my_internals
+ ->task_info);
+ free(msg);
+ }
+ break;
+ default:
+ {
+ const char *msg = "rdp_module: cliprdr: unsupported"
+ " clipboardformat requested";
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_warning, 0);
+ }
+ break;
+ }
+ return CHANNEL_RC_OK;
+}
+
+/* unused for now
+ * static UINT
+rdp_clip_ClientFileContentsRequest(CliprdrClientContext* context,
+ CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
+{
+ return CHANNEL_RC_OK;
+}*/
+
+static UINT
+rdp_clip_ServerFileContentsRequest(CliprdrClientContext *context,
+ const CLIPRDR_FILE_CONTENTS_REQUEST *fileContentsRequest)
+{
+ my_rdp_clipboard *clipboard = context->custom;
+ if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
+ {
+ CLIPRDR_FILE_CONTENTS_RESPONSE resp;
+ if (fileContentsRequest->nPositionLow
+ || fileContentsRequest->nPositionHigh
+ || (fileContentsRequest->cbRequested != 0x00000008))
+ {
+ const char *msg
+ = "rdp_module: ft: ServerFileContentsRequest: "
+ "malformed request";
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_warning, 0);
+ return CHANNEL_RC_OK;
+ }
+ memset(&resp, 0, sizeof(CLIPRDR_FILE_CONTENTS_RESPONSE));
+ resp.common.msgType = CB_FILECONTENTS_RESPONSE;
+ resp.common.msgFlags = CB_RESPONSE_OK;
+ resp.streamId = fileContentsRequest->streamId;
+ resp.cbRequested = sizeof(uint64_t);
+ resp.requestedData = (BYTE *)&(
+ clipboard
+ ->client_filelist_cache[fileContentsRequest->listIndex]
+ .file_size);
+ clipboard->clip_context->ClientFileContentsResponse(
+ clipboard->clip_context, &resp);
+ }
+ else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE)
+ {
+ wrdp_backend_ft_file_request req;
+ memset(&req, 0, sizeof(wrdp_backend_ft_list_entry));
+ /* TODO: better way to detect runing transfer */
+ clipboard->my_rdp_context->ft_to_server->is_runing = true;
+ req.file_id = fileContentsRequest->listIndex;
+ req.req_size = fileContentsRequest->cbRequested;
+ clipboard->my_rdp_context->ft_to_server->transfer_id
+ = fileContentsRequest->streamId;
+ memcpy(
+ &req.file_offset, &(fileContentsRequest->nPositionLow), 4);
+ memcpy(((uint8_t *)&req.file_offset) + 4,
+ &(fileContentsRequest->nPositionHigh), 4);
+ clipboard->my_rdp_context->my_internals->core->api_filetransfers
+ ->ft_request(&req,
+ clipboard->my_rdp_context->my_internals->task_info);
+ }
+ return CHANNEL_RC_OK;
+}
+
+/* unused for now
+ * static UINT
+rdp_clip_ClientFileContentsResponse(CliprdrClientContext* context,
+ CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse)
+{
+ return CHANNEL_RC_OK;
+} */
+
+static UINT
+rdp_clip_ServerFileContentsResponse(
+ CliprdrClientContext *context, const CLIPRDR_FILE_CONTENTS_RESPONSE *resp)
+{
+ /* TODO: somehow avoid memory copying here */
+ my_rdp_clipboard *clipboard = context->custom;
+ wrdp_backend_ft_chunk chunk;
+ if (resp->common.msgFlags & CB_RESPONSE_FAIL)
+ {
+ const char *msg = "rdp_module: ft: ServerFileContentsResponse: "
+ "CB_RESPONSE_FAIL";
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_error, 0);
+ goto transfer_failed;
+ }
+ if (!resp->cbRequested
+ && (clipboard->my_rdp_context->ft_to_client->transferred
+ != clipboard->my_rdp_context->ft_to_client->file_size))
+ {
+ char msg[128];
+ snprintf(msg, 127,
+ "rdp_module: ft: chunk with zero size received on"
+ " position %lu of %lu",
+ clipboard->my_rdp_context->ft_to_client->transferred,
+ clipboard->my_rdp_context->ft_to_client->file_size);
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_error, 0);
+ goto transfer_failed;
+ }
+ else if (clipboard->my_rdp_context->ft_to_client->transfer_id
+ != resp->streamId)
+ {
+ const char *msg
+ = "rdp_module: clipboard: ft: transfer_id mismatch in "
+ "ServerFileContentsResponse";
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_error, 0);
+ goto transfer_failed;
+ }
+ else if (resp->cbRequested)
+ {
+ chunk.transfer_id = resp->streamId;
+ chunk.size = resp->cbRequested;
+ clipboard->my_rdp_context->my_internals->core->api_filetransfers
+ ->ft_send_chunk(&chunk, (uint8_t *)(resp->requestedData),
+ clipboard->my_rdp_context->my_internals->task_info);
+ clipboard->my_rdp_context->ft_to_client->transferred
+ += resp->cbRequested;
+ }
+ /* request next file chunk */
+ if (clipboard->my_rdp_context->ft_to_client->transferred
+ < clipboard->my_rdp_context->ft_to_client->file_size)
+ {
+ my_rdp_context *c = clipboard->my_rdp_context;
+ CLIPRDR_FILE_CONTENTS_REQUEST file_req;
+ uint32_t chunk_len = 512000;
+ uint64_t left = 0;
+ memset(&file_req, 0, sizeof(CLIPRDR_FILE_CONTENTS_REQUEST));
+
+ left
+ = c->ft_to_client->file_size - c->ft_to_client->transferred;
+ if (left < chunk_len)
+ {
+ chunk_len = left;
+ }
+ file_req.cbRequested = chunk_len;
+ c->ft_to_client->transfer_id = rand();
+ file_req.streamId = c->ft_to_client->transfer_id;
+ file_req.listIndex = c->ft_to_client->file_id;
+ file_req.dwFlags = FILECONTENTS_RANGE;
+
+ /* TODO: check/change endianness */
+ memcpy(&(file_req.nPositionLow),
+ &(c->ft_to_client->transferred), 4);
+ memcpy(&(file_req.nPositionHigh),
+ (((uint8_t *)&(c->ft_to_client->transferred)) + 4), 4);
+ c->clipboard->clip_context->ClientFileContentsRequest(
+ c->clipboard->clip_context, &file_req);
+ }
+ else if (clipboard->my_rdp_context->ft_to_client->is_runing == true)
+ {
+ my_rdp_context *c = clipboard->my_rdp_context;
+ wrdp_backend_ft_status status;
+ {
+ char msg[128];
+ snprintf(msg, 127,
+ "rdp_module: ft: status: transfer finished,"
+ " transfered size: %lu",
+ clipboard->my_rdp_context->ft_to_client
+ ->transferred);
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_trace, 0);
+ }
+ memset(&status, 0, sizeof(wrdp_backend_ft_status));
+ status.status = ft_status_success;
+ status.file_id = c->ft_to_client->file_id;
+ status.transfer_id = c->ft_to_client->transfer_id;
+ c->my_internals->core->api_filetransfers->ft_finish(
+ &status, c->my_internals->task_info);
+ clipboard->my_rdp_context->ft_to_client->file_size = 0;
+ clipboard->my_rdp_context->ft_to_client->transfer_id = 0;
+ clipboard->my_rdp_context->ft_to_client->file_id = 0;
+ clipboard->my_rdp_context->ft_to_client->is_runing = false;
+ clipboard->my_rdp_context->ft_to_client->transferred = 0;
+ }
+ else
+ {
+ const char *msg = "rdp_module: clipboard: ft:"
+ " unexpected data chunk on finished transfer";
+ clipboard->my_rdp_context->my_internals->core->api_utils
+ ->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_warning, 0);
+ }
+ return CHANNEL_RC_OK;
+transfer_failed:
+{
+ my_rdp_context *c = clipboard->my_rdp_context;
+ wrdp_backend_ft_status status;
+ memset(&status, 0, sizeof(wrdp_backend_ft_status));
+ status.status = ft_status_failure;
+ status.file_id = c->ft_to_client->file_id;
+ status.transfer_id = c->ft_to_client->transfer_id;
+ c->my_internals->core->api_filetransfers->ft_finish(
+ &status, c->my_internals->task_info);
+ clipboard->my_rdp_context->ft_to_client->file_size = 0;
+ clipboard->my_rdp_context->ft_to_client->transfer_id = 0;
+ clipboard->my_rdp_context->ft_to_client->file_id = 0;
+ clipboard->my_rdp_context->ft_to_client->is_runing = false;
+ clipboard->my_rdp_context->ft_to_client->transferred = 0;
+}
+ return CHANNEL_RC_OK;
+}
+
+void
+rdp_cliprdr_init(my_rdp_context *ctx, CliprdrClientContext *cliprdr)
+{
+ ctx->clipboard->clip_context = cliprdr;
+ cliprdr->custom = (void *)ctx->clipboard;
+ ctx->clipboard->my_internals = ctx->my_internals;
+ cliprdr->MonitorReady = rdp_clip_MonitorReady;
+ cliprdr->ServerCapabilities = rdp_clip_ServerCapabilities;
+ cliprdr->ServerFormatList = rdp_clip_ServerFormatList;
+ cliprdr->ServerFormatListResponse = rdp_clip_ServerFormatListResponse;
+ cliprdr->ServerFormatDataRequest = rdp_clip_ServerFormatDataRequest;
+ cliprdr->ServerFormatDataResponse = rdp_clip_ServerFormatDataResponse;
+ cliprdr->ServerFileContentsRequest = rdp_clip_ServerFileContentsRequest;
+ cliprdr->ServerFileContentsResponse
+ = rdp_clip_ServerFileContentsResponse;
+ cliprdr->ServerLockClipboardData = rdp_clip_ServerLockClipboardData;
+ cliprdr->ServerUnlockClipboardData = rdp_clip_ServerUnlockClipboardData;
+}
+
+void
+rdp_cliprdr_uninit(my_rdp_context *ctx, CliprdrClientContext *cliprdr)
+{
+ /* do not need to do it here, done during backend destruction */
+ /* cliprdr->custom = NULL;
+
+ if (ctx->clipboard)
+ ctx->clipboard->clip_context = NULL;
+ ctx->clipboard = NULL; */
+}
+
+/* unused for now
+ * static UINT
+ClientRequestFileSize(wClipboardDelegate* delegate,
+ const wClipboardFileSizeRequest* request)
+{
+ return 0;
+}*/
+
+static UINT
+ClipboardFileSizeSuccess(wClipboardDelegate *delegate,
+ const wClipboardFileSizeRequest *request, UINT64 fileSize)
+{
+ CLIPRDR_FILE_CONTENTS_RESPONSE response;
+ my_rdp_clipboard *clipboard = delegate->custom;
+ ZeroMemory(&response, sizeof(response));
+ response.common.msgFlags = CB_RESPONSE_OK;
+ response.streamId = request->streamId;
+ response.cbRequested = sizeof(UINT64);
+ response.requestedData = (BYTE *)&fileSize;
+ return clipboard->clip_context->ClientFileContentsResponse(
+ clipboard->clip_context, &response);
+ // return 0;
+}
+static UINT
+ClipboardFileSizeFailure(wClipboardDelegate *delegate,
+ const wClipboardFileSizeRequest *request, UINT errorCode)
+{
+ CLIPRDR_FILE_CONTENTS_RESPONSE response;
+ my_rdp_clipboard *clipboard = delegate->custom;
+ ZeroMemory(&response, sizeof(response));
+ response.common.msgFlags = CB_RESPONSE_FAIL;
+ response.streamId = request->streamId;
+ return clipboard->clip_context->ClientFileContentsResponse(
+ clipboard->clip_context, &response);
+}
+
+/* unused for now
+ * static UINT
+ClientRequestFileRange(wClipboardDelegate* delegate,
+ const wClipboardFileRangeRequest* request)
+{
+ return 0;
+} */
+
+static UINT
+ClipboardFileRangeSuccess(wClipboardDelegate *delegate,
+ const wClipboardFileRangeRequest *request, const BYTE *data, UINT32 size)
+{
+ CLIPRDR_FILE_CONTENTS_RESPONSE response;
+ my_rdp_clipboard *clipboard = delegate->custom;
+ ZeroMemory(&response, sizeof(response));
+ response.common.msgFlags = CB_RESPONSE_OK;
+ response.streamId = request->streamId;
+ response.cbRequested = size;
+ response.requestedData = (BYTE *)data;
+ return clipboard->clip_context->ClientFileContentsResponse(
+ clipboard->clip_context, &response);
+}
+
+static UINT
+ClipboardFileRangeFailure(wClipboardDelegate *delegate,
+ const wClipboardFileRangeRequest *request, UINT errorCode)
+{
+ CLIPRDR_FILE_CONTENTS_RESPONSE response;
+ my_rdp_clipboard *clipboard = delegate->custom;
+ ZeroMemory(&response, sizeof(response));
+ response.common.msgFlags = CB_RESPONSE_FAIL;
+ response.streamId = request->streamId;
+ return clipboard->clip_context->ClientFileContentsResponse(
+ clipboard->clip_context, &response);
+}
+
+my_rdp_clipboard *
+rdp_clipboard_new(my_rdp_context *context)
+{
+ my_rdp_clipboard *clip = calloc(1, sizeof(my_rdp_clipboard));
+ if (!clip)
+ {
+ perror("calloc");
+ return 0;
+ }
+ clip->my_rdp_context = context;
+ clip->clipboard = ClipboardCreate();
+ clip->delegate = ClipboardGetDelegate(clip->clipboard);
+ clip->delegate->custom = clip;
+
+ clip->delegate->ClipboardFileSizeSuccess = ClipboardFileSizeSuccess;
+ clip->delegate->ClipboardFileSizeFailure = ClipboardFileSizeFailure;
+ clip->delegate->ClipboardFileRangeSuccess = ClipboardFileRangeSuccess;
+ clip->delegate->ClipboardFileRangeFailure = ClipboardFileRangeFailure;
+
+ context->clipboard = clip;
+
+ return clip;
+}
+
+static UINT
+cliprdr_send_data_request(my_rdp_clipboard *clipboard)
+{
+ CLIPRDR_FORMAT_DATA_REQUEST request;
+ ZeroMemory(&request, sizeof(CLIPRDR_FORMAT_DATA_REQUEST));
+ request.requestedFormatId = clipboard->cli_req_fmt_id.rdp_fmt.formatId;
+ return clipboard->clip_context->ClientFormatDataRequest(
+ clipboard->clip_context, &request);
+}
+
+static bool
+clip_api_handle_request_data(
+ const wrdp_backend_clipbrd_data_request *request, void *backend_internals)
+{
+ rdp_internals *i = backend_internals;
+ i->context->clipboard->cli_req_fmt_id.my_fmt = request->format;
+ switch (request->format)
+ {
+ case clip_format_raw:
+ {
+ /* i->context->clipboard->cli_req_fmt_id = CF_RAW;
+ * raw format does not work for some reason
+ */
+ i->context->clipboard->cli_req_fmt_id.rdp_fmt.formatId
+ = CF_TEXT;
+ }
+ break;
+ case clip_format_text:
+ {
+ i->context->clipboard->cli_req_fmt_id.rdp_fmt.formatId
+ = CF_TEXT;
+ }
+ break;
+ case clip_format_file_list:
+ {
+ /* 4.5.3 Format Data Request PDU
+ The following is an annotated dump of a Format
+ Data Request PDU (section 2.2.5.1). The format being
+ requested is the File List that was advertised
+ in section 4.5.1 (the advertised ID in the Format
+ List PDU was 49273).
+ */
+ uint8_t p;
+ bool format_found = false;
+ for (p = 0; p < i->context->clipboard->srv_fmts_count;
+ ++p)
+ {
+ if (!strcmp("FileGroupDescriptorW",
+ i->context->clipboard->srv_fmts[p]
+ .rdp_fmt.formatName))
+ {
+ i->context->clipboard->cli_req_fmt_id
+ .rdp_fmt.formatId
+ = i->context->clipboard->srv_fmts[p]
+ .rdp_fmt.formatId;
+ format_found = true;
+ break;
+ }
+ }
+ if (!format_found)
+ {
+ const char *msg = "rdp_module: cliprdr: "
+ "requested data format"
+ " id not found";
+ i->core->api_utils->log_msg(
+ (const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_warning, 0);
+ i->context->clipboard->cli_req_fmt_id.rdp_fmt
+ .formatId
+ = CF_RAW;
+ }
+ }
+ break;
+ case clip_format_unicode:
+ {
+ i->context->clipboard->cli_req_fmt_id.rdp_fmt.formatId
+ = CF_UNICODETEXT;
+ }
+ break;
+ default:
+ {
+ const char *msg
+ = "rdp_module: cliprdr: unsuported data format"
+ " requested";
+ i->core->api_utils->log_msg((const uint8_t *)msg,
+ strlen(msg), wrdp_log_level_warning, 0);
+ }
+ break;
+ }
+ cliprdr_send_data_request(i->context->clipboard);
+ return true;
+}
+
+static bool
+clip_api_handle_data_changed(
+ const wrdp_backend_clipbrd_fmts *fmts, void *backend_internals)
+{
+ rdp_internals *i = backend_internals;
+ my_rdp_clipboard *clip = i->context->clipboard;
+ uint8_t pos = 0;
+
+ CLIPRDR_FORMAT *formats = 0;
+ CLIPRDR_FORMAT_LIST formatList;
+
+ if (!fmts->count)
+ {
+ const char *msg = "received empty clipboard formats list";
+ i->core->api_utils->log_msg((const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_warning, 0);
+ return false;
+ }
+ formats = calloc(fmts->count, sizeof(CLIPRDR_FORMAT));
+ if (!formats)
+ {
+ perror("calloc");
+ return false;
+ }
+
+ for (; pos < fmts->count; ++pos)
+ {
+ switch ((wrdp_enum_clip_format)fmts->formats[pos])
+ {
+ case clip_format_raw:
+ {
+ formats[pos].formatId = CF_RAW;
+ }
+ break;
+ case clip_format_text:
+ {
+ formats[pos].formatId = CF_TEXT;
+ }
+ break;
+ case clip_format_unicode:
+ {
+ formats[pos].formatId = CF_UNICODETEXT;
+ }
+ break;
+ case clip_format_file_list:
+ {
+ formats[pos].formatId = CB_FORMAT_TEXTURILIST;
+ formats[pos].formatName
+ = "FileGroupDescriptorW";
+ }
+ break;
+ default:
+ {
+ formats[pos].formatId = CF_RAW;
+ }
+ break;
+ }
+ }
+ formatList.common.msgFlags = CB_RESPONSE_OK;
+ formatList.numFormats = fmts->count;
+ formatList.formats = formats;
+ /* TODO: check return value of ClientFormatList */
+ clip->clip_context->ClientFormatList(clip->clip_context, &formatList);
+ free(formats);
+
+ return true;
+}
+
+static bool
+clip_api_handle_send_data(
+ const wrdp_backend_clipbrd_data *data, void *backend_internals)
+{
+ rdp_internals *i = backend_internals;
+ my_rdp_clipboard *clip = i->context->clipboard;
+ CLIPRDR_FORMAT_DATA_RESPONSE response;
+
+ uint8_t type = data->data[0];
+ memset(&response, 0, sizeof(CLIPRDR_FORMAT_DATA_RESPONSE));
+ response.common.msgType = CB_FORMAT_DATA_RESPONSE;
+ response.common.msgFlags = CB_RESPONSE_FAIL;
+ response.common.dataLen = 0;
+ response.requestedFormatData = 0;
+#ifdef DEBUG
+ {
+ const char *msg
+ = "rdp_module: clipboard: from client: raw clipboard"
+ " data:";
+ i->core->api_utils->log_msg(
+ (const uint8_t *)msg, strlen(msg), wrdp_log_level_trace, 0);
+ i->core->api_utils->log_msg(data->data, data->size,
+ wrdp_log_level_trace, wrdp_log_flag_binary);
+ }
+#endif
+ switch (type)
+ {
+ case clip_format_raw:
+ case clip_format_text:
+ {
+ if (data->size > 1)
+ {
+ response.common.msgFlags = CB_RESPONSE_OK;
+ response.common.dataLen = data->size - 1;
+ response.requestedFormatData = data->data + 1;
+ }
+ clip->clip_context->ClientFormatDataResponse(
+ clip->clip_context, &response);
+ return true;
+ }
+ break;
+ case clip_format_unicode:
+ {
+ uint8_t *data_buf = 0;
+ BYTE *msg_buf = 0;
+ iconv_t utf8_to_utf16 = iconv_open("UTF-16LE", "UTF-8");
+ char *ptr_w, *ptr_r;
+ size_t text_len_utf16 = (data->size - 1) * 2,
+ text_len_utf8 = data->size - 1,
+ text_len_result = text_len_utf16;
+ data_buf = calloc(text_len_utf16, sizeof(uint8_t));
+ if (!data_buf)
+ {
+ perror("calloc");
+ return true;
+ }
+ ptr_w = (char *)data_buf;
+ ptr_r = (char *)(data->data + 1);
+ while (text_len_utf8 && text_len_utf16)
+ {
+ if (iconv(utf8_to_utf16, &ptr_r, &text_len_utf8,
+ &ptr_w, &text_len_utf16)
+ == (size_t)-1)
+ {
+ log_msg_info mi = {0};
+ free(data_buf);
+ clip->clip_context
+ ->ClientFormatDataResponse(
+ clip->clip_context, &response);
+ mi.level = wrdp_log_level_warning;
+ mi.buf = (const uint8_t
+ *)"rdp_module: cliprdr: iconv: "
+ "failed to encode outgoing "
+ "buffer from utf8 to utf16le";
+ mi.task_info = i->task_info;
+ i->core->api_utils->log_msg_ex(&mi);
+ iconv_close(utf8_to_utf16);
+ return true;
+ }
+ }
+#ifdef DEBUG
+ if (text_len_utf8 && !text_len_utf16)
+ {
+ log_msg_info mi = {0};
+ mi.level = wrdp_log_level_trace;
+ mi.buf = (const uint8_t
+ *)"rdp_module: cliprdr: iconv: not "
+ "sufficient space in output buffer";
+ mi.task_info = i->task_info;
+ i->core->api_utils->log_msg_ex(&mi);
+ }
+#endif
+ iconv(utf8_to_utf16, 0, 0, &ptr_w, &text_len_utf16);
+ text_len_result = text_len_result - text_len_utf16;
+ iconv_close(utf8_to_utf16);
+ if (text_len_result)
+ {
+ msg_buf = calloc(text_len_result, sizeof(BYTE));
+ memcpy(msg_buf, data_buf, text_len_result);
+ free(data_buf);
+ response.common.msgFlags = CB_RESPONSE_OK;
+ response.common.dataLen = text_len_result;
+ response.requestedFormatData = msg_buf;
+ }
+#ifdef DEBUG
+ if (msg_buf)
+ {
+ log_msg_info mi = {0};
+ mi.level = wrdp_log_level_trace;
+ mi.buf = (const uint8_t
+ *)"rdp_module: clipboard: to server:"
+ " utf16 encoded buffer";
+ mi.task_info = i->task_info;
+ i->core->api_utils->log_msg_ex(&mi);
+ mi.flags = wrdp_log_flag_binary;
+ mi.buf = msg_buf;
+ mi.buf_size = text_len_result;
+ i->core->api_utils->log_msg_ex(&mi);
+ }
+#endif
+ clip->clip_context->ClientFormatDataResponse(
+ clip->clip_context, &response);
+ if (msg_buf)
+ {
+ free(msg_buf);
+ }
+ return true;
+ }
+ break;
+ case clip_format_file_list:
+ {
+ uint16_t file_count;
+ bool broken_msg = false;
+ int _i;
+ uint64_t data_offset = 3;
+ FILEDESCRIPTORW *file_list = 0;
+ BYTE *format_data = 0;
+ UINT32 format_data_length = 0;
+ memcpy(&file_count, data->data + 1, 2);
+ if (!file_count)
+ {
+ const char *msg = "rdp_module: clipboard: ft: "
+ "empty file_list received";
+ i->core->api_utils->log_msg(
+ (const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_warning, 0);
+ return true;
+ }
+ if (clip->client_filelist_cache)
+ {
+ free(clip->client_filelist_cache);
+ }
+ clip->client_filelist_cache
+ = calloc(file_count, sizeof(file_list_cache_entry));
+ if (!clip->client_filelist_cache)
+ {
+ perror("calloc");
+ return false;
+ }
+ file_list = calloc(file_count, sizeof(FILEDESCRIPTOR));
+ if (!file_list)
+ {
+ perror("calloc");
+ return false;
+ }
+ for (_i = 0; _i < file_count; ++_i)
+ {
+ uint16_t filename_len;
+ uint32_t file_id;
+ uint64_t file_size;
+ char *filename, *ptr_w, *ptr_r;
+ iconv_t utf8_to_utf16
+ = iconv_open("UTF-16LE", "UTF-8");
+ size_t text_len_utf16 = 260 * sizeof(WCHAR),
+ text_len_utf8;
+ memcpy(
+ &filename_len, data->data + data_offset, 2);
+ data_offset += 2;
+ if (data_offset >= data->size)
+ {
+ broken_msg = true;
+ break;
+ }
+ text_len_utf8 = filename_len;
+ memcpy(&file_id, data->data + data_offset, 4);
+ clip->client_filelist_cache[_i].file_id
+ = file_id;
+ data_offset += 4;
+ if (data_offset >= data->size)
+ {
+ broken_msg = true;
+ break;
+ }
+ memcpy(&file_size, data->data + data_offset, 8);
+ clip->client_filelist_cache[_i].file_size
+ = file_size;
+ data_offset += 8;
+ if ((data_offset >= data->size)
+ && (_i < file_count))
+ {
+ broken_msg = true;
+ break;
+ }
+ filename = (char *)data->data + data_offset;
+ ptr_r = filename;
+ ptr_w = (char *)(file_list[_i].cFileName);
+ iconv(utf8_to_utf16, &ptr_r, &text_len_utf8,
+ &ptr_w, &text_len_utf16);
+ iconv(utf8_to_utf16, 0, 0, &ptr_w,
+ &text_len_utf16);
+ iconv_close(utf8_to_utf16);
+ data_offset += filename_len;
+ file_list[_i].dwFileAttributes
+ = FILE_ATTRIBUTE_NORMAL;
+ file_list[_i].dwFlags = FD_ATTRIBUTES
+ | FD_FILESIZE
+ | FD_SHOWPROGRESSUI;
+ memcpy(&(file_list[_i].nFileSizeLow),
+ &file_size, 4);
+ memcpy(&(file_list[_i].nFileSizeHigh),
+ ((uint8_t *)&file_size) + 4, 4);
+ }
+ if (broken_msg)
+ {
+ char msg[128];
+ snprintf(msg, 127,
+ "rdp_module: cliprdr: error: wrong file"
+ " list message size: %d, file count: %d\n",
+ data->size, file_count);
+ i->core->api_utils->log_msg(
+ (const uint8_t *)msg, strlen(msg),
+ wrdp_log_level_error, 0);
+ }
+ else
+ {
+ UINT error = NO_ERROR;
+ error = cliprdr_serialize_file_list(file_list,
+ file_count, &format_data,
+ &format_data_length);
+ if (error)
+ {
+ char buf[128];
+ snprintf(buf, 127,
+ "failed to serialize "
+ "CLIPRDR_FILELIST:"
+ " 0x%08X",
+ error);
+ i->core->api_utils->log_msg(
+ (const uint8_t *)buf, strlen(buf),
+ wrdp_log_level_error, 0);
+ }
+ }
+ free(file_list);
+ if (format_data_length)
+ {
+ response.common.msgFlags = CB_RESPONSE_OK;
+ response.common.dataLen = format_data_length;
+ response.requestedFormatData = format_data;
+ }
+ clip->clip_context->ClientFormatDataResponse(
+ clip->clip_context, &response);
+
+ free(format_data);
+ return true;
+ }
+ break;
+ default:
+ {
+ response.common.msgFlags = CB_RESPONSE_FAIL;
+ response.common.dataLen = 0;
+ response.requestedFormatData = 0;
+ clip->clip_context->ClientFormatDataResponse(
+ clip->clip_context, &response);
+ return true;
+ }
+ break;
+ }
+ /* TODO: check return code from ClientFormatDataResponse */
+ return true;
+}
+
+void
+register_clipboard(wrdp_backend_module *backend)
+{
+ backend->callbacks_clipbrd->request_data = clip_api_handle_request_data;
+ backend->callbacks_clipbrd->send_data = clip_api_handle_send_data;
+ backend->callbacks_clipbrd->data_changed = clip_api_handle_data_changed;
+}