diff options
author | Antenore Gatta (tmow) <antenore@simbiosi.org> | 2021-10-06 01:05:44 +0300 |
---|---|---|
committer | Antenore Gatta (tmow) <antenore@simbiosi.org> | 2021-10-06 01:05:44 +0300 |
commit | 55bcc1ecb0fb46e43cabcc4be87aaf1e2cdfcfec (patch) | |
tree | 9758d4a878c74f6b08c1487f8edfb4c87a634f7a /plugins | |
parent | 8ec3eca55ac9640b06793944a468438bad4f8d1f (diff) | |
parent | 32bb0e4d16b0df2e4bf7b33a659e15f387fa29b6 (diff) |
Merge branch 'issue1951_mitigation' into 'master'
Mitigation for #1951 and extra RDP clibpoard debug messages
See merge request Remmina/Remmina!2323
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/rdp/rdp_cliprdr.c | 299 | ||||
-rw-r--r-- | plugins/rdp/rdp_cliprdr.h | 2 | ||||
-rw-r--r-- | plugins/rdp/rdp_event.c | 18 | ||||
-rw-r--r-- | plugins/rdp/rdp_plugin.c | 2 | ||||
-rw-r--r-- | plugins/rdp/rdp_plugin.h | 3 |
5 files changed, 215 insertions, 109 deletions
diff --git a/plugins/rdp/rdp_cliprdr.c b/plugins/rdp/rdp_cliprdr.c index d89cb228f..3458c2d82 100644 --- a/plugins/rdp/rdp_cliprdr.c +++ b/plugins/rdp/rdp_cliprdr.c @@ -69,6 +69,7 @@ UINT32 remmina_rdp_cliprdr_get_format_from_gdkatom(GdkAtom atom) return rc; } +/* Never used? */ void remmina_rdp_cliprdr_get_target_types(UINT32 **formats, UINT16 *size, GdkAtom *types, int count) { TRACE_CALL(__func__); @@ -141,11 +142,14 @@ static void crlf2lf(UINT8 *data, size_t *size) *size = out - data; } +/* Never used? */ int remmina_rdp_cliprdr_server_file_contents_request(CliprdrClientContext *context, CLIPRDR_FILE_CONTENTS_REQUEST *fileContentsRequest) { TRACE_CALL(__func__); return -1; } + +/* Never used? */ int remmina_rdp_cliprdr_server_file_contents_response(CliprdrClientContext *context, CLIPRDR_FILE_CONTENTS_RESPONSE *fileContentsResponse) { TRACE_CALL(__func__); @@ -195,7 +199,6 @@ static void remmina_rdp_cliprdr_send_client_capabilities(rfClipboard *clipboard) clipboard->context->ClientCapabilities(clipboard->context, &capabilities); } - static UINT remmina_rdp_cliprdr_monitor_ready(CliprdrClientContext *context, const CLIPRDR_MONITOR_READY *monitorReady) { TRACE_CALL(__func__); @@ -216,6 +219,25 @@ static UINT remmina_rdp_cliprdr_server_capabilities(CliprdrClientContext *contex } +void remmina_rdp_cliprdr_cached_clipboard_free(rfClipboard *clipboard) +{ + TRACE_CALL(__func__); + guint fmt; + + + if (clipboard->srv_data == NULL) + return; + + fmt = clipboard->format; + if (fmt == CB_FORMAT_PNG || fmt == CF_DIB || fmt == CF_DIBV5 || fmt == CB_FORMAT_JPEG) { + g_object_unref(clipboard->srv_data); + } else { + free(clipboard->srv_data); + } + clipboard->srv_data = NULL; +} + + static UINT remmina_rdp_cliprdr_server_format_list(CliprdrClientContext *context, const CLIPRDR_FORMAT_LIST *formatList) { TRACE_CALL(__func__); @@ -229,7 +251,7 @@ static UINT remmina_rdp_cliprdr_server_format_list(CliprdrClientContext *context rfClipboard *clipboard; CLIPRDR_FORMAT *format; CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse; - const char *serverFormatName; + UINT rc; int has_dib_level = 0; @@ -237,12 +259,25 @@ static UINT remmina_rdp_cliprdr_server_format_list(CliprdrClientContext *context clipboard = (rfClipboard *)context->custom; gp = clipboard->rfi->protocol_widget; + + REMMINA_PLUGIN_DEBUG("gp=%p: Received a new ServerFormatList from server clipboard. Remmina version = %s", + gp, VERSION); + + + GtkTargetList *list = gtk_target_list_new(NULL, 0); - REMMINA_PLUGIN_DEBUG("format list from the server:"); + if (clipboard->srv_clip_data_wait == SCDW_BUSY_WAIT) { + REMMINA_PLUGIN_DEBUG("gp=%p: we already have a FormatDataRequest in progress to the server, aborting", gp); + remmina_rdp_clipboard_abort_client_format_data_request(clipboard->rfi); + } + + remmina_rdp_cliprdr_cached_clipboard_free(clipboard); + + REMMINA_PLUGIN_DEBUG("gp=%p: format list from the server:", gp); for (i = 0; i < formatList->numFormats; i++) { format = &formatList->formats[i]; - serverFormatName = format->formatName; + const char *serverFormatName = format->formatName; if (format->formatId == CF_UNICODETEXT) { serverFormatName = "CF_UNICODETEXT"; GdkAtom atom = gdk_atom_intern("UTF8_STRING", TRUE); @@ -292,24 +327,45 @@ static UINT remmina_rdp_cliprdr_server_format_list(CliprdrClientContext *context gtk_target_list_add(list, atom, 0, CF_DIB); } - /* Now we tell GTK to change the local keyboard calling gtk_clipboard_set_with_owner + + REMMINA_PLUGIN_DEBUG("gp=%p: sending ClientFormatListResponse to server", gp); + formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE; + formatListResponse.msgFlags = CB_RESPONSE_OK; // Can be CB_RESPONSE_FAIL in case of error + formatListResponse.dataLen = 0; + rc = clipboard->context->ClientFormatListResponse(clipboard->context, &formatListResponse); + /* Schedule GTK event to tell GTK to change the local clipboard calling gtk_clipboard_set_with_owner * via REMMINA_RDP_UI_CLIPBOARD_SET_DATA - * GTK will immediately fire an "owner-change" event, that we should ignore */ + * GTK will immediately fire an "owner-change" event, that we should ignore. + * And if we are putting text on the clipboard, mutter or other clipboard + * managers may try immediately request a clipboard transfer. */ + + /* Ensure we have at least one target into list, or Gtk will not change owner + when setting clipboard owner later. + We put it directly in the clipboard cache (clipboard->srv_data), + so remmina will never ask it to the server via ClientFormatDataRequest */ + gint n_targets; + GtkTargetEntry *target_table = gtk_target_table_new_from_list(list, &n_targets); + if (target_table) + gtk_target_table_free(target_table, n_targets); + if (n_targets == 0) { + REMMINA_PLUGIN_DEBUG("gp=%p adding a dummy text target (empty text) for local clipboard, because we have no interesting targets from the server. Putting it in the local clipboard cache."); + GdkAtom atom = gdk_atom_intern("UTF8_STRING", TRUE); + gtk_target_list_add(list, atom, 0, CF_UNICODETEXT); + clipboard->srv_data = malloc(1); + ((char *)(clipboard->srv_data))[0] = 0; + clipboard->format = CF_UNICODETEXT; + } + ui = g_new0(RemminaPluginRdpUiObject, 1); ui->type = REMMINA_RDP_UI_CLIPBOARD; ui->clipboard.clipboard = clipboard; ui->clipboard.type = REMMINA_RDP_UI_CLIPBOARD_SET_DATA; ui->clipboard.targetlist = list; - remmina_rdp_event_queue_ui_sync_retint(gp, ui); - - /* Send FormatListResponse to server */ + remmina_rdp_event_queue_ui_async(gp, ui); - formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE; - formatListResponse.msgFlags = CB_RESPONSE_OK; // Can be CB_RESPONSE_FAIL in case of error - formatListResponse.dataLen = 0; - - return clipboard->context->ClientFormatListResponse(clipboard->context, &formatListResponse); + REMMINA_PLUGIN_DEBUG("gp=%p: processing of ServerFormatList ended, returning rc=%u to libfreerdp", gp, rc); + return rc; } static UINT remmina_rdp_cliprdr_server_format_list_response(CliprdrClientContext *context, const CLIPRDR_FORMAT_LIST_RESPONSE *formatListResponse) @@ -318,7 +374,6 @@ static UINT remmina_rdp_cliprdr_server_format_list_response(CliprdrClientContext return CHANNEL_RC_OK; } - static UINT remmina_rdp_cliprdr_server_format_data_request(CliprdrClientContext *context, const CLIPRDR_FORMAT_DATA_REQUEST *formatDataRequest) { TRACE_CALL(__func__); @@ -340,6 +395,16 @@ static UINT remmina_rdp_cliprdr_server_format_data_request(CliprdrClientContext return CHANNEL_RC_OK; } +int timeval_diff(struct timeval *start, struct timeval *end) +{ + /* Returns the time elapsed from start to end, in ms */ + int64_t ms_end = (end->tv_sec * 1000) + (end->tv_usec / 1000); + int64_t ms_start = (start->tv_sec * 1000) + (start->tv_usec / 1000); + + return (int)(ms_end - ms_start); +} + + static UINT remmina_rdp_cliprdr_server_format_data_response(CliprdrClientContext *context, const CLIPRDR_FORMAT_DATA_RESPONSE *formatDataResponse) { TRACE_CALL(__func__); @@ -349,8 +414,9 @@ static UINT remmina_rdp_cliprdr_server_format_data_response(CliprdrClientContext rfContext *rfi; RemminaProtocolWidget *gp; rfClipboard *clipboard; - GdkPixbufLoader *pixbuf; gpointer output = NULL; + struct timeval now; + int mstrans; clipboard = (rfClipboard *)context->custom; gp = clipboard->rfi->protocol_widget; @@ -359,9 +425,14 @@ static UINT remmina_rdp_cliprdr_server_format_data_response(CliprdrClientContext data = formatDataResponse->requestedFormatData; size = formatDataResponse->dataLen; - // formatDataResponse->requestedFormatData is allocated - // by freerdp and freed after returning from this callback function. - // So we must make a copy if we need to preserve it + REMMINA_PLUGIN_DEBUG("gp=%p server FormatDataResponse received: clipboard data arrived form server.", gp); + gettimeofday(&now, NULL); + remmina_rdp_cliprdr_cached_clipboard_free(clipboard); + + /* Calculate stats */ + mstrans = timeval_diff(&(clipboard->clientformatdatarequest_tv), &now); + REMMINA_PLUGIN_DEBUG("gp=%p %zu bytes transferred from server in %d ms. Speed is %d bytes/sec", + gp, (size_t)size, mstrans, (int)((int64_t)size * 1000 / mstrans)); if (size > 0) { switch (rfi->clipboard.format) { @@ -419,59 +490,66 @@ static UINT remmina_rdp_cliprdr_server_format_data_response(CliprdrClientContext data = Stream_Buffer(s); size = Stream_Length(s); - pixbuf = gdk_pixbuf_loader_new(); + GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); perr = NULL; - if (!gdk_pixbuf_loader_write(pixbuf, data, size, &perr)) { + if (!gdk_pixbuf_loader_write(loader, data, size, &perr)) { Stream_Free(s, TRUE); g_warning("[RDP] rdp_cliprdr: gdk_pixbuf_loader_write() returned error %s\n", perr->message); } else { - if (!gdk_pixbuf_loader_close(pixbuf, &perr)) { + if (!gdk_pixbuf_loader_close(loader, &perr)) { g_warning("[RDP] rdp_cliprdr: gdk_pixbuf_loader_close() returned error %s\n", perr->message); perr = NULL; } Stream_Free(s, TRUE); - output = g_object_ref(gdk_pixbuf_loader_get_pixbuf(pixbuf)); + output = g_object_ref(gdk_pixbuf_loader_get_pixbuf(loader)); } - g_object_unref(pixbuf); + g_object_unref(loader); break; } case CB_FORMAT_PNG: case CB_FORMAT_JPEG: { - pixbuf = gdk_pixbuf_loader_new(); - gdk_pixbuf_loader_write(pixbuf, data, size, NULL); - output = g_object_ref(gdk_pixbuf_loader_get_pixbuf(pixbuf)); - gdk_pixbuf_loader_close(pixbuf, NULL); - g_object_unref(pixbuf); + GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); + gdk_pixbuf_loader_write(loader, data, size, NULL); + output = g_object_ref(gdk_pixbuf_loader_get_pixbuf(loader)); + gdk_pixbuf_loader_close(loader, NULL); + g_object_unref(loader); break; } } } - REMMINA_PLUGIN_DEBUG("clibpoard data arrived form server, signalling main GTK thread that we have some data."); + clipboard->srv_data = output; + if (output != NULL) + REMMINA_PLUGIN_DEBUG("gp=%p: clipboard local cache data has been loaded", gp); + else + REMMINA_PLUGIN_DEBUG("gp=%p: data from server is not valid (size=%zu format=%d), cannot load into cache", gp, size, rfi->clipboard.format); + + + REMMINA_PLUGIN_DEBUG("gp=%p: signalling main GTK thread that we have some clipboard data.", gp); pthread_mutex_lock(&clipboard->transfer_clip_mutex); pthread_cond_signal(&clipboard->transfer_clip_cond); if (clipboard->srv_clip_data_wait == SCDW_BUSY_WAIT) { - REMMINA_PLUGIN_DEBUG("clibpoard transfer from server completed."); - clipboard->srv_data = output; + REMMINA_PLUGIN_DEBUG("gp=%p: clipboard transfer from server completed.", gp); } else { - // Clipboard data arrived from server when we are not busywaiting on main loop + // Clipboard data arrived from server when we are not busy waiting on main loop // Unfortunately, we must discard it - REMMINA_PLUGIN_DEBUG("clibpoard transfer from server completed. Data discarded due to abort or timeout."); - clipboard->srv_clip_data_wait = SCDW_NONE; + REMMINA_PLUGIN_DEBUG("gp=%p: clipboard transfer from server completed, but no local application is requesting it. Data is on local cache now, try to paste later.", gp); } + clipboard->srv_clip_data_wait = SCDW_NONE; pthread_mutex_unlock(&clipboard->transfer_clip_mutex); return CHANNEL_RC_OK; } + void remmina_rdp_cliprdr_request_data(GtkClipboard *gtkClipboard, GtkSelectionData *selection_data, guint info, RemminaProtocolWidget *gp) { TRACE_CALL(__func__); - /* Called by GTK when someone press "Paste" on the client side. + /* Called by GTK when someone presses "Paste" on the client side. * We ask to the server the data we need */ CLIPRDR_FORMAT_DATA_REQUEST *pFormatDataRequest; @@ -481,9 +559,9 @@ void remmina_rdp_cliprdr_request_data(GtkClipboard *gtkClipboard, GtkSelectionDa struct timespec to; struct timeval tv; int rc; - time_t tlimit; + time_t tlimit, tlimit1s, tnow, tstart; - REMMINA_PLUGIN_DEBUG("A local application has requested remote clipboard data for local format id %d", info); + REMMINA_PLUGIN_DEBUG("gp=%p: A local application has requested remote clipboard data for remote format id %d", gp, info); clipboard = &(rfi->clipboard); if (clipboard->srv_clip_data_wait != SCDW_NONE) { @@ -491,67 +569,84 @@ void remmina_rdp_cliprdr_request_data(GtkClipboard *gtkClipboard, GtkSelectionDa return; } - clipboard->format = info; + if (clipboard->format != info || clipboard->srv_data == NULL) { + /* We do not have a local cached clipoard, so we have to start a remote request */ + remmina_rdp_cliprdr_cached_clipboard_free(clipboard); - /* Request Clipboard content from the server, the request is async */ + clipboard->format = info; - pthread_mutex_lock(&clipboard->transfer_clip_mutex); + pthread_mutex_lock(&clipboard->transfer_clip_mutex); - pFormatDataRequest = (CLIPRDR_FORMAT_DATA_REQUEST *)malloc(sizeof(CLIPRDR_FORMAT_DATA_REQUEST)); - ZeroMemory(pFormatDataRequest, sizeof(CLIPRDR_FORMAT_DATA_REQUEST)); - pFormatDataRequest->requestedFormatId = clipboard->format; - clipboard->srv_clip_data_wait = SCDW_BUSY_WAIT; + pFormatDataRequest = (CLIPRDR_FORMAT_DATA_REQUEST *)malloc(sizeof(CLIPRDR_FORMAT_DATA_REQUEST)); + ZeroMemory(pFormatDataRequest, sizeof(CLIPRDR_FORMAT_DATA_REQUEST)); + pFormatDataRequest->requestedFormatId = clipboard->format; - REMMINA_PLUGIN_DEBUG("Requesting clipboard data with format %d from the server", clipboard->format); + clipboard->srv_clip_data_wait = SCDW_BUSY_WAIT; // Annotate that we are waiting for ServerFormatDataResponse - rdp_event.type = REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_DATA_REQUEST; - rdp_event.clipboard_formatdatarequest.pFormatDataRequest = pFormatDataRequest; - remmina_rdp_event_event_push(gp, &rdp_event); + REMMINA_PLUGIN_DEBUG("gp=%p Requesting clipboard data with format %d from the server via ServerFormatDataRequest", gp, clipboard->format); + rdp_event.type = REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_DATA_REQUEST; + rdp_event.clipboard_formatdatarequest.pFormatDataRequest = pFormatDataRequest; + remmina_rdp_event_event_push(gp, &rdp_event); + + /* Busy wait clipboard data for CLIPBOARD_TRANSFER_WAIT_TIME seconds. + * In the meanwhile allow GTK event loop to proceed */ + + tstart = time(NULL); + tlimit = tstart + CLIPBOARD_TRANSFER_WAIT_TIME; + rc = 100000; + tlimit1s = tstart + 1; + while ((tnow = time(NULL)) < tlimit && rc != 0 && clipboard->srv_clip_data_wait == SCDW_BUSY_WAIT) { + + if (tnow >= tlimit1s) { + REMMINA_PLUGIN_DEBUG("gp=%p, clipboard data is still not here after %u seconds", gp, (unsigned)(tnow - tstart)); + tlimit1s = time(NULL) + 1; + } + + gettimeofday(&tv, NULL); + to.tv_sec = tv.tv_sec; + to.tv_nsec = tv.tv_usec * 1000 + 5000000; // wait for data for 5ms + if (to.tv_nsec >= 1000000000) { + to.tv_nsec -= 1000000000; + to.tv_sec++; + } + rc = pthread_cond_timedwait(&clipboard->transfer_clip_cond, &clipboard->transfer_clip_mutex, &to); + if (rc == 0) + break; - /* Busy wait clibpoard data for CLIPBOARD_TRANSFER_WAIT_TIME seconds. - * In the meanwhile allow GTK event loop to proceed */ - - tlimit = time(NULL) + CLIPBOARD_TRANSFER_WAIT_TIME; - rc = 100000; - while (time(NULL) < tlimit && rc != 0 && clipboard->srv_clip_data_wait == SCDW_BUSY_WAIT) { - gettimeofday(&tv, NULL); - to.tv_sec = tv.tv_sec; - to.tv_nsec = tv.tv_usec * 1000 + 40000000; // wait for 40ms - if (to.tv_nsec >= 1000000000) { - to.tv_nsec -= 1000000000; - to.tv_sec++; + gtk_main_iteration_do(FALSE); } - rc = pthread_cond_timedwait(&clipboard->transfer_clip_cond, &clipboard->transfer_clip_mutex, &to); - if (rc == 0) - break; - gtk_main_iteration_do(FALSE); - } - if (rc == 0) { - /* Data has arrived without timeout */ - if (clipboard->srv_data != NULL) { - if (info == CB_FORMAT_PNG || info == CF_DIB || info == CF_DIBV5 || info == CB_FORMAT_JPEG) { - gtk_selection_data_set_pixbuf(selection_data, clipboard->srv_data); - g_object_unref(clipboard->srv_data); + if (rc != 0) { + /* Timeout, just log it and hope that data will arrive later */ + if (clipboard->srv_clip_data_wait == SCDW_ABORTING) { + g_warning("[RDP] gp=%p Clipboard data wait aborted.",gp); } else { - gtk_selection_data_set_text(selection_data, clipboard->srv_data, -1); - free(clipboard->srv_data); + if (rc == ETIMEDOUT) + g_warning("[RDP] gp=%p Clipboard data from the server is not available in %d seconds. No data will be available to user.", + gp, CLIPBOARD_TRANSFER_WAIT_TIME); + else + g_warning("[RDP] gp=%p internal error: pthread_cond_timedwait() returned %d\n", gp, rc); } } - clipboard->srv_clip_data_wait = SCDW_NONE; - } else { - if (clipboard->srv_clip_data_wait == SCDW_ABORTING) { - g_warning("[RDP] Clipboard data wait aborted."); + + pthread_mutex_unlock(&clipboard->transfer_clip_mutex); + + } + + if (clipboard->srv_data != NULL) { + REMMINA_PLUGIN_DEBUG("gp=%p pasting data to local application", gp); + /* We have data in cache, just paste it */ + if (info == CB_FORMAT_PNG || info == CF_DIB || info == CF_DIBV5 || info == CB_FORMAT_JPEG) { + gtk_selection_data_set_pixbuf(selection_data, clipboard->srv_data); } else { - if (rc == ETIMEDOUT) - g_warning("[RDP] Clipboard data from the server is not available in %d seconds. No data will be available to user.", - CLIPBOARD_TRANSFER_WAIT_TIME); - else - g_warning("[RDP] internal error: pthread_cond_timedwait() returned %d\n", rc); + REMMINA_PLUGIN_DEBUG("gp=%p returning %zu bytes of text in clipboard to requesting application", gp, strlen(clipboard->srv_data)); + gtk_selection_data_set_text(selection_data, clipboard->srv_data, -1); } clipboard->srv_clip_data_wait = SCDW_NONE; + } else { + REMMINA_PLUGIN_DEBUG("gp=%p cannot paste data to local application because ->srv_data is NULL", gp); } - pthread_mutex_unlock(&clipboard->transfer_clip_mutex); + } void remmina_rdp_cliprdr_empty_clipboard(GtkClipboard *gtkClipboard, rfClipboard *clipboard) @@ -571,7 +666,6 @@ CLIPRDR_FORMAT_LIST *remmina_rdp_cliprdr_get_client_format_list(RemminaProtocolW gint loccount, srvcount; gint formatId, i; CLIPRDR_FORMAT *formats; - gchar *name; struct retp_t { CLIPRDR_FORMAT_LIST pFormatList; CLIPRDR_FORMAT formats[]; @@ -584,14 +678,14 @@ CLIPRDR_FORMAT_LIST *remmina_rdp_cliprdr_get_client_format_list(RemminaProtocolW gtkClipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD); if (gtkClipboard) result = gtk_clipboard_wait_for_targets(gtkClipboard, &targets, &loccount); - REMMINA_PLUGIN_DEBUG("Sending to server the following local clipboard content formats"); + REMMINA_PLUGIN_DEBUG("gp=%p sending to server the following local clipboard content formats", gp); if (result && loccount > 0) { formats = (CLIPRDR_FORMAT *)malloc(loccount * sizeof(CLIPRDR_FORMAT)); srvcount = 0; for (i = 0; i < loccount; i++) { formatId = remmina_rdp_cliprdr_get_format_from_gdkatom(targets[i]); if (formatId != 0) { - name = gdk_atom_name(targets[i]); + gchar *name = gdk_atom_name(targets[i]); REMMINA_PLUGIN_DEBUG(" local clipboard format %s will be sent to remote as %d", name, formatId); g_free(name); formats[srvcount].formatId = formatId; @@ -631,7 +725,6 @@ static void remmina_rdp_cliprdr_mt_get_format_list(RemminaProtocolWidget *gp, Re ui->retptr = (void *)remmina_rdp_cliprdr_get_client_format_list(gp); } - void remmina_rdp_cliprdr_get_clipboard_data(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui) { TRACE_CALL(__func__); @@ -746,20 +839,24 @@ void remmina_rdp_cliprdr_set_clipboard_data(RemminaProtocolWidget *gp, RemminaPl { TRACE_CALL(__func__); GtkClipboard *gtkClipboard; - GtkTargetEntry *targets; gint n_targets; rfContext *rfi = GET_PLUGIN_DATA(gp); gtkClipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD); if (gtkClipboard) { - targets = gtk_target_table_new_from_list(ui->clipboard.targetlist, &n_targets); - if (targets) { - REMMINA_PLUGIN_DEBUG("setting clipboard with owner to owner %p", gp); - gtk_clipboard_set_with_owner(gtkClipboard, targets, n_targets, - (GtkClipboardGetFunc)remmina_rdp_cliprdr_request_data, - (GtkClipboardClearFunc)remmina_rdp_cliprdr_empty_clipboard, G_OBJECT(gp)); - gtk_target_table_free(targets, n_targets); + GtkTargetEntry *targets = gtk_target_table_new_from_list(ui->clipboard.targetlist, &n_targets); + if (!targets) { + /* If no targets exists, this is an internal error, because + * remmina_rdp_cliprdr_server_format_list() must have produced + * at least one target before calling REMMINA_RDP_UI_CLIPBOARD_SET_DATA */ + g_warning("[RDP] internal error: no targets to insert into the local clipboard"); } + + REMMINA_PLUGIN_DEBUG("setting clipboard with owner to me: %p", gp); + gtk_clipboard_set_with_owner(gtkClipboard, targets, n_targets, + (GtkClipboardGetFunc)remmina_rdp_cliprdr_request_data, + (GtkClipboardClearFunc)remmina_rdp_cliprdr_empty_clipboard, G_OBJECT(gp)); + gtk_target_table_free(targets, n_targets); } } @@ -811,21 +908,21 @@ void remmina_rdp_clipboard_free(rfContext *rfi) { TRACE_CALL(__func__); - // Future: deinitialize rfi->clipboard + remmina_rdp_cliprdr_cached_clipboard_free(&(rfi->clipboard)); + } -void remmina_rdp_clipboard_abort_transfer(rfContext *rfi) +void remmina_rdp_clipboard_abort_client_format_data_request(rfContext *rfi) { + TRACE_CALL(__func__); if (rfi && rfi->clipboard.srv_clip_data_wait == SCDW_BUSY_WAIT) { - REMMINA_PLUGIN_DEBUG("requesting clipboard transfer to abort"); + REMMINA_PLUGIN_DEBUG("requesting clipboard data transfer from server to be ignored and busywait loop to exit"); /* Allow clipboard transfer from server to terminate */ rfi->clipboard.srv_clip_data_wait = SCDW_ABORTING; usleep(100000); } } - - void remmina_rdp_cliprdr_init(rfContext *rfi, CliprdrClientContext *cliprdr) { TRACE_CALL(__func__); diff --git a/plugins/rdp/rdp_cliprdr.h b/plugins/rdp/rdp_cliprdr.h index 2dbe363cb..a5062332c 100644 --- a/plugins/rdp/rdp_cliprdr.h +++ b/plugins/rdp/rdp_cliprdr.h @@ -48,4 +48,4 @@ void remmina_rdp_channel_cliprdr_process(RemminaProtocolWidget *gp, wMessage *ev void remmina_rdp_event_process_clipboard(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui); CLIPRDR_FORMAT_LIST *remmina_rdp_cliprdr_get_client_format_list(RemminaProtocolWidget *gp); void remmina_rdp_cliprdr_detach_owner(RemminaProtocolWidget *gp); -void remmina_rdp_clipboard_abort_transfer(rfContext *rfi); +void remmina_rdp_clipboard_abort_client_format_data_request(rfContext *rfi); diff --git a/plugins/rdp/rdp_event.c b/plugins/rdp/rdp_event.c index 7f5d8fd4b..70d2f367d 100644 --- a/plugins/rdp/rdp_event.c +++ b/plugins/rdp/rdp_event.c @@ -815,25 +815,29 @@ gboolean remmina_rdp_event_on_clipboard(GtkClipboard *gtkClipboard, GdkEvent *ev TRACE_CALL(__func__); RemminaPluginRdpEvent rdp_event = { 0 }; CLIPRDR_FORMAT_LIST *pFormatList; + GObject *new_owner; - /* Usually "owner-change" is fired when a user pres "COPY" on the client + /* Usually "owner-change" is fired when a user presses "COPY" on the client * OR when this plugin calls gtk_clipboard_set_with_owner() - * after receivina a RDP server format list in remmina_rdp_cliprdr_server_format_list() + * after receiving a RDP server format list in remmina_rdp_cliprdr_server_format_list() * In the latter case, we must ignore owner change */ - REMMINA_PLUGIN_DEBUG("owner-change event received"); + REMMINA_PLUGIN_DEBUG("gp=%p: owner-change event received", gp); rfContext *rfi = GET_PLUGIN_DATA(gp); if (rfi) - remmina_rdp_clipboard_abort_transfer(rfi); + remmina_rdp_clipboard_abort_client_format_data_request(rfi); - if (gtk_clipboard_get_owner(gtkClipboard) != (GObject *)gp) { + new_owner = gtk_clipboard_get_owner(gtkClipboard); + if (new_owner != (GObject *)gp) { /* To do: avoid this when the new owner is another remmina protocol widget of * the same remmina application */ - REMMINA_PLUGIN_DEBUG(" new owner is different than me: new=%p me=%p. Sending local clipboard format list to server.", - gtk_clipboard_get_owner(gtkClipboard), (GObject *)gp); + REMMINA_PLUGIN_DEBUG("gp=%p owner-change: new owner is different than me: new=%p me=%p", + gp, new_owner, gp); + REMMINA_PLUGIN_DEBUG("gp=%p owner-change: new owner is not me: Sending local clipboard format list to server.", + gp, new_owner, gp); pFormatList = remmina_rdp_cliprdr_get_client_format_list(gp); rdp_event.type = REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_LIST; rdp_event.clipboard_formatlist.pFormatList = pFormatList; diff --git a/plugins/rdp/rdp_plugin.c b/plugins/rdp/rdp_plugin.c index 23f634d1e..274dfd25c 100644 --- a/plugins/rdp/rdp_plugin.c +++ b/plugins/rdp/rdp_plugin.c @@ -244,6 +244,8 @@ static BOOL rf_process_event_queue(RemminaProtocolWidget *gp) break; case REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_DATA_REQUEST: + REMMINA_PLUGIN_DEBUG("Sending client FormatDataRequest to server"); + gettimeofday(&(rfi->clipboard.clientformatdatarequest_tv), NULL); rfi->clipboard.context->ClientFormatDataRequest(rfi->clipboard.context, event->clipboard_formatdatarequest.pFormatDataRequest); free(event->clipboard_formatdatarequest.pFormatDataRequest); break; diff --git a/plugins/rdp/rdp_plugin.h b/plugins/rdp/rdp_plugin.h index 2973ff680..1c1f2a817 100644 --- a/plugins/rdp/rdp_plugin.h +++ b/plugins/rdp/rdp_plugin.h @@ -118,6 +118,9 @@ struct rf_clipboard { pthread_cond_t transfer_clip_cond; enum { SCDW_NONE, SCDW_BUSY_WAIT, SCDW_ABORTING } srv_clip_data_wait; gpointer srv_data; + + /* Stats for clipboard download */ + struct timeval clientformatdatarequest_tv; }; typedef struct rf_clipboard rfClipboard; |