[gnome-remote-desktop] clipboard-rdp: Implement clipboard data locking
- From: Jonas Ådahl <jadahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-remote-desktop] clipboard-rdp: Implement clipboard data locking
- Date: Fri, 30 Apr 2021 15:15:57 +0000 (UTC)
commit 7d92aad2148de217f6ec9f981c3a93880494e4c8
Author: Pascal Nowack <Pascal Nowack gmx de>
Date: Tue Feb 9 08:23:07 2021 +0100
clipboard-rdp: Implement clipboard data locking
Normally, when copying file content via the clipboard in RDP, the file
stream data exists as long as the same file list exists on the other
peers side.
This has the effect that copying new content to the clipboard aborts
the transfer of files that are currently copied via the clipboard to
the other peer.
Clipboard data locking is a mechanism that allows a peer to assign a
clipDataId to the current file list.
This operation is called "locking the clipboard data".
With this "lock" in place, the peer, which received the lock MUST
retain the file stream data (aka the uri-list) for this clipDataId.
When now a FileContentsRequest happens, the requesting peer now also
attaches the clipDataId to the request, which then the other peer looks
up.
When the clipboard content changes, the peer with the lock still knows
now which file list is meant and can therefore still provide file data,
although the new clipboard content might not even have a file list any
more.
Once all operations are done, the peer, which emitted the lock, can
request an unlock to free the clipDataId and the associated file list.
The cliprdr documentation mentions that a lock or an unlock can happen
at any time, as soon as the initialization sequence is complete.
However, the relationship between clipDataId and file list MUST be
bijective.
This means that a peer is not allowed to request multiple locks for the
same file list.
It is also not allowed to try to emit a lock for clipDataId that is
already in use.
While the cliprdr documentation shows the example of using the locking
mechanism before requsting the file list via a FormatDataRequest, it
is not the behaviour that mstsc has.
When locking is advertised, mstsc (, which supports locking,) always
unconditionally emit a lock as soon as it receives a new FormatList.
This lock is emitted before sending the FormatListResponse.
While a clipDataId can only be used in conjunction with a file list,
this behaviour is a little bit overkill, but not forbidden.
As soon as mstsc sent the lock and the FormatListResponse, it will try
to emit an unlock for the previous clipDataId.
If a pending associated file operation is still happening, the unlock
will happen as soon as the operation finishes.
To implement the locking mechanism, gnome-remote-desktop needs to be
able to track these clipDataIds.
Each clipDataId in g-r-d will have a bijective relation to a
ClipDataEntry.
This entry, will track the clipDataId, the associated WinPR clipboard,
since the WinPR clipboard only works with one file list, a boolean
value to track, whether file operations are allowed (FormatListResponse
with CB_RESPONSE_OK flag received) and whether the file list is
available in the WinPR clipboard (Client requested the file list via a
FormatDataRequest before).
ClipDataEntries are managed with two HashTables: one (serial, entry)-
HashTable and one (clipDataId, entry)-HashTable.
The former is used by the main thread to determine the current
ClipDataEntry (if it exists) to allow the entry to request file
contents, to set the state of having a file list or to set the
independence state of the associated WinPR clipboard.
The latter is used by the cliprdr thread for FileContentsRequests.
Upon creating and destroying a ClipDataEntry both HashTables are
synchronized.
This means: If exists (serial, entry), then exists (clipDataId, entry)
too.
And: If exists (clipDataId, entry), then exists (serial, entry) too.
For the usage of the ClipDataEntry no mutex is required, because the
only actions that the main thread does is setting the boolean values
of the independence state (upon sending a new FormatList), the
allowance state (of FileContentsRequests) and the has_file_list state.
This allows the cliprdr thread to handle FileContentsRequests without
depending on the main thread (waiting on a mutex to be released).
Each FormatList, that the server sends, will create a new serial for
the clipboard.
If an entry with that serial exists, the associated WinPR clipboard,
becomes independent from the main one, since the main clipboard will
be recreated.
Upon destroying a ClipDataEntry, the WinPR clipboard will only be
destroyed, when it is independent from the main WinPR clipboard.
While this locking behaviour in theory allows new copy and paste
actions, while a file transfer operation is happening, the reality is
different.
Microsofts locking implementation is poorly done: Locking with mstsc
only works, if the clipboard owner doesn't change.
For this commit specifically, this means that if a server-to-client
transfer is happening and a copy operation on the client side happens,
mstsc will still abort the transfer although g-r-d still has the data
available.
The abort happens on the client side. gnome-remote-desktop cannot
prevent this behaviour.
As soon as FreeRDP implements locking on its side, we can expect to
have a proper implementation, that does not abort the current transfer,
when the clipboard owner changes.
src/grd-clipboard-rdp.c | 400 ++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 384 insertions(+), 16 deletions(-)
---
diff --git a/src/grd-clipboard-rdp.c b/src/grd-clipboard-rdp.c
index b00aed9..2c62ad8 100644
--- a/src/grd-clipboard-rdp.c
+++ b/src/grd-clipboard-rdp.c
@@ -46,6 +46,19 @@ typedef struct _ServerFormatDataRequestContext
gboolean needs_conversion;
} ServerFormatDataRequestContext;
+typedef struct _ClipDataEntry
+{
+ uint32_t id;
+
+ wClipboard *system;
+ wClipboardDelegate *delegate;
+ uint64_t serial;
+
+ gboolean is_independent;
+ gboolean has_file_list;
+ gboolean requests_allowed;
+} ClipDataEntry;
+
typedef struct _FormatData
{
uint8_t *data;
@@ -61,6 +74,8 @@ struct _GrdClipboardRdp
wClipboard *system;
wClipboardDelegate *delegate;
+ gboolean has_file_list;
+ uint64_t serial;
GHashTable *allowed_server_formats;
GList *pending_server_formats;
@@ -68,6 +83,19 @@ struct _GrdClipboardRdp
gboolean server_file_contents_requests_allowed;
uint16_t format_list_response_msg_flags;
+ GHashTable *serial_entry_table;
+ GHashTable *clip_data_table;
+ struct
+ {
+ ClipDataEntry *entry;
+ ClipDataEntry *entry_to_replace;
+ gboolean serial_already_in_use;
+ } clipboard_retrieval_context;
+ struct
+ {
+ ClipDataEntry *entry;
+ } clipboard_destruction_context;
+
char *fuse_mount_path;
GrdRdpFuseClipboard *rdp_fuse_clipboard;
GHashTable *format_data_cache;
@@ -76,12 +104,16 @@ struct _GrdClipboardRdp
HANDLE format_data_received_event;
CLIPRDR_FORMAT_DATA_RESPONSE *format_data_response;
+ HANDLE clip_data_entry_event;
+ HANDLE completed_clip_data_entry_event;
HANDLE completed_format_list_event;
HANDLE completed_format_data_request_event;
HANDLE format_list_received_event;
HANDLE format_list_response_received_event;
HANDLE format_data_request_received_event;
unsigned int pending_server_formats_drop_id;
+ unsigned int clipboard_retrieval_id;
+ unsigned int clipboard_destruction_id;
unsigned int server_format_list_update_id;
unsigned int server_format_data_request_id;
unsigned int client_format_list_response_id;
@@ -89,6 +121,15 @@ struct _GrdClipboardRdp
G_DEFINE_TYPE (GrdClipboardRdp, grd_clipboard_rdp, GRD_TYPE_CLIPBOARD);
+static gboolean
+retrieve_current_clipboard (gpointer user_data);
+
+static gboolean
+handle_clip_data_entry_destruction (gpointer user_data);
+
+static void
+create_new_winpr_clipboard (GrdClipboardRdp *clipboard_rdp);
+
static void
update_allowed_server_formats (GrdClipboardRdp *clipboard_rdp,
gboolean received_response)
@@ -104,7 +145,16 @@ update_allowed_server_formats (GrdClipboardRdp *clipboard_rdp,
for (l = clipboard_rdp->pending_server_formats; l; l = l->next)
{
if (GPOINTER_TO_UINT (l->data) == GRD_MIME_TYPE_TEXT_URILIST)
- clipboard_rdp->server_file_contents_requests_allowed = TRUE;
+ {
+ ClipDataEntry *entry;
+
+ if (g_hash_table_lookup_extended (clipboard_rdp->serial_entry_table,
+ GUINT_TO_POINTER (clipboard_rdp->serial),
+ NULL, (gpointer *) &entry))
+ entry->requests_allowed = TRUE;
+
+ clipboard_rdp->server_file_contents_requests_allowed = TRUE;
+ }
g_hash_table_add (clipboard_rdp->allowed_server_formats, l->data);
}
@@ -174,6 +224,17 @@ remove_duplicated_clipboard_mime_types (GrdClipboardRdp *clipboard_rdp,
}
}
+static void
+update_clipboard_serial (GrdClipboardRdp *clipboard_rdp)
+{
+ ++clipboard_rdp->serial;
+ while (g_hash_table_contains (clipboard_rdp->serial_entry_table,
+ GUINT_TO_POINTER (clipboard_rdp->serial)))
+ ++clipboard_rdp->serial;
+
+ g_debug ("[RDP.CLIPRDR] Updated clipboard serial to %lu", clipboard_rdp->serial);
+}
+
static void
send_mime_type_list (GrdClipboardRdp *clipboard_rdp,
GList *mime_type_list)
@@ -182,11 +243,24 @@ send_mime_type_list (GrdClipboardRdp *clipboard_rdp,
CliprdrServerContext *cliprdr_context = clipboard_rdp->cliprdr_context;
CLIPRDR_FORMAT_LIST format_list = {0};
CLIPRDR_FORMAT *cliprdr_formats;
+ ClipDataEntry *entry;
GrdMimeType mime_type;
uint32_t n_formats;
uint32_t i;
GList *l;
+ if (g_hash_table_lookup_extended (clipboard_rdp->serial_entry_table,
+ GUINT_TO_POINTER (clipboard_rdp->serial),
+ NULL, (gpointer *) &entry))
+ {
+ g_debug ("[RDP.CLIPRDR] ClipDataEntry with id %u and serial %lu is now "
+ "independent", entry->id, entry->serial);
+ entry->is_independent = TRUE;
+
+ create_new_winpr_clipboard (clipboard_rdp);
+ }
+ update_clipboard_serial (clipboard_rdp);
+
n_formats = g_list_length (mime_type_list);
cliprdr_formats = g_malloc0 (n_formats * sizeof (CLIPRDR_FORMAT));
for (i = 0, l = mime_type_list; i < n_formats; ++i, l = l->next)
@@ -315,7 +389,7 @@ get_format_data_response (GrdClipboardRdp *clipboard_rdp,
CliprdrServerContext *cliprdr_context = clipboard_rdp->cliprdr_context;
CLIPRDR_FORMAT_DATA_RESPONSE *format_data_response = NULL;
HANDLE format_data_received_event = clipboard_rdp->format_data_received_event;
- HANDLE events[3];
+ HANDLE events[4];
uint32_t error;
ResetEvent (format_data_received_event);
@@ -327,10 +401,31 @@ get_format_data_response (GrdClipboardRdp *clipboard_rdp,
events[0] = clipboard_rdp->stop_event;
events[1] = format_data_received_event;
events[2] = clipboard_rdp->format_list_received_event;
- if (WaitForMultipleObjects (3, events, FALSE, MAX_WAIT_TIME) == WAIT_TIMEOUT)
+ events[3] = clipboard_rdp->clip_data_entry_event;
+ while (TRUE)
{
- g_warning ("[RDP.CLIPRDR] Possible protocol violation: Client did not "
- "send format data response (Timeout reached)");
+ if (WaitForMultipleObjects (4, events, FALSE, MAX_WAIT_TIME) == WAIT_TIMEOUT)
+ {
+ g_warning ("[RDP.CLIPRDR] Possible protocol violation: Client did not "
+ "send format data response (Timeout reached)");
+ break;
+ }
+
+ if (WaitForSingleObject (clipboard_rdp->clip_data_entry_event, 0) == WAIT_TIMEOUT)
+ break;
+
+ if (clipboard_rdp->clipboard_retrieval_id)
+ {
+ g_clear_handle_id (&clipboard_rdp->clipboard_retrieval_id,
+ g_source_remove);
+ retrieve_current_clipboard (clipboard_rdp);
+ }
+ if (clipboard_rdp->clipboard_destruction_id)
+ {
+ g_clear_handle_id (&clipboard_rdp->clipboard_destruction_id,
+ g_source_remove);
+ handle_clip_data_entry_destruction (clipboard_rdp);
+ }
}
if (WaitForSingleObject (format_data_received_event, 0) == WAIT_OBJECT_0)
@@ -1014,6 +1109,198 @@ cliprdr_client_format_list_response (CliprdrServerContext *cliprdr
return CHANNEL_RC_OK;
}
+static gboolean
+retrieve_current_clipboard (gpointer user_data)
+{
+ GrdClipboardRdp *clipboard_rdp = user_data;
+ ClipDataEntry *entry = clipboard_rdp->clipboard_retrieval_context.entry;
+ ClipDataEntry *entry_to_replace;
+ uint64_t serial = clipboard_rdp->serial;
+ gboolean serial_already_in_use;
+
+ g_debug ("[RDP.CLIPRDR] Tracking serial %lu for ClipDataEntry",
+ clipboard_rdp->serial);
+
+ entry_to_replace = clipboard_rdp->clipboard_retrieval_context.entry_to_replace;
+ if (entry_to_replace)
+ {
+ g_hash_table_remove (clipboard_rdp->serial_entry_table,
+ GUINT_TO_POINTER (entry_to_replace->serial));
+ }
+
+ serial_already_in_use = g_hash_table_contains (clipboard_rdp->serial_entry_table,
+ GUINT_TO_POINTER (serial));
+ if (!serial_already_in_use)
+ {
+ g_hash_table_insert (clipboard_rdp->serial_entry_table,
+ GUINT_TO_POINTER (serial), entry);
+ }
+ clipboard_rdp->clipboard_retrieval_context.serial_already_in_use =
+ serial_already_in_use;
+
+ entry->serial = serial;
+ entry->has_file_list = clipboard_rdp->has_file_list;
+ entry->requests_allowed = clipboard_rdp->server_file_contents_requests_allowed;
+
+ entry->system = clipboard_rdp->system;
+ entry->delegate = clipboard_rdp->delegate;
+
+ WaitForSingleObject (clipboard_rdp->clip_data_entry_event, INFINITE);
+ clipboard_rdp->clipboard_retrieval_id = 0;
+ ResetEvent (clipboard_rdp->clip_data_entry_event);
+ SetEvent (clipboard_rdp->completed_clip_data_entry_event);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+is_clip_data_entry_user_of_serial (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ ClipDataEntry *entry = value;
+ uint64_t serial = GPOINTER_TO_UINT (user_data);
+
+ return entry->serial == serial;
+}
+
+/**
+ * Client requests us to retain all file stream data on the clipboard
+ */
+static uint32_t
+cliprdr_client_lock_clipboard_data (CliprdrServerContext *cliprdr_context,
+ const CLIPRDR_LOCK_CLIPBOARD_DATA *lock_clipboard_data)
+{
+ GrdClipboardRdp *clipboard_rdp = cliprdr_context->custom;
+ uint32_t clip_data_id = lock_clipboard_data->clipDataId;
+ ClipDataEntry *entry;
+ HANDLE events[2];
+
+ if (WaitForSingleObject (clipboard_rdp->stop_event, 0) == WAIT_OBJECT_0)
+ return CHANNEL_RC_OK;
+
+ g_debug ("[RDP.CLIPRDR] Client requested a lock with clipDataId: %u",
+ clip_data_id);
+
+ clipboard_rdp->clipboard_retrieval_context.entry_to_replace = NULL;
+ if (g_hash_table_lookup_extended (clipboard_rdp->clip_data_table,
+ GUINT_TO_POINTER (clip_data_id),
+ NULL, (gpointer *) &entry))
+ {
+ g_warning ("[RDP.CLIPRDR] Protocol violation: Client requested a lock with"
+ " an existing clipDataId %u. Replacing existing ClipDataEntry",
+ clip_data_id);
+
+ clipboard_rdp->clipboard_retrieval_context.entry_to_replace = entry;
+ }
+
+ ResetEvent (clipboard_rdp->completed_clip_data_entry_event);
+ entry = g_malloc0 (sizeof (ClipDataEntry));
+ clipboard_rdp->clipboard_retrieval_context.entry = entry;
+
+ clipboard_rdp->clipboard_retrieval_id =
+ g_idle_add (retrieve_current_clipboard, clipboard_rdp);
+ SetEvent (clipboard_rdp->clip_data_entry_event);
+
+ events[0] = clipboard_rdp->stop_event;
+ events[1] = clipboard_rdp->completed_clip_data_entry_event;
+ WaitForMultipleObjects (2, events, FALSE, INFINITE);
+
+ if (WaitForSingleObject (clipboard_rdp->stop_event, 0) == WAIT_OBJECT_0)
+ return CHANNEL_RC_OK;
+
+ if (clipboard_rdp->clipboard_retrieval_context.serial_already_in_use)
+ {
+ uint64_t serial = entry->serial;
+
+ g_warning ("[RDP.CLIPRDR] Protocol violation: Double lock detected ("
+ "Clipboard serial already in use). Replacing the clipDataId.");
+
+ g_free (entry);
+ entry = g_hash_table_find (clipboard_rdp->clip_data_table,
+ is_clip_data_entry_user_of_serial,
+ GUINT_TO_POINTER (serial));
+ g_hash_table_steal (clipboard_rdp->clip_data_table,
+ GUINT_TO_POINTER (entry->id));
+ }
+
+ entry->id = clip_data_id;
+
+ g_debug ("[RDP.CLIPRDR] Tracking lock with clipDataId %u", clip_data_id);
+ g_hash_table_insert (clipboard_rdp->clip_data_table,
+ GUINT_TO_POINTER (clip_data_id),
+ entry);
+
+ return CHANNEL_RC_OK;
+}
+
+static gboolean
+handle_clip_data_entry_destruction (gpointer user_data)
+{
+ GrdClipboardRdp *clipboard_rdp = user_data;
+ ClipDataEntry *entry;
+
+ entry = g_steal_pointer (&clipboard_rdp->clipboard_destruction_context.entry);
+
+ g_debug ("[RDP.CLIPRDR] Deleting ClipDataEntry with serial %lu", entry->serial);
+ g_hash_table_remove (clipboard_rdp->serial_entry_table,
+ GUINT_TO_POINTER (entry->serial));
+
+ WaitForSingleObject (clipboard_rdp->clip_data_entry_event, INFINITE);
+ clipboard_rdp->clipboard_destruction_id = 0;
+ ResetEvent (clipboard_rdp->clip_data_entry_event);
+ SetEvent (clipboard_rdp->completed_clip_data_entry_event);
+
+ return G_SOURCE_REMOVE;
+}
+
+/**
+ * Client informs us that the file stream data for a specific clip data id can
+ * now be released
+ */
+static uint32_t
+cliprdr_client_unlock_clipboard_data (CliprdrServerContext *cliprdr_context,
+ const CLIPRDR_UNLOCK_CLIPBOARD_DATA *unlock_clipboard_data)
+{
+ GrdClipboardRdp *clipboard_rdp = cliprdr_context->custom;
+ uint32_t clip_data_id = unlock_clipboard_data->clipDataId;
+ ClipDataEntry *entry;
+ HANDLE events[2];
+
+ if (WaitForSingleObject (clipboard_rdp->stop_event, 0) == WAIT_OBJECT_0)
+ return CHANNEL_RC_OK;
+
+ g_debug ("[RDP.CLIPRDR] Client requested an unlock with clipDataId: %u",
+ clip_data_id);
+ if (!g_hash_table_lookup_extended (clipboard_rdp->clip_data_table,
+ GUINT_TO_POINTER (clip_data_id),
+ NULL, (gpointer *) &entry))
+ {
+ g_warning ("[RDP.CLIPRDR] Protocol violation: ClipDataEntry with id %u "
+ "does not exist", clip_data_id);
+ return CHANNEL_RC_OK;
+ }
+
+ g_debug ("[RDP.CLIPRDR] Removing lock with clipDataId: %u", clip_data_id);
+
+ ResetEvent (clipboard_rdp->completed_clip_data_entry_event);
+ clipboard_rdp->clipboard_destruction_context.entry = entry;
+
+ clipboard_rdp->clipboard_destruction_id =
+ g_idle_add (handle_clip_data_entry_destruction, clipboard_rdp);
+ SetEvent (clipboard_rdp->clip_data_entry_event);
+
+ events[0] = clipboard_rdp->stop_event;
+ events[1] = clipboard_rdp->completed_clip_data_entry_event;
+ WaitForMultipleObjects (2, events, FALSE, INFINITE);
+
+ g_debug ("[RDP.CLIPRDR] Untracking lock with clipDataId %u", clip_data_id);
+ g_hash_table_remove (clipboard_rdp->clip_data_table,
+ GUINT_TO_POINTER (clip_data_id));
+
+ return CHANNEL_RC_OK;
+}
+
static void
#ifdef HAVE_FREERDP_2_3
serialize_file_list (FILEDESCRIPTORW *files,
@@ -1117,6 +1404,8 @@ request_server_format_data (gpointer user_data)
if (dst_data && mime_type == GRD_MIME_TYPE_TEXT_URILIST)
{
+ uint64_t serial = clipboard_rdp->serial;
+ ClipDataEntry *entry;
#ifdef HAVE_FREERDP_2_3
FILEDESCRIPTORW *files;
uint32_t n_files;
@@ -1136,6 +1425,12 @@ request_server_format_data (gpointer user_data)
serialize_file_list (files, n_files, &dst_data, &dst_size);
g_free (files);
+
+ clipboard_rdp->has_file_list = TRUE;
+ if (g_hash_table_lookup_extended (clipboard_rdp->serial_entry_table,
+ GUINT_TO_POINTER (serial),
+ NULL, (gpointer *) &entry))
+ entry->has_file_list = TRUE;
}
}
if (!success || !dst_data)
@@ -1355,14 +1650,40 @@ cliprdr_client_file_contents_request (CliprdrServerContext *clipr
const CLIPRDR_FILE_CONTENTS_REQUEST *file_contents_request)
{
GrdClipboardRdp *clipboard_rdp = cliprdr_context->custom;
+ wClipboardDelegate *delegate = clipboard_rdp->delegate;
+ gboolean has_file_list = clipboard_rdp->has_file_list;
+ gboolean requests_allowed = clipboard_rdp->server_file_contents_requests_allowed;
+ uint32_t clip_data_id = file_contents_request->clipDataId;
+ uint32_t stream_id = file_contents_request->streamId;
+ ClipDataEntry *entry = NULL;
uint32_t error = NO_ERROR;
- if (!clipboard_rdp->server_file_contents_requests_allowed)
+ if (file_contents_request->haveClipDataId)
+ g_debug ("[RDP.CLIPRDR] FileContentsRequest has clipDataId %u", clip_data_id);
+ else
+ g_debug ("[RDP.CLIPRDR] FileContentsRequest does not have a clipDataId");
+
+ if (file_contents_request->haveClipDataId)
{
- return send_file_contents_response_failure (cliprdr_context,
- file_contents_request->streamId);
+ if (!g_hash_table_lookup_extended (clipboard_rdp->clip_data_table,
+ GUINT_TO_POINTER (clip_data_id),
+ NULL, (gpointer *) &entry))
+ return send_file_contents_response_failure (cliprdr_context, stream_id);
+
+ if (!entry->requests_allowed)
+ {
+ g_debug ("[RDP.CLIPRDR] ClipDataEntry with id %u is not eligible of "
+ "requesting file contents.", clip_data_id);
+ }
+
+ delegate = entry->delegate;
+ has_file_list = entry->has_file_list;
+ requests_allowed = entry->requests_allowed;
}
+ if (!requests_allowed || !has_file_list)
+ return send_file_contents_response_failure (cliprdr_context, stream_id);
+
if (file_contents_request->dwFlags & FILECONTENTS_SIZE &&
file_contents_request->dwFlags & FILECONTENTS_RANGE)
{
@@ -1370,18 +1691,17 @@ cliprdr_client_file_contents_request (CliprdrServerContext *clipr
* FILECONTENTS_SIZE and FILECONTENTS_RANGE are not allowed
* to be set at the same time
*/
- return send_file_contents_response_failure (cliprdr_context,
- file_contents_request->streamId);
+ return send_file_contents_response_failure (cliprdr_context, stream_id);
}
if (file_contents_request->dwFlags & FILECONTENTS_SIZE)
{
- error = delegate_request_file_contents_size (clipboard_rdp->delegate,
+ error = delegate_request_file_contents_size (delegate,
file_contents_request);
}
else if (file_contents_request->dwFlags & FILECONTENTS_RANGE)
{
- error = delegate_request_file_contents_range (clipboard_rdp->delegate,
+ error = delegate_request_file_contents_range (delegate,
file_contents_request);
}
else
@@ -1390,10 +1710,7 @@ cliprdr_client_file_contents_request (CliprdrServerContext *clipr
}
if (error)
- {
- return send_file_contents_response_failure (cliprdr_context,
- file_contents_request->streamId);
- }
+ return send_file_contents_response_failure (cliprdr_context, stream_id);
return CHANNEL_RC_OK;
}
@@ -1478,6 +1795,23 @@ cliprdr_file_range_failure (wClipboardDelegate *delegate,
file_range_request->streamId);
}
+static void
+create_new_winpr_clipboard (GrdClipboardRdp *clipboard_rdp)
+{
+ g_debug ("[RDP.CLIPRDR] Creating new WinPR clipboard");
+
+ clipboard_rdp->system = ClipboardCreate ();
+ clipboard_rdp->delegate = ClipboardGetDelegate (clipboard_rdp->system);
+ clipboard_rdp->delegate->ClipboardFileSizeSuccess = cliprdr_file_size_success;
+ clipboard_rdp->delegate->ClipboardFileSizeFailure = cliprdr_file_size_failure;
+ clipboard_rdp->delegate->ClipboardFileRangeSuccess = cliprdr_file_range_success;
+ clipboard_rdp->delegate->ClipboardFileRangeFailure = cliprdr_file_range_failure;
+ clipboard_rdp->delegate->basePath = NULL;
+ clipboard_rdp->delegate->custom = clipboard_rdp;
+
+ clipboard_rdp->has_file_list = FALSE;
+}
+
GrdClipboardRdp *
grd_clipboard_rdp_new (GrdSessionRdp *session_rdp,
HANDLE vcm,
@@ -1514,6 +1848,8 @@ grd_clipboard_rdp_new (GrdSessionRdp *session_rdp,
cliprdr_context->ClientCapabilities = cliprdr_client_capabilities;
cliprdr_context->ClientFormatList = cliprdr_client_format_list;
cliprdr_context->ClientFormatListResponse = cliprdr_client_format_list_response;
+ cliprdr_context->ClientLockClipboardData = cliprdr_client_lock_clipboard_data;
+ cliprdr_context->ClientUnlockClipboardData = cliprdr_client_unlock_clipboard_data;
cliprdr_context->ClientFormatDataRequest = cliprdr_client_format_data_request;
cliprdr_context->ClientFormatDataResponse = cliprdr_client_format_data_response;
cliprdr_context->ClientFileContentsRequest = cliprdr_client_file_contents_request;
@@ -1562,6 +1898,9 @@ grd_clipboard_rdp_dispose (GObject *object)
g_free ((uint8_t *) clipboard_rdp->format_data_response->requestedFormatData);
g_clear_pointer (&clipboard_rdp->format_data_response, g_free);
+ if (clipboard_rdp->clipboard_retrieval_id)
+ g_clear_pointer (&clipboard_rdp->clipboard_retrieval_context.entry, g_free);
+
g_clear_pointer (&clipboard_rdp->queued_server_formats, g_list_free);
g_clear_pointer (&clipboard_rdp->pending_server_formats, g_list_free);
grd_rdp_fuse_clipboard_clear_selection (clipboard_rdp->rdp_fuse_clipboard);
@@ -1573,6 +1912,8 @@ grd_clipboard_rdp_dispose (GObject *object)
rmdir (clipboard_rdp->fuse_mount_path);
g_clear_pointer (&clipboard_rdp->fuse_mount_path, g_free);
g_clear_pointer (&clipboard_rdp->format_data_cache, g_hash_table_unref);
+ g_clear_pointer (&clipboard_rdp->clip_data_table, g_hash_table_destroy);
+ g_clear_pointer (&clipboard_rdp->serial_entry_table, g_hash_table_destroy);
g_clear_pointer (&clipboard_rdp->allowed_server_formats, g_hash_table_destroy);
g_clear_pointer (&clipboard_rdp->format_data_request_received_event,
CloseHandle);
@@ -1582,11 +1923,16 @@ grd_clipboard_rdp_dispose (GObject *object)
g_clear_pointer (&clipboard_rdp->completed_format_data_request_event,
CloseHandle);
g_clear_pointer (&clipboard_rdp->completed_format_list_event, CloseHandle);
+ g_clear_pointer (&clipboard_rdp->completed_clip_data_entry_event,
+ CloseHandle);
+ g_clear_pointer (&clipboard_rdp->clip_data_entry_event, CloseHandle);
g_clear_pointer (&clipboard_rdp->format_data_received_event, CloseHandle);
g_clear_pointer (&clipboard_rdp->system, ClipboardDestroy);
g_clear_pointer (&clipboard_rdp->cliprdr_context, cliprdr_server_context_free);
g_clear_handle_id (&clipboard_rdp->pending_server_formats_drop_id, g_source_remove);
+ g_clear_handle_id (&clipboard_rdp->clipboard_retrieval_id, g_source_remove);
+ g_clear_handle_id (&clipboard_rdp->clipboard_destruction_id, g_source_remove);
g_clear_handle_id (&clipboard_rdp->server_format_list_update_id, g_source_remove);
g_clear_handle_id (&clipboard_rdp->server_format_data_request_id, g_source_remove);
g_clear_handle_id (&clipboard_rdp->client_format_list_response_id, g_source_remove);
@@ -1594,6 +1940,21 @@ grd_clipboard_rdp_dispose (GObject *object)
G_OBJECT_CLASS (grd_clipboard_rdp_parent_class)->dispose (object);
}
+static void
+clip_data_entry_free (gpointer data)
+{
+ ClipDataEntry *entry = data;
+
+ g_debug ("[RDP.CLIPRDR] Freeing ClipDataEntry with id %u and serial %lu. "
+ "ClipDataEntry is independent: %s", entry->id, entry->serial,
+ entry->is_independent ? "true" : "false");
+
+ if (entry->is_independent)
+ g_clear_pointer (&entry->system, ClipboardDestroy);
+
+ g_free (entry);
+}
+
static void
grd_clipboard_rdp_init (GrdClipboardRdp *clipboard_rdp)
{
@@ -1630,6 +1991,10 @@ grd_clipboard_rdp_init (GrdClipboardRdp *clipboard_rdp)
clipboard_rdp->format_data_received_event =
CreateEvent (NULL, TRUE, FALSE, NULL);
+ clipboard_rdp->clip_data_entry_event =
+ CreateEvent (NULL, TRUE, FALSE, NULL);
+ clipboard_rdp->completed_clip_data_entry_event =
+ CreateEvent (NULL, TRUE, TRUE, NULL);
clipboard_rdp->completed_format_list_event =
CreateEvent (NULL, TRUE, TRUE, NULL);
clipboard_rdp->completed_format_data_request_event =
@@ -1642,6 +2007,9 @@ grd_clipboard_rdp_init (GrdClipboardRdp *clipboard_rdp)
CreateEvent (NULL, TRUE, FALSE, NULL);
clipboard_rdp->allowed_server_formats = g_hash_table_new (NULL, NULL);
+ clipboard_rdp->serial_entry_table = g_hash_table_new_full (NULL, NULL, NULL,
+ clip_data_entry_free);
+ clipboard_rdp->clip_data_table = g_hash_table_new (NULL, NULL);
clipboard_rdp->format_data_cache = g_hash_table_new (NULL, NULL);
clipboard_rdp->fuse_mount_path = g_steal_pointer (&template_path);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]