GUACAMOLE-249: Restore support for CLIPRDR channel.

This commit is contained in:
Michael Jumper 2019-10-11 17:01:26 -07:00
parent 831606a4e9
commit b64b8f375a
10 changed files with 684 additions and 465 deletions

View File

@ -36,6 +36,7 @@ libguac_client_rdp_la_SOURCES = \
audio_input.c \ audio_input.c \
channels.c \ channels.c \
client.c \ client.c \
clipboard.c \
decompose.c \ decompose.c \
dvc.c \ dvc.c \
error.c \ error.c \
@ -44,7 +45,6 @@ libguac_client_rdp_la_SOURCES = \
ptr_string.c \ ptr_string.c \
rdp.c \ rdp.c \
rdp_bitmap.c \ rdp_bitmap.c \
rdp_cliprdr.c \
rdp_color.c \ rdp_color.c \
rdp_disp.c \ rdp_disp.c \
rdp_fs.c \ rdp_fs.c \
@ -104,6 +104,7 @@ noinst_HEADERS = \
guac_svc/svc_service.h \ guac_svc/svc_service.h \
audio_input.h \ audio_input.h \
client.h \ client.h \
clipboard.h \
channels.h \ channels.h \
decompose.h \ decompose.h \
dvc.h \ dvc.h \
@ -113,7 +114,6 @@ noinst_HEADERS = \
ptr_string.h \ ptr_string.h \
rdp.h \ rdp.h \
rdp_bitmap.h \ rdp_bitmap.h \
rdp_cliprdr.h \
rdp_color.h \ rdp_color.h \
rdp_disp.h \ rdp_disp.h \
rdp_fs.h \ rdp_fs.h \

View File

