GUACAMOLE-249: Restore support for CLIPRDR channel.
This commit is contained in:
parent
831606a4e9
commit
b64b8f375a
@ -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 \
|
||||||
|
@ -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;
|
||||||
|
527
src/protocols/rdp/clipboard.c
Normal file
527
src/protocols/rdp/clipboard.c
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
144
src/protocols/rdp/clipboard.h
Normal file
144
src/protocols/rdp/clipboard.h
Normal 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
|
||||||
|
|
@ -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 */
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user