summaryrefslogtreecommitdiffstats
path: root/client/Wayland/wlf_cliprdr.c
diff options
context:
space:
mode:
Diffstat (limited to 'client/Wayland/wlf_cliprdr.c')
-rw-r--r--client/Wayland/wlf_cliprdr.c1009
1 files changed, 1009 insertions, 0 deletions
diff --git a/client/Wayland/wlf_cliprdr.c b/client/Wayland/wlf_cliprdr.c
new file mode 100644
index 0000000..dc189d5
--- /dev/null
+++ b/client/Wayland/wlf_cliprdr.c
@@ -0,0 +1,1009 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Wayland Clipboard Redirection
+ *
+ * Copyright 2018 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2018 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <stdlib.h>
+
+#include <winpr/crt.h>
+#include <winpr/image.h>
+#include <winpr/stream.h>
+#include <winpr/clipboard.h>
+
+#include <freerdp/log.h>
+#include <freerdp/client/cliprdr.h>
+#include <freerdp/channels/channels.h>
+#include <freerdp/channels/cliprdr.h>
+
+#include <freerdp/client/client_cliprdr_file.h>
+
+#include "wlf_cliprdr.h"
+
+#define TAG CLIENT_TAG("wayland.cliprdr")
+
+#define MAX_CLIPBOARD_FORMATS 255
+
+#define mime_text_plain "text/plain"
+#define mime_text_utf8 mime_text_plain ";charset=utf-8"
+
+static const char* mime_text[] = { mime_text_plain, mime_text_utf8, "UTF8_STRING",
+ "COMPOUND_TEXT", "TEXT", "STRING" };
+
+static const char mime_png[] = "image/png";
+static const char mime_webp[] = "image/webp";
+static const char mime_jpg[] = "image/jpeg";
+static const char mime_tiff[] = "image/tiff";
+static const char mime_uri_list[] = "text/uri-list";
+static const char mime_html[] = "text/html";
+
+#define BMP_MIME_LIST "image/bmp", "image/x-bmp", "image/x-MS-bmp", "image/x-win-bitmap"
+static const char* mime_bitmap[] = { BMP_MIME_LIST };
+static const char* mime_image[] = { mime_png, mime_webp, mime_jpg, mime_tiff, BMP_MIME_LIST };
+
+static const char mime_gnome_copied_files[] = "x-special/gnome-copied-files";
+static const char mime_mate_copied_files[] = "x-special/mate-copied-files";
+
+static const char type_FileGroupDescriptorW[] = "FileGroupDescriptorW";
+static const char type_HtmlFormat[] = "HTML Format";
+
+typedef struct
+{
+ FILE* responseFile;
+ UINT32 responseFormat;
+ char* responseMime;
+} wlf_request;
+
+struct wlf_clipboard
+{
+ wlfContext* wfc;
+ rdpChannels* channels;
+ CliprdrClientContext* context;
+ wLog* log;
+
+ UwacSeat* seat;
+ wClipboard* system;
+
+ size_t numClientFormats;
+ CLIPRDR_FORMAT* clientFormats;
+
+ size_t numServerFormats;
+ CLIPRDR_FORMAT* serverFormats;
+
+ BOOL sync;
+
+ CRITICAL_SECTION lock;
+ CliprdrFileContext* file;
+
+ wQueue* request_queue;
+};
+
+static void wlf_request_free(void* rq)
+{
+ wlf_request* request = rq;
+ if (request)
+ {
+ free(request->responseMime);
+ if (request->responseFile)
+ fclose(request->responseFile);
+ }
+ free(request);
+}
+
+static wlf_request* wlf_request_new(void)
+{
+ return calloc(1, sizeof(wlf_request));
+}
+
+static void* wlf_request_clone(const void* oth)
+{
+ const wlf_request* other = (const wlf_request*)oth;
+ wlf_request* copy = wlf_request_new();
+ if (!copy)
+ return NULL;
+ *copy = *other;
+ if (other->responseMime)
+ {
+ copy->responseMime = _strdup(other->responseMime);
+ if (!copy->responseMime)
+ goto fail;
+ }
+ return copy;
+fail:
+ wlf_request_free(copy);
+ return NULL;
+}
+
+static BOOL wlf_mime_is_file(const char* mime)
+{
+ if (strncmp(mime_uri_list, mime, sizeof(mime_uri_list)) == 0)
+ return TRUE;
+ if (strncmp(mime_gnome_copied_files, mime, sizeof(mime_gnome_copied_files)) == 0)
+ return TRUE;
+ if (strncmp(mime_mate_copied_files, mime, sizeof(mime_mate_copied_files)) == 0)
+ return TRUE;
+ return FALSE;
+}
+
+static BOOL wlf_mime_is_text(const char* mime)
+{
+ for (size_t x = 0; x < ARRAYSIZE(mime_text); x++)
+ {
+ if (strcmp(mime, mime_text[x]) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static BOOL wlf_mime_is_image(const char* mime)
+{
+ for (size_t x = 0; x < ARRAYSIZE(mime_image); x++)
+ {
+ if (strcmp(mime, mime_image[x]) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static BOOL wlf_mime_is_html(const char* mime)
+{
+ if (strcmp(mime, mime_html) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void wlf_cliprdr_free_server_formats(wfClipboard* clipboard)
+{
+ if (clipboard && clipboard->serverFormats)
+ {
+ for (size_t j = 0; j < clipboard->numServerFormats; j++)
+ {
+ CLIPRDR_FORMAT* format = &clipboard->serverFormats[j];
+ free(format->formatName);
+ }
+
+ free(clipboard->serverFormats);
+ clipboard->serverFormats = NULL;
+ clipboard->numServerFormats = 0;
+ }
+
+ if (clipboard)
+ UwacClipboardOfferDestroy(clipboard->seat);
+}
+
+static void wlf_cliprdr_free_client_formats(wfClipboard* clipboard)
+{
+ if (clipboard && clipboard->numClientFormats)
+ {
+ for (size_t j = 0; j < clipboard->numClientFormats; j++)
+ {
+ CLIPRDR_FORMAT* format = &clipboard->clientFormats[j];
+ free(format->formatName);
+ }
+
+ free(clipboard->clientFormats);
+ clipboard->clientFormats = NULL;
+ clipboard->numClientFormats = 0;
+ }
+
+ if (clipboard)
+ UwacClipboardOfferDestroy(clipboard->seat);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT wlf_cliprdr_send_client_format_list(wfClipboard* clipboard)
+{
+ WINPR_ASSERT(clipboard);
+
+ const CLIPRDR_FORMAT_LIST formatList = { .common.msgFlags = CB_RESPONSE_OK,
+ .numFormats = (UINT32)clipboard->numClientFormats,
+ .formats = clipboard->clientFormats,
+ .common.msgType = CB_FORMAT_LIST };
+
+ cliprdr_file_context_clear(clipboard->file);
+
+ WLog_VRB(TAG, "-------------- client format list [%" PRIu32 "] ------------------",
+ formatList.numFormats);
+ for (UINT32 x = 0; x < formatList.numFormats; x++)
+ {
+ const CLIPRDR_FORMAT* format = &formatList.formats[x];
+ WLog_VRB(TAG, "client announces %" PRIu32 " [%s][%s]", format->formatId,
+ ClipboardGetFormatIdString(format->formatId), format->formatName);
+ }
+ WINPR_ASSERT(clipboard->context);
+ WINPR_ASSERT(clipboard->context->ClientFormatList);
+ return clipboard->context->ClientFormatList(clipboard->context, &formatList);
+}
+
+static void wfl_cliprdr_add_client_format_id(wfClipboard* clipboard, UINT32 formatId)
+{
+ CLIPRDR_FORMAT* format = NULL;
+ const char* name = ClipboardGetFormatName(clipboard->system, formatId);
+
+ for (size_t x = 0; x < clipboard->numClientFormats; x++)
+ {
+ format = &clipboard->clientFormats[x];
+
+ if (format->formatId == formatId)
+ return;
+ }
+
+ format = realloc(clipboard->clientFormats,
+ (clipboard->numClientFormats + 1) * sizeof(CLIPRDR_FORMAT));
+
+ if (!format)
+ return;
+
+ clipboard->clientFormats = format;
+ format = &clipboard->clientFormats[clipboard->numClientFormats++];
+ format->formatId = formatId;
+ format->formatName = NULL;
+
+ if (name && (formatId >= CF_MAX))
+ format->formatName = _strdup(name);
+}
+
+static BOOL wlf_cliprdr_add_client_format(wfClipboard* clipboard, const char* mime)
+{
+ WINPR_ASSERT(mime);
+ ClipboardLock(clipboard->system);
+ if (wlf_mime_is_html(mime))
+ {
+ UINT32 formatId = ClipboardGetFormatId(clipboard->system, type_HtmlFormat);
+ wfl_cliprdr_add_client_format_id(clipboard, formatId);
+ }
+ else if (wlf_mime_is_text(mime))
+ {
+ wfl_cliprdr_add_client_format_id(clipboard, CF_TEXT);
+ wfl_cliprdr_add_client_format_id(clipboard, CF_OEMTEXT);
+ wfl_cliprdr_add_client_format_id(clipboard, CF_UNICODETEXT);
+ }
+ else if (wlf_mime_is_image(mime))
+ {
+ for (size_t x = 0; x < ARRAYSIZE(mime_image); x++)
+ {
+ const char* mime_bmp = mime_image[x];
+ UINT32 formatId = ClipboardGetFormatId(clipboard->system, mime_bmp);
+ if (formatId != 0)
+ wfl_cliprdr_add_client_format_id(clipboard, formatId);
+ }
+ wfl_cliprdr_add_client_format_id(clipboard, CF_DIB);
+ wfl_cliprdr_add_client_format_id(clipboard, CF_TIFF);
+ }
+ else if (wlf_mime_is_file(mime))
+ {
+ const UINT32 fileFormatId =
+ ClipboardGetFormatId(clipboard->system, type_FileGroupDescriptorW);
+ wfl_cliprdr_add_client_format_id(clipboard, fileFormatId);
+ }
+
+ ClipboardUnlock(clipboard->system);
+ if (wlf_cliprdr_send_client_format_list(clipboard) != CHANNEL_RC_OK)
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT wlf_cliprdr_send_data_request(wfClipboard* clipboard, const wlf_request* rq)
+{
+ WINPR_ASSERT(rq);
+
+ CLIPRDR_FORMAT_DATA_REQUEST request = { .requestedFormatId = rq->responseFormat };
+
+ if (!Queue_Enqueue(clipboard->request_queue, rq))
+ return ERROR_INTERNAL_ERROR;
+
+ WINPR_ASSERT(clipboard);
+ WINPR_ASSERT(clipboard->context);
+ WINPR_ASSERT(clipboard->context->ClientFormatDataRequest);
+ return clipboard->context->ClientFormatDataRequest(clipboard->context, &request);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT wlf_cliprdr_send_data_response(wfClipboard* clipboard, const BYTE* data, size_t size)
+{
+ CLIPRDR_FORMAT_DATA_RESPONSE response = { 0 };
+
+ if (size > UINT32_MAX)
+ return ERROR_INVALID_PARAMETER;
+
+ response.common.msgFlags = (data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
+ response.common.dataLen = (UINT32)size;
+ response.requestedFormatData = data;
+
+ WINPR_ASSERT(clipboard);
+ WINPR_ASSERT(clipboard->context);
+ WINPR_ASSERT(clipboard->context->ClientFormatDataResponse);
+ return clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
+}
+
+BOOL wlf_cliprdr_handle_event(wfClipboard* clipboard, const UwacClipboardEvent* event)
+{
+ if (!clipboard || !event)
+ return FALSE;
+
+ if (!clipboard->context)
+ return TRUE;
+
+ switch (event->type)
+ {
+ case UWAC_EVENT_CLIPBOARD_AVAILABLE:
+ clipboard->seat = event->seat;
+ return TRUE;
+
+ case UWAC_EVENT_CLIPBOARD_OFFER:
+ WLog_Print(clipboard->log, WLOG_DEBUG, "client announces mime %s", event->mime);
+ return wlf_cliprdr_add_client_format(clipboard, event->mime);
+
+ case UWAC_EVENT_CLIPBOARD_SELECT:
+ WLog_Print(clipboard->log, WLOG_DEBUG, "client announces new data");
+ wlf_cliprdr_free_client_formats(clipboard);
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT wlf_cliprdr_send_client_capabilities(wfClipboard* clipboard)
+{
+ WINPR_ASSERT(clipboard);
+
+ CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet = {
+ .capabilitySetType = CB_CAPSTYPE_GENERAL,
+ .capabilitySetLength = 12,
+ .version = CB_CAPS_VERSION_2,
+ .generalFlags =
+ CB_USE_LONG_FORMAT_NAMES | cliprdr_file_context_current_flags(clipboard->file)
+ };
+ CLIPRDR_CAPABILITIES capabilities = { .cCapabilitiesSets = 1,
+ .capabilitySets =
+ (CLIPRDR_CAPABILITY_SET*)&(generalCapabilitySet) };
+
+ WINPR_ASSERT(clipboard);
+ WINPR_ASSERT(clipboard->context);
+ WINPR_ASSERT(clipboard->context->ClientCapabilities);
+ return clipboard->context->ClientCapabilities(clipboard->context, &capabilities);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT wlf_cliprdr_send_client_format_list_response(wfClipboard* clipboard, BOOL status)
+{
+ const CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse = {
+ .common.msgType = CB_FORMAT_LIST_RESPONSE,
+ .common.msgFlags = status ? CB_RESPONSE_OK : CB_RESPONSE_FAIL,
+ .common.dataLen = 0
+ };
+ WINPR_ASSERT(clipboard);
+ WINPR_ASSERT(clipboard->context);
+ WINPR_ASSERT(clipboard->context->ClientFormatListResponse);
+ return clipboard->context->ClientFormatListResponse(clipboard->context, &formatListResponse);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT wlf_cliprdr_monitor_ready(CliprdrClientContext* context,
+ const CLIPRDR_MONITOR_READY* monitorReady)
+{
+ UINT ret = 0;
+
+ WINPR_UNUSED(monitorReady);
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(monitorReady);
+
+ wfClipboard* clipboard = cliprdr_file_context_get_context(context->custom);
+ WINPR_ASSERT(clipboard);
+
+ if ((ret = wlf_cliprdr_send_client_capabilities(clipboard)) != CHANNEL_RC_OK)
+ return ret;
+
+ if ((ret = wlf_cliprdr_send_client_format_list(clipboard)) != CHANNEL_RC_OK)
+ return ret;
+
+ clipboard->sync = TRUE;
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT wlf_cliprdr_server_capabilities(CliprdrClientContext* context,
+ const CLIPRDR_CAPABILITIES* capabilities)
+{
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(capabilities);
+
+ const BYTE* capsPtr = (const BYTE*)capabilities->capabilitySets;
+ WINPR_ASSERT(capsPtr);
+
+ wfClipboard* clipboard = cliprdr_file_context_get_context(context->custom);
+ WINPR_ASSERT(clipboard);
+
+ if (!cliprdr_file_context_remote_set_flags(clipboard->file, 0))
+ return ERROR_INTERNAL_ERROR;
+
+ for (UINT32 i = 0; i < capabilities->cCapabilitiesSets; i++)
+ {
+ const CLIPRDR_CAPABILITY_SET* caps = (const CLIPRDR_CAPABILITY_SET*)capsPtr;
+
+ if (caps->capabilitySetType == CB_CAPSTYPE_GENERAL)
+ {
+ const CLIPRDR_GENERAL_CAPABILITY_SET* generalCaps =
+ (const CLIPRDR_GENERAL_CAPABILITY_SET*)caps;
+
+ if (!cliprdr_file_context_remote_set_flags(clipboard->file, generalCaps->generalFlags))
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ capsPtr += caps->capabilitySetLength;
+ }
+
+ return CHANNEL_RC_OK;
+}
+
+static UINT32 wlf_get_server_format_id(const wfClipboard* clipboard, const char* name)
+{
+ WINPR_ASSERT(clipboard);
+ WINPR_ASSERT(name);
+
+ for (UINT32 x = 0; x < clipboard->numServerFormats; x++)
+ {
+ const CLIPRDR_FORMAT* format = &clipboard->serverFormats[x];
+ if (!format->formatName)
+ continue;
+ if (strcmp(name, format->formatName) == 0)
+ return format->formatId;
+ }
+ return 0;
+}
+
+static const char* wlf_get_server_format_name(const wfClipboard* clipboard, UINT32 formatId)
+{
+ WINPR_ASSERT(clipboard);
+
+ for (UINT32 x = 0; x < clipboard->numServerFormats; x++)
+ {
+ const CLIPRDR_FORMAT* format = &clipboard->serverFormats[x];
+ if (format->formatId == formatId)
+ return format->formatName;
+ }
+ return NULL;
+}
+
+static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char* mime, int fd)
+{
+ wfClipboard* clipboard = (wfClipboard*)context;
+ WINPR_UNUSED(seat);
+
+ EnterCriticalSection(&clipboard->lock);
+
+ wlf_request request = { 0 };
+ if (wlf_mime_is_html(mime))
+ {
+ request.responseMime = mime_html;
+ request.responseFormat = wlf_get_server_format_id(clipboard, type_HtmlFormat);
+ }
+ else if (wlf_mime_is_file(mime))
+ {
+ request.responseMime = mime;
+ request.responseFormat = wlf_get_server_format_id(clipboard, type_FileGroupDescriptorW);
+ }
+ else if (wlf_mime_is_text(mime))
+ {
+ request.responseMime = mime_text_plain;
+ request.responseFormat = CF_UNICODETEXT;
+ }
+ else if (wlf_mime_is_image(mime))
+ {
+ request.responseMime = mime;
+ if (strcmp(mime, mime_tiff) == 0)
+ request.responseFormat = CF_TIFF;
+ else
+ request.responseFormat = CF_DIB;
+ }
+
+ if (request.responseMime != NULL)
+ {
+ request.responseFile = fdopen(fd, "w");
+
+ if (request.responseFile)
+ wlf_cliprdr_send_data_request(clipboard, &request);
+ else
+ WLog_Print(clipboard->log, WLOG_ERROR,
+ "failed to open clipboard file descriptor for MIME %s",
+ request.responseMime);
+ }
+
+ LeaveCriticalSection(&clipboard->lock);
+}
+
+static void wlf_cliprdr_cancel_data(UwacSeat* seat, void* context)
+{
+ wfClipboard* clipboard = (wfClipboard*)context;
+
+ WINPR_UNUSED(seat);
+ WINPR_ASSERT(clipboard);
+ cliprdr_file_context_clear(clipboard->file);
+}
+
+/**
+ * Called when the clipboard changes server side.
+ *
+ * Clear the local clipboard offer and replace it with a new one
+ * that announces the formats we get listed here.
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT wlf_cliprdr_server_format_list(CliprdrClientContext* context,
+ const CLIPRDR_FORMAT_LIST* formatList)
+{
+ BOOL html = FALSE;
+ BOOL text = FALSE;
+ BOOL image = FALSE;
+ BOOL file = FALSE;
+
+ if (!context || !context->custom)
+ return ERROR_INVALID_PARAMETER;
+
+ wfClipboard* clipboard = cliprdr_file_context_get_context(context->custom);
+ WINPR_ASSERT(clipboard);
+
+ wlf_cliprdr_free_server_formats(clipboard);
+ cliprdr_file_context_clear(clipboard->file);
+
+ if (!(clipboard->serverFormats =
+ (CLIPRDR_FORMAT*)calloc(formatList->numFormats, sizeof(CLIPRDR_FORMAT))))
+ {
+ WLog_Print(clipboard->log, WLOG_ERROR,
+ "failed to allocate %" PRIuz " CLIPRDR_FORMAT structs",
+ clipboard->numServerFormats);
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ clipboard->numServerFormats = formatList->numFormats;
+
+ if (!clipboard->seat)
+ {
+ WLog_Print(clipboard->log, WLOG_ERROR,
+ "clipboard->seat=NULL, check your client implementation");
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ for (UINT32 i = 0; i < formatList->numFormats; i++)
+ {
+ const CLIPRDR_FORMAT* format = &formatList->formats[i];
+ CLIPRDR_FORMAT* srvFormat = &clipboard->serverFormats[i];
+ srvFormat->formatId = format->formatId;
+
+ if (format->formatName)
+ {
+ srvFormat->formatName = _strdup(format->formatName);
+
+ if (!srvFormat->formatName)
+ {
+ wlf_cliprdr_free_server_formats(clipboard);
+ return CHANNEL_RC_NO_MEMORY;
+ }
+ }
+
+ if (format->formatName)
+ {
+ if (strcmp(format->formatName, type_HtmlFormat) == 0)
+ {
+ text = TRUE;
+ html = TRUE;
+ }
+ else if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0)
+ {
+ file = TRUE;
+ text = TRUE;
+ }
+ }
+ else
+ {
+ switch (format->formatId)
+ {
+ case CF_TEXT:
+ case CF_OEMTEXT:
+ case CF_UNICODETEXT:
+ text = TRUE;
+ break;
+
+ case CF_DIB:
+ image = TRUE;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ if (html)
+ {
+ UwacClipboardOfferCreate(clipboard->seat, mime_html);
+ }
+
+ if (file && cliprdr_file_context_has_local_support(clipboard->file))
+ {
+ UwacClipboardOfferCreate(clipboard->seat, mime_uri_list);
+ UwacClipboardOfferCreate(clipboard->seat, mime_gnome_copied_files);
+ UwacClipboardOfferCreate(clipboard->seat, mime_mate_copied_files);
+ }
+
+ if (text)
+ {
+ for (size_t x = 0; x < ARRAYSIZE(mime_text); x++)
+ UwacClipboardOfferCreate(clipboard->seat, mime_text[x]);
+ }
+
+ if (image)
+ {
+ for (size_t x = 0; x < ARRAYSIZE(mime_image); x++)
+ UwacClipboardOfferCreate(clipboard->seat, mime_image[x]);
+ }
+
+ UwacClipboardOfferAnnounce(clipboard->seat, clipboard, wlf_cliprdr_transfer_data,
+ wlf_cliprdr_cancel_data);
+ return wlf_cliprdr_send_client_format_list_response(clipboard, TRUE);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT
+wlf_cliprdr_server_format_list_response(CliprdrClientContext* context,
+ const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
+{
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(formatListResponse);
+
+ if (formatListResponse->common.msgFlags & CB_RESPONSE_FAIL)
+ WLog_WARN(TAG, "format list update failed");
+ return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT
+wlf_cliprdr_server_format_data_request(CliprdrClientContext* context,
+ const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
+{
+ UINT rc = CHANNEL_RC_OK;
+ BYTE* data = NULL;
+ size_t size = 0;
+ const char* mime = NULL;
+ UINT32 formatId = 0;
+ UINT32 localFormatId = 0;
+ wfClipboard* clipboard = 0;
+
+ UINT32 dsize = 0;
+ BYTE* ddata = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(formatDataRequest);
+
+ localFormatId = formatId = formatDataRequest->requestedFormatId;
+ clipboard = cliprdr_file_context_get_context(context->custom);
+ WINPR_ASSERT(clipboard);
+
+ ClipboardLock(clipboard->system);
+ const UINT32 fileFormatId = ClipboardGetFormatId(clipboard->system, type_FileGroupDescriptorW);
+ const UINT32 htmlFormatId = ClipboardGetFormatId(clipboard->system, type_HtmlFormat);
+
+ switch (formatId)
+ {
+ case CF_TEXT:
+ case CF_OEMTEXT:
+ case CF_UNICODETEXT:
+ localFormatId = ClipboardGetFormatId(clipboard->system, mime_text_plain);
+ mime = mime_text_utf8;
+ break;
+
+ case CF_DIB:
+ case CF_DIBV5:
+ mime = mime_bitmap[0];
+ break;
+
+ case CF_TIFF:
+ mime = mime_tiff;
+ break;
+
+ default:
+ if (formatId == fileFormatId)
+ {
+ localFormatId = ClipboardGetFormatId(clipboard->system, mime_uri_list);
+ mime = mime_uri_list;
+ }
+ else if (formatId == htmlFormatId)
+ {
+ localFormatId = ClipboardGetFormatId(clipboard->system, mime_html);
+ mime = mime_html;
+ }
+ else
+ goto fail;
+ break;
+ }
+
+ data = UwacClipboardDataGet(clipboard->seat, mime, &size);
+
+ if (!data)
+ goto fail;
+
+ if (fileFormatId == formatId)
+ {
+ if (!cliprdr_file_context_update_client_data(clipboard->file, data, size))
+ goto fail;
+ }
+
+ const BOOL res = ClipboardSetData(clipboard->system, localFormatId, data, size);
+ free(data);
+
+ UINT32 len = 0;
+ data = NULL;
+ if (res)
+ data = ClipboardGetData(clipboard->system, formatId, &len);
+
+ if (!res || !data)
+ goto fail;
+
+ if (fileFormatId == formatId)
+ {
+ const UINT32 flags = cliprdr_file_context_remote_get_flags(clipboard->file);
+ const UINT32 error = cliprdr_serialize_file_list_ex(
+ flags, (const FILEDESCRIPTORW*)data, len / sizeof(FILEDESCRIPTORW), &ddata, &dsize);
+ if (error)
+ goto fail;
+ }
+fail:
+ ClipboardUnlock(clipboard->system);
+ rc = wlf_cliprdr_send_data_response(clipboard, ddata, dsize);
+ free(data);
+ return rc;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT
+wlf_cliprdr_server_format_data_response(CliprdrClientContext* context,
+ const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
+{
+ UINT rc = ERROR_INTERNAL_ERROR;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(formatDataResponse);
+
+ const UINT32 size = formatDataResponse->common.dataLen;
+ const BYTE* data = formatDataResponse->requestedFormatData;
+
+ wfClipboard* clipboard = cliprdr_file_context_get_context(context->custom);
+ WINPR_ASSERT(clipboard);
+
+ wlf_request* request = Queue_Dequeue(clipboard->request_queue);
+ if (!request)
+ goto fail;
+
+ rc = CHANNEL_RC_OK;
+ if (formatDataResponse->common.msgFlags & CB_RESPONSE_FAIL)
+ {
+ WLog_WARN(TAG, "clipboard data request for format %" PRIu32 " [%s], mime %s failed",
+ request->responseFormat, ClipboardGetFormatIdString(request->responseFormat),
+ request->responseMime);
+ goto fail;
+ }
+ rc = ERROR_INTERNAL_ERROR;
+
+ ClipboardLock(clipboard->system);
+ EnterCriticalSection(&clipboard->lock);
+
+ UINT32 srcFormatId = 0;
+ UINT32 dstFormatId = 0;
+ switch (request->responseFormat)
+ {
+ case CF_TEXT:
+ case CF_OEMTEXT:
+ case CF_UNICODETEXT:
+ srcFormatId = request->responseFormat;
+ dstFormatId = ClipboardGetFormatId(clipboard->system, request->responseMime);
+ break;
+
+ case CF_DIB:
+ case CF_DIBV5:
+ srcFormatId = request->responseFormat;
+ dstFormatId = ClipboardGetFormatId(clipboard->system, request->responseMime);
+ break;
+
+ default:
+ {
+ const char* name = wlf_get_server_format_name(clipboard, request->responseFormat);
+ if (name)
+ {
+ if (strcmp(type_FileGroupDescriptorW, name) == 0)
+ {
+ srcFormatId =
+ ClipboardGetFormatId(clipboard->system, type_FileGroupDescriptorW);
+ dstFormatId = ClipboardGetFormatId(clipboard->system, request->responseMime);
+
+ if (!cliprdr_file_context_update_server_data(clipboard->file, clipboard->system,
+ data, size))
+ goto unlock;
+ }
+ else if (strcmp(type_HtmlFormat, name) == 0)
+ {
+ srcFormatId = ClipboardGetFormatId(clipboard->system, type_HtmlFormat);
+ dstFormatId = ClipboardGetFormatId(clipboard->system, request->responseMime);
+ }
+ }
+ }
+ break;
+ }
+
+ UINT32 len = 0;
+
+ const BOOL sres = ClipboardSetData(clipboard->system, srcFormatId, data, size);
+ if (sres)
+ data = ClipboardGetData(clipboard->system, dstFormatId, &len);
+
+ if (!sres || !data)
+ goto unlock;
+
+ if (request->responseFile)
+ {
+ const size_t res = fwrite(data, 1, len, request->responseFile);
+ if (res == len)
+ rc = CHANNEL_RC_OK;
+ }
+ else
+ rc = CHANNEL_RC_OK;
+
+unlock:
+ ClipboardUnlock(clipboard->system);
+ LeaveCriticalSection(&clipboard->lock);
+fail:
+ wlf_request_free(request);
+ return rc;
+}
+
+wfClipboard* wlf_clipboard_new(wlfContext* wfc)
+{
+ rdpChannels* channels = NULL;
+ wfClipboard* clipboard = NULL;
+
+ WINPR_ASSERT(wfc);
+
+ clipboard = (wfClipboard*)calloc(1, sizeof(wfClipboard));
+
+ if (!clipboard)
+ goto fail;
+
+ InitializeCriticalSection(&clipboard->lock);
+ clipboard->wfc = wfc;
+ channels = wfc->common.context.channels;
+ clipboard->log = WLog_Get(TAG);
+ clipboard->channels = channels;
+ clipboard->system = ClipboardCreate();
+ if (!clipboard->system)
+ goto fail;
+
+ clipboard->file = cliprdr_file_context_new(clipboard);
+ if (!clipboard->file)
+ goto fail;
+
+ if (!cliprdr_file_context_set_locally_available(clipboard->file, TRUE))
+ goto fail;
+
+ clipboard->request_queue = Queue_New(TRUE, -1, -1);
+ if (!clipboard->request_queue)
+ goto fail;
+
+ wObject* obj = Queue_Object(clipboard->request_queue);
+ WINPR_ASSERT(obj);
+ obj->fnObjectFree = wlf_request_free;
+ obj->fnObjectNew = wlf_request_clone;
+
+ return clipboard;
+
+fail:
+ wlf_clipboard_free(clipboard);
+ return NULL;
+}
+
+void wlf_clipboard_free(wfClipboard* clipboard)
+{
+ if (!clipboard)
+ return;
+
+ cliprdr_file_context_free(clipboard->file);
+
+ wlf_cliprdr_free_server_formats(clipboard);
+ wlf_cliprdr_free_client_formats(clipboard);
+ ClipboardDestroy(clipboard->system);
+
+ EnterCriticalSection(&clipboard->lock);
+
+ Queue_Free(clipboard->request_queue);
+ LeaveCriticalSection(&clipboard->lock);
+ DeleteCriticalSection(&clipboard->lock);
+ free(clipboard);
+}
+
+BOOL wlf_cliprdr_init(wfClipboard* clipboard, CliprdrClientContext* cliprdr)
+{
+ WINPR_ASSERT(clipboard);
+ WINPR_ASSERT(cliprdr);
+
+ clipboard->context = cliprdr;
+ cliprdr->MonitorReady = wlf_cliprdr_monitor_ready;
+ cliprdr->ServerCapabilities = wlf_cliprdr_server_capabilities;
+ cliprdr->ServerFormatList = wlf_cliprdr_server_format_list;
+ cliprdr->ServerFormatListResponse = wlf_cliprdr_server_format_list_response;
+ cliprdr->ServerFormatDataRequest = wlf_cliprdr_server_format_data_request;
+ cliprdr->ServerFormatDataResponse = wlf_cliprdr_server_format_data_response;
+
+ return cliprdr_file_context_init(clipboard->file, cliprdr);
+}
+
+BOOL wlf_cliprdr_uninit(wfClipboard* clipboard, CliprdrClientContext* cliprdr)
+{
+ WINPR_ASSERT(clipboard);
+ if (!cliprdr_file_context_uninit(clipboard->file, cliprdr))
+ return FALSE;
+
+ if (cliprdr)
+ cliprdr->custom = NULL;
+
+ return TRUE;
+}