@ -56,7 +56,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
client->data = rdp_client; client->data = rdp_client;
/* Init clipboard */ /* Init clipboard */
rdp_client->clipboard = guac_common_clipboard_alloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH); rdp_client->clipboard = guac_rdp_clipboard_alloc(client);
/* Init display update module */ /* Init display update module */
rdp_client->disp = guac_rdp_disp_alloc(); rdp_client->disp = guac_rdp_disp_alloc();
@ -92,6 +92,9 @@ int guac_rdp_client_free_handler(guac_client* client) {
if (rdp_client->settings != NULL) if (rdp_client->settings != NULL)
guac_rdp_settings_free(rdp_client->settings); guac_rdp_settings_free(rdp_client->settings);
/* Clean up clipboard */
guac_rdp_clipboard_free(rdp_client->clipboard);
/* Free display update module */ /* Free display update module */
guac_rdp_disp_free(rdp_client->disp); guac_rdp_disp_free(rdp_client->disp);
@ -128,7 +131,6 @@ int guac_rdp_client_free_handler(guac_client* client) {
guac_rdp_audio_buffer_free(rdp_client->audio_input); guac_rdp_audio_buffer_free(rdp_client->audio_input);
/* Free client data */ /* Free client data */
guac_common_clipboard_free(rdp_client->clipboard);
free(rdp_client); free(rdp_client);
return 0; return 0;

View File

@ -0,0 +1,527 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 "config.h"
#include "channels.h"
#include "client.h"
#include "clipboard.h"
#include "common/clipboard.h"
#include "common/iconv.h"
#include "rdp.h"
#include <freerdp/channels/channels.h>
#include <freerdp/client/cliprdr.h>
#include <freerdp/freerdp.h>
#include <guacamole/client.h>
#include <winpr/wtypes.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
/**
* Sends a Format List PDU to the RDP server containing the formats of
* clipboard data supported. This PDU is used both to indicate the general
* clipboard formats supported at the begining of an RDP session and to inform
* the RDP server that new clipboard data is available within the listed
* formats.
*
* @param cliprdr
* The CliprdrClientContext structure used by FreeRDP to handle the
* CLIPRDR channel for the current RDP session.
*
* @return
* CHANNEL_RC_OK (zero) if the Format List PDU was sent successfully, an
* error code (non-zero) otherwise.
*/
static UINT guac_rdp_cliprdr_send_format_list(CliprdrClientContext* cliprdr) {
/* We support CP-1252 and UTF-16 text */
CLIPRDR_FORMAT_LIST format_list = {
.formats = (CLIPRDR_FORMAT[]) {
{ .formatId = CF_TEXT },
{ .formatId = CF_UNICODETEXT }
},
.numFormats = 2,
.msgFlags = CB_RESPONSE_OK
};
return cliprdr->ClientFormatList(cliprdr, &format_list);
}
/**
* Callback invoked by the FreeRDP CLIPRDR plugin for received Monitor Ready
* PDUs. The Monitor Ready PDU is sent by the RDP server only during
* initialization of the CLIPRDR channel. It is part of the CLIPRDR channel
* handshake and indicates that the RDP server's handling of clipboard
* redirection is ready to proceed.
*
* @param cliprdr
* The CliprdrClientContext structure used by FreeRDP to handle the CLIPRDR
* channel for the current RDP session.
*
* @param monitor_ready
* The CLIPRDR_MONITOR_READY structure representing the Monitor Ready PDU
* that was received.
*
* @return
* CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
* (non-zero) otherwise.
*/
static UINT guac_rdp_cliprdr_monitor_ready(CliprdrClientContext* cliprdr,
const CLIPRDR_MONITOR_READY* monitor_ready) {
/* Respond with supported format list */
return guac_rdp_cliprdr_send_format_list(cliprdr);
}
/**
* Sends a Format Data Request PDU to the RDP server, requesting that available
* clipboard data be sent to the client in the specified format. This PDU is
* sent when the server indicating that clipboard data is available via a
* Format List PDU.
*
* @param client
* The guac_client associated with the current RDP session.
*
* @param format
* The clipboard format to request. This format must be one of the
* documented values used by the CLIPRDR channel for clipboard format IDs.
*
* @return
* CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
* (non-zero) otherwise.
*/
static UINT guac_rdp_cliprdr_send_format_data_request(
CliprdrClientContext* cliprdr, UINT32 format) {
/* FreeRDP-specific handlers for CLIPRDR are not assigned, and thus not
* callable, until after the relevant guac_rdp_clipboard structure is
* allocated and associated with the CliprdrClientContext */
guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
assert(clipboard != NULL);
/* Create new data request */
CLIPRDR_FORMAT_DATA_REQUEST data_request = {
.requestedFormatId = format
};
/* Note the format we've requested for reference later when the requested
* data is received via a Format Data Response PDU */
clipboard->requested_format = format;
/* Send request */
return cliprdr->ClientFormatDataRequest(cliprdr, &data_request);
}
/**
* Returns whether the given Format List PDU indicates support for the given
* clipboard format.
*
* @param format_list
* The CLIPRDR_FORMAT_LIST structure representing the Format List PDU
* being tested.
*
* @param format_id
* The ID of the clipboard format to test, such as CF_TEXT or
* CF_UNICODETEXT.
*
* @return
* Non-zero if the given Format List PDU indicates support for the given
* clipboard format, zero otherwise.
*/
static int guac_rdp_cliprdr_format_supported(const CLIPRDR_FORMAT_LIST* format_list,
UINT format_id) {
/* Search format list for matching ID */
for (int i = 0; i < format_list->numFormats; i++) {
if (format_list->formats[i].formatId == format_id)
return 1;
}
/* If no matching ID, format is not supported */
return 0;
}
/**
* Callback invoked by the FreeRDP CLIPRDR plugin for received Format List
* PDUs. The Format List PDU is sent by the RDP server to indicate that new
* clipboard data has been copied and is available for retrieval in the formats
* listed. A client wishing to retrieve that data responds with a Format Data
* Request PDU.
*
* @param cliprdr
* The CliprdrClientContext structure used by FreeRDP to handle the CLIPRDR
* channel for the current RDP session.
*
* @param format_list
* The CLIPRDR_FORMAT_LIST structure representing the Format List PDU that
* was received.
*
* @return
* CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
* (non-zero) otherwise.
*/
static UINT guac_rdp_cliprdr_format_list(CliprdrClientContext* cliprdr,
const CLIPRDR_FORMAT_LIST* format_list) {
/* FreeRDP-specific handlers for CLIPRDR are not assigned, and thus not
* callable, until after the relevant guac_rdp_clipboard structure is
* allocated and associated with the CliprdrClientContext */
guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
assert(clipboard != NULL);
/* Prefer Unicode (in this case, UTF-16) */
if (guac_rdp_cliprdr_format_supported(format_list, CF_UNICODETEXT))
return guac_rdp_cliprdr_send_format_data_request(cliprdr, CF_UNICODETEXT);
/* Use Windows' CP-1252 if Unicode unavailable */
if (guac_rdp_cliprdr_format_supported(format_list, CF_TEXT))
return guac_rdp_cliprdr_send_format_data_request(cliprdr, CF_TEXT);
/* Ignore any unsupported data */
guac_client_log(clipboard->client, GUAC_LOG_DEBUG, "Ignoring unsupported "
"clipboard data. Only Unicode and text clipboard formats are "
"currently supported.");
return CHANNEL_RC_OK;
}
/**
* Callback invoked by the FreeRDP CLIPRDR plugin for received Format Data
* Request PDUs. The Format Data Request PDU is sent by the RDP server when
* requesting that clipboard data be sent, in response to a received Format
* List PDU. The client is required to respond with a Format Data Response PDU
* containing the requested data.
*
* @param cliprdr
* The CliprdrClientContext structure used by FreeRDP to handle the CLIPRDR
* channel for the current RDP session.
*
* @param format_data_request
* The CLIPRDR_FORMAT_DATA_REQUEST structure representing the Format Data
* Request PDU that was received.
*
* @return
* CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
* (non-zero) otherwise.
*/
static UINT guac_rdp_cliprdr_format_data_request(CliprdrClientContext* cliprdr,
const CLIPRDR_FORMAT_DATA_REQUEST* format_data_request) {
/* FreeRDP-specific handlers for CLIPRDR are not assigned, and thus not
* callable, until after the relevant guac_rdp_clipboard structure is
* allocated and associated with the CliprdrClientContext */
guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
assert(clipboard != NULL);
guac_iconv_write* writer;
const char* input = clipboard->clipboard->buffer;
char* output = malloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
/* Map requested clipboard format to a guac_iconv writer */
switch (format_data_request->requestedFormatId) {
case CF_TEXT:
writer = GUAC_WRITE_CP1252;
break;
case CF_UNICODETEXT:
writer = GUAC_WRITE_UTF16;
break;
/* Warn if clipboard data cannot be sent as intended due to a violation
* of the CLIPRDR spec */
default:
guac_client_log(clipboard->client, GUAC_LOG_WARNING, "Received "
"clipboard data cannot be sent to the RDP server because "
"the RDP server has requested a clipboard format which "
"was not declared as available. This violates the "
"specification for the CLIPRDR channel.");
free(output);
return CHANNEL_RC_OK;
}
/* Send received clipboard data to the RDP server in the format
* requested */
BYTE* start = (BYTE*) output;
guac_iconv(GUAC_READ_UTF8, &input, clipboard->clipboard->length,
writer, &output, GUAC_RDP_CLIPBOARD_MAX_LENGTH);
CLIPRDR_FORMAT_DATA_RESPONSE data_response = {
.requestedFormatData = (BYTE*) output,
.dataLen = ((BYTE*) output) - start
};
return cliprdr->ClientFormatDataResponse(cliprdr, &data_response);
}
/**
* Callback invoked by the FreeRDP CLIPRDR plugin for received Format Data
* Response PDUs. The Format Data Response PDU is sent by the RDP server when
* fullfilling a request for clipboard data received via a Format Data Request
* PDU.
*
* @param cliprdr
* The CliprdrClientContext structure used by FreeRDP to handle the CLIPRDR
* channel for the current RDP session.
*
* @param format_data_response
* The CLIPRDR_FORMAT_DATA_RESPONSE structure representing the Format Data
* Response PDU that was received.
*
* @return
* CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
* (non-zero) otherwise.
*/
static UINT guac_rdp_cliprdr_format_data_response(CliprdrClientContext* cliprdr,
const CLIPRDR_FORMAT_DATA_RESPONSE* format_data_response) {
/* FreeRDP-specific handlers for CLIPRDR are not assigned, and thus not
* callable, until after the relevant guac_rdp_clipboard structure is
* allocated and associated with the CliprdrClientContext */
guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
assert(clipboard != NULL);
char received_data[GUAC_RDP_CLIPBOARD_MAX_LENGTH];
guac_iconv_read* reader;
const char* input = (char*) format_data_response->requestedFormatData;
char* output = received_data;
/* Find correct source encoding */
switch (clipboard->requested_format) {
/* Non-Unicode (Windows CP-1252) */
case CF_TEXT:
reader = GUAC_READ_CP1252;
break;
/* Unicode (UTF-16) */
case CF_UNICODETEXT:
reader = GUAC_READ_UTF16;
break;
/* If the format ID stored within the guac_rdp_clipboard structure is actually
* not supported here, then something has been implemented incorrectly.
* Either incorrect values are (somehow) being stored, or support for
* the format indicated by that value is incomplete and must be added
* here. The values which may be stored within requested_format are
* completely within our control. */
default:
guac_client_log(clipboard->client, GUAC_LOG_DEBUG, "Requested "
"clipboard data in unsupported format (0x%X).",
clipboard->requested_format);
return CHANNEL_RC_OK;
}
/* Convert, store, and forward the clipboard data received from RDP
* server */
if (guac_iconv(reader, &input, format_data_response->dataLen,
GUAC_WRITE_UTF8, &output, sizeof(received_data))) {
int length = strnlen(received_data, sizeof(received_data));
guac_common_clipboard_reset(clipboard->clipboard, "text/plain");
guac_common_clipboard_append(clipboard->clipboard, received_data, length);
guac_common_clipboard_send(clipboard->clipboard, clipboard->client);
}
return CHANNEL_RC_OK;
}
/**
* Callback which associates handlers specific to Guacamole with the
* CliprdrClientContext instance allocated by FreeRDP to deal with received
* CLIPRDR (clipboard redirection) messages.
*
* This function is called whenever a channel connects via the PubSub event
* system within FreeRDP, but only has any effect if the connected channel is
* the CLIPRDR channel. This specific callback is registered with the PubSub
* system of the relevant rdpContext when guac_rdp_clipboard_load_plugin() is
* called.
*
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* Event-specific arguments, mainly the name of the channel, and a
* reference to the associated plugin loaded for that channel by FreeRDP.
*/
static void guac_rdp_cliprdr_channel_connected(rdpContext* context,
ChannelConnectedEventArgs* e) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_clipboard* clipboard = rdp_client->clipboard;
/* FreeRDP-specific handlers for CLIPRDR are not assigned, and thus not
* callable, until after the relevant guac_rdp_clipboard structure is
* allocated and associated with the guac_rdp_client */
assert(clipboard != NULL);
/* Ignore connection event if it's not for the CLIPRDR channel */
if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) != 0)
return;
/* The structure pointed to by pInterface is guaranteed to be a
* CliprdrClientContext if the channel is CLIPRDR */
CliprdrClientContext* cliprdr = (CliprdrClientContext*) e->pInterface;
/* Associate FreeRDP CLIPRDR context and its Guacamole counterpart with
* eachother */
cliprdr->custom = clipboard;
clipboard->cliprdr = cliprdr;
cliprdr->MonitorReady = guac_rdp_cliprdr_monitor_ready;
cliprdr->ServerFormatList = guac_rdp_cliprdr_format_list;
cliprdr->ServerFormatDataRequest = guac_rdp_cliprdr_format_data_request;
cliprdr->ServerFormatDataResponse = guac_rdp_cliprdr_format_data_response;
guac_client_log(client, GUAC_LOG_DEBUG, "CLIPRDR (clipboard redirection) "
"channel connected.");
}
guac_rdp_clipboard* guac_rdp_clipboard_alloc(guac_client* client) {
/* Allocate clipboard and underlying storage */
guac_rdp_clipboard* clipboard = calloc(1, sizeof(guac_rdp_clipboard));
clipboard->client = client;
clipboard->clipboard = guac_common_clipboard_alloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
clipboard->requested_format = CF_TEXT;
return clipboard;
}
void guac_rdp_clipboard_load_plugin(guac_rdp_clipboard* clipboard,
rdpContext* context) {
/* Attempt to load FreeRDP support for the CLIPRDR channel */
if (guac_freerdp_channels_load_plugin(context->channels, context->settings, "cliprdr", NULL)) {
guac_client_log(clipboard->client, GUAC_LOG_WARNING,
"Support for the CLIPRDR channel (clipboard redirection) "
"could not be loaded. This support normally takes the form of "
"a plugin which is built into FreeRDP. Lacking this support, "
"clipboard will not work.");
return;
}
/* Complete RDP side of initialization when channel is connected */
PubSub_SubscribeChannelConnected(context->pubSub,
(pChannelConnectedEventHandler) guac_rdp_cliprdr_channel_connected);
guac_client_log(clipboard->client, GUAC_LOG_DEBUG, "Support for CLIPRDR "
"(clipboard redirection) registered. Awaiting channel "
"connection.");
}
void guac_rdp_clipboard_free(guac_rdp_clipboard* clipboard) {
/* Do nothing if the clipboard is not actually allocated */
if (clipboard == NULL)
return;
/* Free clipboard and underlying storage */
guac_common_clipboard_free(clipboard->clipboard);
free(clipboard);
}
int guac_rdp_clipboard_handler(guac_user* user, guac_stream* stream,
char* mimetype) {
guac_client* client = user->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Ignore stream creation if no clipboard structure is available to handle
* received data */
guac_rdp_clipboard* clipboard = rdp_client->clipboard;
if (clipboard == NULL)
return 0;
/* Handle any future "blob" and "end" instructions for this stream with
* handlers that are aware of the RDP clipboard state */
stream->blob_handler = guac_rdp_clipboard_blob_handler;
stream->end_handler = guac_rdp_clipboard_end_handler;
/* Clear any current contents, assigning the mimetype the data which will
* be received */
guac_common_clipboard_reset(clipboard->clipboard, mimetype);
return 0;
}
int guac_rdp_clipboard_blob_handler(guac_user* user, guac_stream* stream,
void* data, int length) {
guac_client* client = user->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Ignore received data if no clipboard structure is available to handle
* that data */
guac_rdp_clipboard* clipboard = rdp_client->clipboard;
if (clipboard == NULL)
return 0;
/* Append received data to current clipboard contents */
guac_common_clipboard_append(clipboard->clipboard, (char*) data, length);
return 0;
}
int guac_rdp_clipboard_end_handler(guac_user* user, guac_stream* stream) {
guac_client* client = user->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Ignore end of stream if no clipboard structure is available to handle
* the data that was received */
guac_rdp_clipboard* clipboard = rdp_client->clipboard;
if (clipboard == NULL)
return 0;
/* Terminate clipboard data with NULL */
guac_common_clipboard_append(clipboard->clipboard, "", 1);
/* Notify RDP server of new data, if connected */
if (clipboard->cliprdr != NULL) {
guac_client_log(client, GUAC_LOG_DEBUG, "Clipboard data received. "
"Reporting availability of clipboard data to RDP server.");
guac_rdp_cliprdr_send_format_list(clipboard->cliprdr);
}
else
guac_client_log(client, GUAC_LOG_DEBUG, "Clipboard data has been "
"received, but cannot be sent to the RDP server because the "
"CLIPRDR channel is not yet connected.");
return 0;
}

