guacamole-spice-protocol/src/protocols/rdp/rdp_cliprdr.c
2016-03-28 20:39:19 -07:00

270 lines
8.0 KiB
C

/*
* 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 "rdp.h"
#include "rdp_cliprdr.h"
#include "guac_clipboard.h"
#include "guac_iconv.h"
#include <freerdp/channels/channels.h>
#include <freerdp/freerdp.h>
#include <freerdp/utils/event.h>
#include <guacamole/client.h>
#ifdef ENABLE_WINPR
#include <winpr/wtypes.h>
#else
#include "compat/winpr-wtypes.h"
#endif
#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
#include <freerdp/client/cliprdr.h>
#else
#include "compat/client-cliprdr.h"
#endif
#include <stdlib.h>
#include <string.h>
void guac_rdp_process_cliprdr_event(guac_client* client, wMessage* event) {
#ifdef LEGACY_EVENT
switch (event->event_type) {
#else
switch (GetMessageType(event->id)) {
#endif
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:
#ifdef LEGACY_EVENT
guac_client_log(client, GUAC_LOG_INFO,
"Unknown cliprdr event type: 0x%x",
event->event_type);
#else
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) {
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] = CB_FORMAT_TEXT;
format_list->formats[1] = CB_FORMAT_UNICODETEXT;
format_list->num_formats = 2;
freerdp_channels_send_event(channels, (wMessage*) format_list);
}
/**
* 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) {
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);
}
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] == CB_FORMAT_TEXT)
formats |= GUAC_RDP_CLIPBOARD_FORMAT_CP1252;
else if (event->formats[i] == CB_FORMAT_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, CB_FORMAT_UNICODETEXT);
return;
}
/* Use plain text if Unicode unavailable */
if (formats & GUAC_RDP_CLIPBOARD_FORMAT_CP1252) {
__guac_rdp_cb_request_format(client, CB_FORMAT_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) {
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 CB_FORMAT_TEXT:
writer = GUAC_WRITE_CP1252;
break;
case CB_FORMAT_UNICODETEXT:
writer = GUAC_WRITE_UTF16;
break;
default:
guac_client_log(client, GUAC_LOG_ERROR,
"Server requested unsupported clipboard data type");
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);
}
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 CB_FORMAT_TEXT:
reader = GUAC_READ_CP1252;
break;
/* Unicode (UTF-16) */
case CB_FORMAT_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);
}
}