Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
451 lines (408 sloc) 11.7 KB
/*
* Remmina - The GTK+ Remote Desktop Client
* Copyright (C) 2012-2012 Jean-Louis Dupond
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "rdp_plugin.h"
#include "rdp_cliprdr.h"
#include <freerdp/freerdp.h>
#include <freerdp/utils/memory.h>
#include <freerdp/channels/channels.h>
#include <freerdp/plugins/cliprdr.h>
/*
* Get the formats we can export based on the current clipboard data.
*/
void remmina_rdp_cliprdr_get_target_types(uint32** dst_formats, uint16* size, GdkAtom* types, int count)
{
int i;
gboolean image = FALSE;
gboolean text = FALSE;
gboolean textutf8 = FALSE;
int matches = 1;
uint32* formats = (uint32*) xmalloc(sizeof(uint32) * (count+1));
formats[0] = CB_FORMAT_RAW;
for (i = 0; i < count; i++)
{
GdkAtom atom = GDK_POINTER_TO_ATOM(types[i]);
gchar* name = gdk_atom_name(atom);
if (g_strcmp0("UTF8_STRING", name) == 0 || g_strcmp0("text/plain;charset=utf-8", name) == 0)
{
textutf8 = TRUE;
}
if (g_strcmp0("TEXT", name) == 0 || g_strcmp0("text/plain", name) == 0)
{
text = TRUE;
}
if (g_strcmp0("text/html", name) == 0)
{
formats[matches] = CB_FORMAT_HTML;
matches++;
}
if (g_strcmp0("image/png", name) == 0)
{
formats[matches] = CB_FORMAT_PNG;
image = TRUE;
matches++;
}
if (g_strcmp0("image/jpeg", name) == 0)
{
formats[matches] = CB_FORMAT_JPEG;
image = TRUE;
matches++;
}
if (g_strcmp0("image/bmp", name) == 0)
{
formats[matches] = CB_FORMAT_DIB;
image = TRUE;
matches++;
}
g_free(name);
}
//Only add text formats if we don't have image formats
if (!image)
{
if (textutf8)
{
formats[matches] = CB_FORMAT_UNICODETEXT;
matches++;
}
if (text)
{
formats[matches] = CB_FORMAT_TEXT;
matches++;
}
}
*size = (uint16)matches;
*dst_formats = (uint32*) xmalloc(sizeof(uint32) * matches);
memcpy(*dst_formats, formats, sizeof(uint32) * matches);
g_free(formats);
}
int remmina_rdp_cliprdr_send_format_list_event(RemminaProtocolWidget* gp)
{
GtkClipboard* clipboard;
GdkAtom* targets;
gboolean result = 0;
gint count;
RDP_EVENT* rdp_event;
RDP_CB_FORMAT_LIST_EVENT* format_list_event;
rfContext* rfi = GET_DATA(gp);
/* Lets see if we have something in our clipboard */
THREADS_ENTER
clipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD);
if (clipboard)
{
result = gtk_clipboard_wait_for_targets(clipboard, &targets, &count);
}
THREADS_LEAVE
if (!result)
return 1;
rdp_event = (RDP_EVENT*) xnew(RDP_CB_FORMAT_LIST_EVENT);
rdp_event->event_class = RDP_EVENT_CLASS_CLIPRDR;
rdp_event->event_type = RDP_EVENT_TYPE_CB_FORMAT_LIST;
format_list_event = (RDP_CB_FORMAT_LIST_EVENT*) rdp_event;
remmina_rdp_cliprdr_get_target_types(&format_list_event->formats, &format_list_event->num_formats, targets, count);
g_free(targets);
return freerdp_channels_send_event(rfi->channels, (RDP_EVENT*) format_list_event);
}
static uint8* lf2crlf(uint8* data, int* size)
{
uint8 c;
uint8* outbuf;
uint8* out;
uint8* in_end;
uint8* in;
int out_size;
out_size = (*size) * 2 + 1;
outbuf = (uint8*) xmalloc(out_size);
out = outbuf;
in = data;
in_end = data + (*size);
while (in < in_end)
{
c = *in++;
if (c == '\n')
{
*out++ = '\r';
*out++ = '\n';
}
else
{
*out++ = c;
}
}
*out++ = 0;
*size = out - outbuf;
return outbuf;
}
static void crlf2lf(uint8* data, int* size)
{
uint8 c;
uint8* out;
uint8* in;
uint8* in_end;
out = data;
in = data;
in_end = data + (*size);
while (in < in_end)
{
c = *in++;
if (c != '\r')
*out++ = c;
}
*size = out - data;
}
uint8* remmina_rdp_cliprdr_get_data(RemminaProtocolWidget* gp, uint32 format, int* size)
{
rfContext* rfi = GET_DATA(gp);
GtkClipboard* clipboard;
uint8* inbuf = NULL;
uint8* outbuf = NULL;
GdkPixbuf *image = NULL;
THREADS_ENTER
clipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD);
if (clipboard)
{
if (format == CB_FORMAT_TEXT || format == CB_FORMAT_UNICODETEXT || format == CB_FORMAT_HTML)
{
inbuf = (uint8*)gtk_clipboard_wait_for_text(clipboard);
}
if (format == CB_FORMAT_PNG || format == CB_FORMAT_JPEG || format == CB_FORMAT_DIB)
{
image = gtk_clipboard_wait_for_image(clipboard);
}
}
THREADS_LEAVE
/* No data received, send nothing */
if (inbuf == NULL && image == NULL)
{
*size = 0;
return NULL;
}
if (format == CB_FORMAT_TEXT || format == CB_FORMAT_HTML || format == CB_FORMAT_UNICODETEXT)
{
*size = strlen((char*)inbuf);
inbuf = lf2crlf(inbuf, size);
if (format == CB_FORMAT_TEXT)
{
outbuf = inbuf;
}
if (format == CB_FORMAT_HTML)
{
//TODO: check if we need special handling for HTML
outbuf = inbuf;
}
if (format == CB_FORMAT_UNICODETEXT)
{
size_t out_size;
UNICONV* uniconv;
uniconv = freerdp_uniconv_new();
outbuf = (uint8*) freerdp_uniconv_out(uniconv, (char*) inbuf, &out_size);
freerdp_uniconv_free(uniconv);
g_free(inbuf);
*size = out_size + 2;
}
}
if (format == CB_FORMAT_PNG || format == CB_FORMAT_JPEG || format == CB_FORMAT_DIB)
{
gchar* data;
gsize buffersize;
if (format == CB_FORMAT_PNG)
{
gdk_pixbuf_save_to_buffer(image, &data, &buffersize, "png", NULL, NULL);
outbuf = (uint8*) xmalloc(buffersize);
memcpy(outbuf, data, buffersize);
*size = buffersize;
}
if (format == CB_FORMAT_JPEG)
{
gdk_pixbuf_save_to_buffer(image, &data, &buffersize, "jpeg", NULL, NULL);
outbuf = (uint8*) xmalloc(buffersize);
memcpy(outbuf, data, buffersize);
*size = buffersize;
}
if (format == CB_FORMAT_DIB)
{
gdk_pixbuf_save_to_buffer(image, &data, &buffersize, "bmp", NULL, NULL);
*size = buffersize - 14;
outbuf = (uint8*) xmalloc(*size);
memcpy(outbuf, data + 14, *size);
}
g_object_unref(image);
}
return outbuf;
}
void remmina_rdp_cliprdr_parse_response_event(RemminaProtocolWidget* gp, RDP_EVENT* event)
{
GtkClipboard* clipboard;
GdkPixbuf *image = NULL;
uint8* data;
int size;
gboolean text = FALSE;
gboolean img = FALSE;
rfContext* rfi = GET_DATA(gp);
RDP_CB_DATA_RESPONSE_EVENT* data_response_event;
GdkPixbufLoader *pixbuf;
data_response_event = (RDP_CB_DATA_RESPONSE_EVENT*) event;
data = data_response_event->data;
size = data_response_event->size;
if (rfi->requested_format == CB_FORMAT_TEXT || rfi->requested_format == CB_FORMAT_UNICODETEXT || rfi->requested_format == CB_FORMAT_HTML)
{
if (rfi->requested_format == CB_FORMAT_UNICODETEXT)
{
UNICONV* uniconv;
uniconv = freerdp_uniconv_new();
data = (uint8*) freerdp_uniconv_in(uniconv, data, size);
size = strlen((char*) data);
freerdp_uniconv_free(uniconv);
}
crlf2lf(data, &size);
text = TRUE;
}
if (rfi->requested_format == CB_FORMAT_DIB || rfi->requested_format == CB_FORMAT_PNG || rfi->requested_format == CB_FORMAT_JPEG)
{
/* Reconstruct header */
if (rfi->requested_format == CB_FORMAT_DIB)
{
STREAM* s;
uint16 bpp;
uint32 offset;
uint32 ncolors;
s = stream_new(0);
stream_attach(s, data, size);
stream_seek(s, 14);
stream_read_uint16(s, bpp);
stream_read_uint32(s, ncolors);
offset = 14 + 40 + (bpp <= 8 ? (ncolors == 0 ? (1 << bpp) : ncolors) * 4 : 0);
stream_detach(s);
stream_free(s);
s = stream_new(14 + size);
stream_write_uint8(s, 'B');
stream_write_uint8(s, 'M');
stream_write_uint32(s, 14 + size);
stream_write_uint32(s, 0);
stream_write_uint32(s, offset);
stream_write(s, data, size);
data = stream_get_head(s);
size = stream_get_length(s);
stream_detach(s);
stream_free(s);
}
pixbuf = gdk_pixbuf_loader_new();
gdk_pixbuf_loader_write(pixbuf, data, size, NULL);
image = gdk_pixbuf_loader_get_pixbuf(pixbuf);
img = TRUE;
}
THREADS_ENTER
clipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD);
if (clipboard)
{
if (text || img)
{
rfi->clipboard_wait = 2;
}
if (text)
{
gtk_clipboard_set_text(clipboard, (gchar*)data, size);
gtk_clipboard_store(clipboard);
}
if (img)
{
gtk_clipboard_set_image(clipboard, image);
gtk_clipboard_store(clipboard);
gdk_pixbuf_loader_close(pixbuf, NULL);
g_object_unref(pixbuf);
}
}
THREADS_LEAVE
}
void remmina_handle_channel_event(RemminaProtocolWidget* gp, RDP_EVENT* event)
{
RDP_EVENT* rdp_event = NULL;
rfContext* rfi = GET_DATA(gp);
switch (event->event_class)
{
case RDP_EVENT_CLASS_CLIPRDR:
if (event->event_type == RDP_EVENT_TYPE_CB_MONITOR_READY)
{
/* Sending our format list */
remmina_rdp_cliprdr_send_format_list_event(gp);
}
if (event->event_type == RDP_EVENT_TYPE_CB_FORMAT_LIST)
{
/* We received a FORMAT_LIST from the server, update our clipboard */
int i;
uint32 format = CB_FORMAT_RAW;
RDP_CB_FORMAT_LIST_EVENT* format_list_event;
format_list_event = (RDP_CB_FORMAT_LIST_EVENT*) event;
for (i = 0; i < format_list_event->num_formats; i++)
{
if (format_list_event->formats[i] > format)
{
if (format_list_event->formats[i] == CB_FORMAT_UNICODETEXT)
{
format = CB_FORMAT_UNICODETEXT;
}
if (format_list_event->formats[i] == CB_FORMAT_DIB)
{
format = CB_FORMAT_DIB;
}
if (format_list_event->formats[i] == CB_FORMAT_JPEG)
{
format = CB_FORMAT_JPEG;
}
if (format_list_event->formats[i] == CB_FORMAT_PNG)
{
format = CB_FORMAT_PNG;
}
if (format_list_event->formats[i] == CB_FORMAT_TEXT)
{
format = CB_FORMAT_TEXT;
}
}
}
rfi->requested_format = format;
/* Request Clipboard data of the server */
RDP_CB_DATA_REQUEST_EVENT* data_request_event;
rdp_event = (RDP_EVENT*) xnew(RDP_CB_DATA_REQUEST_EVENT);
rdp_event->event_class = RDP_EVENT_CLASS_CLIPRDR;
rdp_event->event_type = RDP_EVENT_TYPE_CB_DATA_REQUEST;
data_request_event = (RDP_CB_DATA_REQUEST_EVENT*) rdp_event;
data_request_event->format = format;
freerdp_channels_send_event(rfi->channels, (RDP_EVENT*) data_request_event);
}
if (event->event_type == RDP_EVENT_TYPE_CB_DATA_REQUEST)
{
uint8* data;
int size;
RDP_CB_DATA_REQUEST_EVENT* data_request_event = (RDP_CB_DATA_REQUEST_EVENT*) event;
RDP_CB_DATA_RESPONSE_EVENT* data_response_event;
/* Send Data */
rdp_event = (RDP_EVENT*) xnew(RDP_CB_DATA_RESPONSE_EVENT);
rdp_event->event_class = RDP_EVENT_CLASS_CLIPRDR;
rdp_event->event_type = RDP_EVENT_TYPE_CB_DATA_RESPONSE;
data_response_event = (RDP_CB_DATA_RESPONSE_EVENT*) rdp_event;
data = remmina_rdp_cliprdr_get_data(gp, data_request_event->format, &size);
data_response_event->data = data;
data_response_event->size = size;
freerdp_channels_send_event(rfi->channels, rdp_event);
}
if (event->event_type == RDP_EVENT_TYPE_CB_DATA_RESPONSE)
{
remmina_rdp_cliprdr_parse_response_event(gp, event);
}
}
}