View File

@ -0,0 +1,144 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#ifndef GUAC_RDP_CLIPBOARD_H
#define GUAC_RDP_CLIPBOARD_H
#include "config.h"
#include "common/clipboard.h"
#include <guacamole/client.h>
#include <freerdp/client/cliprdr.h>
#include <winpr/stream.h>
/**
* RDP clipboard, leveraging the "CLIPRDR" channel.
*/
typedef struct guac_rdp_clipboard {
/**
* The guac_client associated with the RDP connection. The broadcast
* socket of this client will receive any clipboard data received from the
* RDP server.
*/
guac_client* client;
/**
* CLIPRDR control interface.
*/
CliprdrClientContext* cliprdr;
/**
* The current clipboard contents.
*/
guac_common_clipboard* clipboard;
/**
* The format of the clipboard which was requested. Data received from
* the RDP server should conform to this format. This will be one of
* several legal clipboard format values defined within FreeRDP's WinPR
* library, such as CF_TEXT.
*/
UINT requested_format;
} guac_rdp_clipboard;
/**
* Allocates a new guac_rdp_clipboard which has been initialized for processing
* of Guacamole clipboard data. Support for the RDP side of the clipboard (the
* CLIPRDR channel) must be loaded separately during FreeRDP's PreConnect event
* using guac_rdp_clipboard_load_plugin().
*
* @param client
* The guac_client associated with the Guacamole side of the RDP
* connection.
*
* @return
* A newly-allocated instance of guac_rdp_clipboard which has been
* initialized for processing Guacamole clipboard data.
*/
guac_rdp_clipboard* guac_rdp_clipboard_alloc(guac_client* client);
/**
* Initializes clipboard support for RDP and handling of the CLIPRDR channel.
* If failures occur, messages noting the specifics of those failures will be
* logged, and the RDP side of clipboard support will not be functional.
*
* This MUST be called within the PreConnect callback of the freerdp instance
* for CLIPRDR support to be loaded.
*
* @param clipboard
* The guac_rdp_clipboard instance which has been allocated for the current
* RDP connection.
*
* @param rdpContext
* The rdpContext associated with the FreeRDP side of the RDP connection.
*/
void guac_rdp_clipboard_load_plugin(guac_rdp_clipboard* clipboard,
rdpContext* context);
/**
* Frees the resources associated with clipboard support for RDP and handling
* of the CLIPRDR channel. Only resources specific to Guacamole are freed.
* Resources specific to FreeRDP's handling of the CLIPRDR channel will be
* freed by FreeRDP. If the provided guac_rdp_clipboard is NULL, this function
* has no effect.
*
* @param clipboard
* The guac_rdp_clipboard instance which was been allocated for the current
* RDP connection.
*/
void guac_rdp_clipboard_free(guac_rdp_clipboard* clipboard);
/**
* Handler for inbound clipboard data, received via the stream created by an
* inbound "clipboard" instruction. This handler will assign the
* stream-specific handlers for processing "blob" and "end" instructions which
* will eventually be received as clipboard data is sent. This specific handler
* is expected to be assigned to the guac_user object of any user that may send
* clipboard data. The guac_rdp_clipboard instance which will receive this data
* MUST already be stored on the guac_rdp_client structure associated with the
* current RDP connection.
*/
guac_user_clipboard_handler guac_rdp_clipboard_handler;
/**
* Handler for stream data related to clipboard, received via "blob"
* instructions for a stream which has already been created with an inbound
* "clipboard" instruction. This specific handler is assigned to the
* guac_stream structure associated with that clipboard stream by
* guac_rdp_clipboard_handler(). The guac_rdp_clipboard instance which will
* receive this data MUST already be stored on the guac_rdp_client structure
* associated with the current RDP connection.
*/
guac_user_blob_handler guac_rdp_clipboard_blob_handler;
/**
* Handler for end-of-stream related to clipboard, indicated via an "end"
* instruction for a stream which has already been created with an inbound
* "clipboard" instruction. This specific handler is assigned to the
* guac_stream structure associated with that clipboard stream by
* guac_rdp_clipboard_handler(). The guac_rdp_clipboard instance which will
* receive this data MUST already be stored on the guac_rdp_client structure
* associated with the current RDP connection.
*/
guac_user_end_handler guac_rdp_clipboard_end_handler;
#endif

View File

@ -22,6 +22,7 @@
#include "audio_input.h" #include "audio_input.h"
#include "channels.h" #include "channels.h"
#include "client.h" #include "client.h"
#include "clipboard.h"
#include "common/cursor.h" #include "common/cursor.h"
#include "common/display.h" #include "common/display.h"
#include "common/recording.h" #include "common/recording.h"
@ -30,7 +31,6 @@
#include "keyboard.h" #include "keyboard.h"
#include "rdp.h" #include "rdp.h"
#include "rdp_bitmap.h" #include "rdp_bitmap.h"
#include "rdp_cliprdr.h"
#include "rdp_disp.h" #include "rdp_disp.h"
#include "rdp_fs.h" #include "rdp_fs.h"
#include "rdp_print_job.h" #include "rdp_print_job.h"
@ -109,11 +109,8 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
guac_rdp_audio_load_plugin(instance->context, dvc_list); guac_rdp_audio_load_plugin(instance->context, dvc_list);
} }
/* Load clipboard plugin */ /* Load "cliprdr" plugin for clipboard support */
if (guac_freerdp_channels_load_plugin(channels, instance->settings, guac_rdp_clipboard_load_plugin(rdp_client->clipboard, context);
"cliprdr", NULL))
guac_client_log(client, GUAC_LOG_WARNING,
"Failed to load cliprdr plugin. Clipboard will not work.");
/* If RDPSND/RDPDR required, load them */ /* If RDPSND/RDPDR required, load them */
if (settings->printing_enabled if (settings->printing_enabled
@ -414,7 +411,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
rdp_client->current_surface = rdp_client->display->default_surface; rdp_client->current_surface = rdp_client->display->default_surface;
rdp_client->requested_clipboard_format = CF_TEXT;
rdp_client->available_svc = guac_common_list_alloc(); rdp_client->available_svc = guac_common_list_alloc();
/* Init client */ /* Init client */

View File

@ -23,6 +23,7 @@
#include "config.h" #include "config.h"
#include "audio_input.h" #include "audio_input.h"
#include "clipboard.h"
#include "common/clipboard.h" #include "common/clipboard.h"
#include "common/display.h" #include "common/display.h"
#include "common/list.h" #include "common/list.h"
@ -95,17 +96,9 @@ typedef struct guac_rdp_client {
guac_rdp_keyboard* keyboard; guac_rdp_keyboard* keyboard;
/** /**
* The current clipboard contents. * The current state of the clipboard and the CLIPRDR channel.
*/ */
guac_common_clipboard* clipboard; guac_rdp_clipboard* clipboard;
/**
* The format of the clipboard which was requested. Data received from
* the RDP server should conform to this format. This will be one of
* several legal clipboard format values defined within FreeRDP, such as
* CB_FORMAT_TEXT.
*/
int requested_clipboard_format;
/** /**
* Audio output, if any. * Audio output, if any.

View File

@ -1,249 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 "config.h"
#include "client.h"
#include "common/clipboard.h"
#include "common/iconv.h"
#include "rdp.h"
#include "rdp_cliprdr.h"
#include <freerdp/channels/channels.h>
#include <freerdp/client/cliprdr.h>
#include <freerdp/freerdp.h>
#include <guacamole/client.h>
#include <winpr/wtypes.h>
#include <stdlib.h>
#include <string.h>
void guac_rdp_process_cliprdr_event(guac_client* client, wMessage* event) {
#if 0
switch (event->event_type) {
case CliprdrChannel_MonitorReady:
guac_rdp_process_cb_monitor_ready(client, event);
break;
case CliprdrChannel_FormatList:
guac_rdp_process_cb_format_list(client,
(RDP_CB_FORMAT_LIST_EVENT*) event);
break;
case CliprdrChannel_DataRequest:
guac_rdp_process_cb_data_request(client,
(RDP_CB_DATA_REQUEST_EVENT*) event);
break;
case CliprdrChannel_DataResponse:
guac_rdp_process_cb_data_response(client,
(RDP_CB_DATA_RESPONSE_EVENT*) event);
break;
default:
guac_client_log(client, GUAC_LOG_INFO,
"Unknown cliprdr event type: 0x%x",
GetMessageType(event->id));
}
#endif
}
void guac_rdp_process_cb_monitor_ready(guac_client* client, wMessage* event) {
#if 0
rdpChannels* channels =
((guac_rdp_client*) client->data)->rdp_inst->context->channels;
RDP_CB_FORMAT_LIST_EVENT* format_list =
(RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(
CliprdrChannel_Class,
CliprdrChannel_FormatList,
NULL, NULL);
/* Received notification of clipboard support. */
/* Respond with supported format list */
format_list->formats = (UINT32*) malloc(sizeof(UINT32)*2);
format_list->formats[0] = CF_TEXT;
format_list->formats[1] = CF_UNICODETEXT;
format_list->num_formats = 2;
freerdp_channels_send_event(channels, (wMessage*) format_list);
#endif
}
/**
* Sends a clipboard data request for the given format.
*
* @param client
* The guac_client associated with the current RDP session.
*
* @param format
* The clipboard format to request. This format must be one of the
* documented values used by the CLIPRDR channel for clipboard format IDs.
*/
static void __guac_rdp_cb_request_format(guac_client* client, int format) {
#if 0
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
rdpChannels* channels = rdp_client->rdp_inst->context->channels;
/* Create new data request */
RDP_CB_DATA_REQUEST_EVENT* data_request =
(RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new(
CliprdrChannel_Class,
CliprdrChannel_DataRequest,
NULL, NULL);
/* Set to requested format */
rdp_client->requested_clipboard_format = format;
data_request->format = format;
/* Send request */
freerdp_channels_send_event(channels, (wMessage*) data_request);
#endif
}
void guac_rdp_process_cb_format_list(guac_client* client,
RDP_CB_FORMAT_LIST_EVENT* event) {
int formats = 0;
/* Received notification of available data */
int i;
for (i=0; i<event->num_formats; i++) {
/* If plain text available, request it */
if (event->formats[i] == CF_TEXT)
formats |= GUAC_RDP_CLIPBOARD_FORMAT_CP1252;
else if (event->formats[i] == CF_UNICODETEXT)
formats |= GUAC_RDP_CLIPBOARD_FORMAT_UTF16;
}
/* Prefer Unicode to plain text */
if (formats & GUAC_RDP_CLIPBOARD_FORMAT_UTF16) {
__guac_rdp_cb_request_format(client, CF_UNICODETEXT);
return;
}
/* Use plain text if Unicode unavailable */
if (formats & GUAC_RDP_CLIPBOARD_FORMAT_CP1252) {
__guac_rdp_cb_request_format(client, CF_TEXT);
return;
}
/* Ignore if no supported format available */
guac_client_log(client, GUAC_LOG_INFO, "Ignoring unsupported clipboard data");
}
void guac_rdp_process_cb_data_request(guac_client* client,
RDP_CB_DATA_REQUEST_EVENT* event) {
#if 0
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
rdpChannels* channels = rdp_client->rdp_inst->context->channels;
guac_iconv_write* writer;
const char* input = rdp_client->clipboard->buffer;
char* output = malloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
RDP_CB_DATA_RESPONSE_EVENT* data_response;
/* Determine output encoding */
switch (event->format) {
case CF_TEXT:
writer = GUAC_WRITE_CP1252;
break;
case CF_UNICODETEXT:
writer = GUAC_WRITE_UTF16;
break;
default:
guac_client_log(client, GUAC_LOG_ERROR,
"Server requested unsupported clipboard data type");
free(output);
return;
}
/* Create new data response */
data_response = (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(
CliprdrChannel_Class,
CliprdrChannel_DataResponse,
NULL, NULL);
/* Set data and size */
data_response->data = (BYTE*) output;
guac_iconv(GUAC_READ_UTF8, &input, rdp_client->clipboard->length,
writer, &output, GUAC_RDP_CLIPBOARD_MAX_LENGTH);
data_response->size = ((BYTE*) output) - data_response->data;
/* Send response */
freerdp_channels_send_event(channels, (wMessage*) data_response);
#endif
}
void guac_rdp_process_cb_data_response(guac_client* client,
RDP_CB_DATA_RESPONSE_EVENT* event) {
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
char received_data[GUAC_RDP_CLIPBOARD_MAX_LENGTH];
guac_iconv_read* reader;
const char* input = (char*) event->data;
char* output = received_data;
/* Find correct source encoding */
switch (rdp_client->requested_clipboard_format) {
/* Non-Unicode */
case CF_TEXT:
reader = GUAC_READ_CP1252;
break;
/* Unicode (UTF-16) */
case CF_UNICODETEXT:
reader = GUAC_READ_UTF16;
break;
default:
guac_client_log(client, GUAC_LOG_ERROR, "Requested clipboard data in "
"unsupported format %i",
rdp_client->requested_clipboard_format);
return;
}
/* Convert send clipboard data */
if (guac_iconv(reader, &input, event->size,
GUAC_WRITE_UTF8, &output, sizeof(received_data))) {
int length = strnlen(received_data, sizeof(received_data));
guac_common_clipboard_reset(rdp_client->clipboard, "text/plain");
guac_common_clipboard_append(rdp_client->clipboard, received_data, length);
guac_common_clipboard_send(rdp_client->clipboard, client);
}
}

View File

@ -1,110 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#ifndef __GUAC_RDP_RDP_CLIPRDR_H
#define __GUAC_RDP_RDP_CLIPRDR_H
#include "config.h"
#include <guacamole/client.h>
#include <freerdp/client/cliprdr.h>
#include <winpr/stream.h>
/**
* Clipboard format for text encoded in Windows CP1252.
*/
#define GUAC_RDP_CLIPBOARD_FORMAT_CP1252 1
/**
* Clipboard format for text encoded in UTF-16.
*/
#define GUAC_RDP_CLIPBOARD_FORMAT_UTF16 2
/**
* Called within the main RDP connection thread whenever a CLIPRDR message is
* received. This function will dispatch that message to an appropriate
* function, specific to that message type.
*
* @param client
* The guac_client associated with the current RDP session.
*
* @param event
* The received CLIPRDR message.
*/
void guac_rdp_process_cliprdr_event(guac_client* client, wMessage* event);
/**
* Handles the given CLIPRDR event, which MUST be a Monitor Ready event. It
* is the responsibility of this function to respond to the Monitor Ready
* event with a list of supported clipboard formats.
*
* @param client
* The guac_client associated with the current RDP session.
*
* @param event
* The received CLIPRDR message, which must be a Monitor Ready event.
*/
void guac_rdp_process_cb_monitor_ready(guac_client* client, wMessage* event);
/**
* Handles the given CLIPRDR event, which MUST be a Format List event. It
* is the responsibility of this function to respond to the Format List
* event with a request for clipboard data in one of the enumerated formats.
* This event is fired whenever remote clipboard data is available.
*
* @param client
* The guac_client associated with the current RDP session.
*
* @param event
* The received CLIPRDR message, which must be a Format List event.
*/
void guac_rdp_process_cb_format_list(guac_client* client,
RDP_CB_FORMAT_LIST_EVENT* event);
/**
* Handles the given CLIPRDR event, which MUST be a Data Request event. It
* is the responsibility of this function to respond to the Data Request
* event with a data response containing the current clipoard contents.
*
* @param client
* The guac_client associated with the current RDP session.
*
* @param event
* The received CLIPRDR message, which must be a Data Request event.
*/
void guac_rdp_process_cb_data_request(guac_client* client,
RDP_CB_DATA_REQUEST_EVENT* event);
/**
* Handles the given CLIPRDR event, which MUST be a Data Response event. It
* is the responsibility of this function to read and forward the received
* clipboard data to connected clients.
*
* @param client
* The guac_client associated with the current RDP session.
*
* @param event
* The received CLIPRDR message, which must be a Data Response event.
*/
void guac_rdp_process_cb_data_response(guac_client* client,
RDP_CB_DATA_RESPONSE_EVENT* event);
#endif

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "client.h" #include "client.h"
#include "common/clipboard.h"
#include "rdp.h" #include "rdp.h"
#include "rdp_fs.h" #include "rdp_fs.h"
#if 0 #if 0
@ -153,24 +152,6 @@ int guac_rdp_svc_pipe_handler(guac_user* user, guac_stream* stream,
} }
int guac_rdp_clipboard_handler(guac_user* user, guac_stream* stream,
char* mimetype) {
guac_client* client = user->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_stream* rdp_stream;
/* Init stream data */
stream->data = rdp_stream = malloc(sizeof(guac_rdp_stream));
stream->blob_handler = guac_rdp_clipboard_blob_handler;
stream->end_handler = guac_rdp_clipboard_end_handler;
rdp_stream->type = GUAC_RDP_INBOUND_CLIPBOARD_STREAM;
guac_common_clipboard_reset(rdp_client->clipboard, mimetype);
return 0;
}
int guac_rdp_upload_blob_handler(guac_user* user, guac_stream* stream, int guac_rdp_upload_blob_handler(guac_user* user, guac_stream* stream,
void* data, int length) { void* data, int length) {
@ -237,16 +218,6 @@ int guac_rdp_svc_blob_handler(guac_user* user, guac_stream* stream,
} }
int guac_rdp_clipboard_blob_handler(guac_user* user, guac_stream* stream,
void* data, int length) {
guac_client* client = user->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_common_clipboard_append(rdp_client->clipboard, (char*) data, length);
return 0;
}
int guac_rdp_upload_end_handler(guac_user* user, guac_stream* stream) { int guac_rdp_upload_end_handler(guac_user* user, guac_stream* stream) {
guac_client* client = user->client; guac_client* client = user->client;
@ -275,41 +246,6 @@ int guac_rdp_upload_end_handler(guac_user* user, guac_stream* stream) {
} }
int guac_rdp_clipboard_end_handler(guac_user* user, guac_stream* stream) {
guac_client* client = user->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Terminate clipboard data with NULL */
guac_common_clipboard_append(rdp_client->clipboard, "", 1);
/* Notify RDP server of new data, if connected */
#if 0
freerdp* rdp_inst = rdp_client->rdp_inst;
if (rdp_inst != NULL) {
rdpChannels* channels = rdp_inst->context->channels;
RDP_CB_FORMAT_LIST_EVENT* format_list =
(RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(
CliprdrChannel_Class,
CliprdrChannel_FormatList,
NULL, NULL);
/* Notify server that text data is now available */
format_list->formats = (UINT32*) malloc(sizeof(UINT32) * 2);
format_list->formats[0] = CF_TEXT;
format_list->formats[1] = CF_UNICODETEXT;
format_list->num_formats = 2;
freerdp_channels_send_event(channels, (wMessage*) format_list);
}
#endif
return 0;
}
int guac_rdp_download_ack_handler(guac_user* user, guac_stream* stream, int guac_rdp_download_ack_handler(guac_user* user, guac_stream* stream,
char* message, guac_protocol_status status) { char* message, guac_protocol_status status) {

View File

@ -118,12 +118,7 @@ typedef enum guac_rdp_stream_type {
/** /**
* The inbound half of a static virtual channel. * The inbound half of a static virtual channel.
*/ */
GUAC_RDP_INBOUND_SVC_STREAM, GUAC_RDP_INBOUND_SVC_STREAM
/**
* An inbound stream of clipboard data.
*/
GUAC_RDP_INBOUND_CLIPBOARD_STREAM
} guac_rdp_stream_type; } guac_rdp_stream_type;
@ -171,11 +166,6 @@ guac_user_file_handler guac_rdp_upload_file_handler;
*/ */
guac_user_pipe_handler guac_rdp_svc_pipe_handler; guac_user_pipe_handler guac_rdp_svc_pipe_handler;
/**
* Handler for inbound clipboard data.
*/
guac_user_clipboard_handler guac_rdp_clipboard_handler;
/** /**
* Handler for stream data related to file uploads. * Handler for stream data related to file uploads.
*/ */
@ -186,21 +176,11 @@ guac_user_blob_handler guac_rdp_upload_blob_handler;
*/ */
guac_user_blob_handler guac_rdp_svc_blob_handler; guac_user_blob_handler guac_rdp_svc_blob_handler;
/**
* Handler for stream data related to clipboard.
*/
guac_user_blob_handler guac_rdp_clipboard_blob_handler;
/** /**
* Handler for end-of-stream related to file uploads. * Handler for end-of-stream related to file uploads.
*/ */
guac_user_end_handler guac_rdp_upload_end_handler; guac_user_end_handler guac_rdp_upload_end_handler;
/**
* Handler for end-of-stream related to clipboard.
*/
guac_user_end_handler guac_rdp_clipboard_end_handler;
/** /**
* Handler for acknowledgements of receipt of data related to file downloads. * Handler for acknowledgements of receipt of data related to file downloads.
*/ */