Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.git] / client / X11 / xf_cliprdr.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Client
3  * X11 Clipboard Redirection
4  *
5  * Copyright 2010-2011 Vic Lee
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include <stdlib.h>
21 #include <X11/Xlib.h>
22 #include <X11/Xatom.h>
23 #include <freerdp/utils/event.h>
24 #include <freerdp/utils/stream.h>
25 #include <freerdp/utils/unicode.h>
26 #include <freerdp/plugins/cliprdr.h>
27
28 #include "xf_cliprdr.h"
29
30 typedef struct clipboard_format_mapping clipboardFormatMapping;
31 struct clipboard_format_mapping
32 {
33         Atom target_format;
34         uint32 format_id;
35 };
36
37 typedef struct clipboard_context clipboardContext;
38 struct clipboard_context
39 {
40         rdpChannels* channels;
41         Window root_window;
42         Atom clipboard_atom;
43         Atom property_atom;
44         Atom identity_atom;
45
46         clipboardFormatMapping format_mappings[20];
47         int num_format_mappings;
48
49         /* server->client data */
50         uint32* formats;
51         int num_formats;
52         Atom targets[20];
53         int num_targets;
54         uint8* data;
55         uint32 data_format;
56         uint32 data_alt_format;
57         int data_length;
58         XEvent* respond;
59
60         /* client->server data */
61         Window owner;
62         int request_index;
63         boolean sync;
64
65         /* INCR mechanism */
66         Atom incr_atom;
67         boolean incr_starts;
68         uint8* incr_data;
69         int incr_data_length;
70 };
71
72 void xf_cliprdr_init(xfInfo* xfi, rdpChannels* chanman)
73 {
74         int n;
75         uint32 id;
76         clipboardContext* cb;
77
78         cb = xnew(clipboardContext);
79         xfi->clipboard_context = cb;
80
81         cb->channels = chanman;
82         cb->request_index = -1;
83
84         cb->root_window = DefaultRootWindow(xfi->display);
85         cb->clipboard_atom = XInternAtom(xfi->display, "CLIPBOARD", false);
86
87         if (cb->clipboard_atom == None)
88         {
89                 DEBUG_WARN("unable to get CLIPBOARD atom");
90         }
91
92         id = 1;
93         cb->property_atom = XInternAtom(xfi->display, "_FREERDP_CLIPRDR", false);
94         cb->identity_atom = XInternAtom(xfi->display, "_FREERDP_CLIPRDR_ID", false);
95
96         XChangeProperty(xfi->display, xfi->drawable, cb->identity_atom,
97                         XA_INTEGER, 32, PropModeReplace, (uint8*) &id, 1);
98
99         XSelectInput(xfi->display, cb->root_window, PropertyChangeMask);
100
101         n = 0;
102         cb->format_mappings[n].target_format = XInternAtom(xfi->display, "_FREERDP_RAW", false);
103         cb->format_mappings[n].format_id = CB_FORMAT_RAW;
104
105         n++;
106         cb->format_mappings[n].target_format = XInternAtom(xfi->display, "UTF8_STRING", false);
107         cb->format_mappings[n].format_id = CB_FORMAT_UNICODETEXT;
108
109         n++;
110         cb->format_mappings[n].target_format = XA_STRING;
111         cb->format_mappings[n].format_id = CB_FORMAT_TEXT;
112
113         n++;
114         cb->format_mappings[n].target_format = XInternAtom(xfi->display, "image/png", false);
115         cb->format_mappings[n].format_id = CB_FORMAT_PNG;
116
117         n++;
118         cb->format_mappings[n].target_format = XInternAtom(xfi->display, "image/jpeg", false);
119         cb->format_mappings[n].format_id = CB_FORMAT_JPEG;
120
121         n++;
122         cb->format_mappings[n].target_format = XInternAtom(xfi->display, "image/gif", false);
123         cb->format_mappings[n].format_id = CB_FORMAT_GIF;
124
125         n++;
126         cb->format_mappings[n].target_format = XInternAtom(xfi->display, "image/bmp", false);
127         cb->format_mappings[n].format_id = CB_FORMAT_DIB;
128
129         n++;
130         cb->format_mappings[n].target_format = XInternAtom(xfi->display, "text/html", false);
131         cb->format_mappings[n].format_id = CB_FORMAT_HTML;
132
133         cb->num_format_mappings = n + 1;
134         cb->targets[0] = XInternAtom(xfi->display, "TIMESTAMP", false);
135         cb->targets[1] = XInternAtom(xfi->display, "TARGETS", false);
136         cb->num_targets = 2;
137
138         cb->incr_atom = XInternAtom(xfi->display, "INCR", false);
139 }
140
141 void xf_cliprdr_uninit(xfInfo* xfi)
142 {
143         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
144
145         if (cb)
146         {
147                 xfree(cb->formats);
148                 xfree(cb->data);
149                 xfree(cb->respond);
150                 xfree(cb->incr_data);
151                 xfree(cb);
152                 xfi->clipboard_context = NULL;
153         }
154 }
155
156 static uint8* lf2crlf(uint8* data, int* size)
157 {
158         uint8 c;
159         uint8* outbuf;
160         uint8* out;
161         uint8* in_end;
162         uint8* in;
163         int out_size;
164
165         out_size = (*size) * 2 + 1;
166         outbuf = (uint8*) xzalloc(out_size);
167         out = outbuf;
168         in = data;
169         in_end = data + (*size);
170
171         while (in < in_end)
172         {
173                 c = *in++;
174                 if (c == '\n')
175                 {
176                         *out++ = '\r';
177                         *out++ = '\n';
178                 }
179                 else
180                 {
181                         *out++ = c;
182                 }
183         }
184
185         *out++ = 0;
186         *size = out - outbuf;
187
188         return outbuf;
189 }
190
191 static void crlf2lf(uint8* data, int* size)
192 {
193         uint8 c;
194         uint8* out;
195         uint8* in;
196         uint8* in_end;
197
198         out = data;
199         in = data;
200         in_end = data + (*size);
201
202         while (in < in_end)
203         {
204                 c = *in++;
205
206                 if (c != '\r')
207                         *out++ = c;
208         }
209
210         *size = out - data;
211 }
212
213 static void be2le(uint8* data, int size)
214 {
215         uint8 c;
216
217         while (size >= 2)
218         {
219                 c = data[0];
220                 data[0] = data[1];
221                 data[1] = c;
222
223                 data += 2;
224                 size -= 2;
225         }
226 }
227
228 static boolean xf_cliprdr_is_self_owned(xfInfo* xfi)
229 {
230         Atom type;
231         uint32 id = 0;
232         uint32* pid = NULL;
233         int format, result = 0;
234         unsigned long length, bytes_left;
235         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
236
237         cb->owner = XGetSelectionOwner(xfi->display, cb->clipboard_atom);
238
239         if (cb->owner != None)
240         {
241                 result = XGetWindowProperty(xfi->display, cb->owner,
242                         cb->identity_atom, 0, 4, 0, XA_INTEGER,
243                         &type, &format, &length, &bytes_left, (uint8**) &pid);
244         }
245
246         if (pid)
247         {
248                 id = *pid;
249                 XFree(pid);
250         }
251
252         if ((cb->owner == None) || (cb->owner == xfi->drawable))
253                 return false;
254
255         if (result != Success)
256                 return false;
257
258         return (id ? true : false);
259 }
260
261 static int xf_cliprdr_select_format_by_id(clipboardContext* cb, uint32 format_id)
262 {
263         int i;
264
265         for (i = 0; i < cb->num_format_mappings; i++)
266         {
267                 if (cb->format_mappings[i].format_id == format_id)
268                         return i;
269         }
270
271         return -1;
272 }
273
274 static int xf_cliprdr_select_format_by_atom(clipboardContext* cb, Atom target)
275 {
276         int i;
277         int j;
278
279         if (cb->formats == NULL)
280                 return -1;
281
282         for (i = 0; i < cb->num_format_mappings; i++)
283         {
284                 if (cb->format_mappings[i].target_format != target)
285                         continue;
286
287                 if (cb->format_mappings[i].format_id == CB_FORMAT_RAW)
288                         return i;
289
290                 for (j = 0; j < cb->num_formats; j++)
291                 {
292                         if (cb->format_mappings[i].format_id == cb->formats[j])
293                                 return i;
294                 }
295         }
296
297         return -1;
298 }
299
300 static void xf_cliprdr_send_raw_format_list(xfInfo* xfi)
301 {
302         Atom type;
303         uint8* format_data;
304         int format, result;
305         unsigned long length, bytes_left;
306         RDP_CB_FORMAT_LIST_EVENT* event;
307         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
308
309         result = XGetWindowProperty(xfi->display, cb->root_window,
310                 cb->property_atom, 0, 3600, 0, XA_STRING,
311                 &type, &format, &length, &bytes_left, (uint8**) &format_data);
312
313         if (result != Success)
314         {
315                 DEBUG_WARN("XGetWindowProperty failed");
316                 return;
317         }
318         DEBUG_X11_CLIPRDR("format=%d len=%d bytes_left=%d", format, (int) length, (int) bytes_left);
319
320         event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR,
321                 RDP_EVENT_TYPE_CB_FORMAT_LIST, NULL, NULL);
322
323         event->raw_format_data = (uint8*) xmalloc(length);
324         memcpy(event->raw_format_data, format_data, length);
325         event->raw_format_data_size = length;
326         XFree(format_data);
327
328         freerdp_channels_send_event(cb->channels, (RDP_EVENT*) event);
329 }
330
331 static void xf_cliprdr_send_null_format_list(xfInfo* xfi)
332 {
333         RDP_CB_FORMAT_LIST_EVENT* event;
334         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
335
336         event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR,
337                 RDP_EVENT_TYPE_CB_FORMAT_LIST, NULL, NULL);
338
339         event->num_formats = 0;
340
341         freerdp_channels_send_event(cb->channels, (RDP_EVENT*) event);
342 }
343
344 static void xf_cliprdr_send_supported_format_list(xfInfo* xfi)
345 {
346         int i;
347         RDP_CB_FORMAT_LIST_EVENT* event;
348         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
349
350         event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR,
351                 RDP_EVENT_TYPE_CB_FORMAT_LIST, NULL, NULL);
352
353         event->formats = (uint32*) xmalloc(sizeof(uint32) * cb->num_format_mappings);
354         event->num_formats = cb->num_format_mappings;
355
356         for (i = 0; i < cb->num_format_mappings; i++)
357                 event->formats[i] = cb->format_mappings[i].format_id;
358
359         freerdp_channels_send_event(cb->channels, (RDP_EVENT*) event);
360 }
361
362 static void xf_cliprdr_send_format_list(xfInfo* xfi)
363 {
364         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
365
366         if (xf_cliprdr_is_self_owned(xfi))
367         {
368                 xf_cliprdr_send_raw_format_list(xfi);
369         }
370         else if (cb->owner == None)
371         {
372                 xf_cliprdr_send_null_format_list(xfi);
373         }
374         else if (cb->owner != xfi->drawable)
375         {
376                 /* Request the owner for TARGETS, and wait for SelectionNotify event */
377                 XConvertSelection(xfi->display, cb->clipboard_atom,
378                         cb->targets[1], cb->property_atom, xfi->drawable, CurrentTime);
379         }
380 }
381
382 static void xf_cliprdr_send_data_request(xfInfo* xfi, uint32 format)
383 {
384         RDP_CB_DATA_REQUEST_EVENT* event;
385         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
386
387         event = (RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR,
388                 RDP_EVENT_TYPE_CB_DATA_REQUEST, NULL, NULL);
389
390         event->format = format;
391
392         freerdp_channels_send_event(cb->channels, (RDP_EVENT*) event);
393 }
394
395 static void xf_cliprdr_send_data_response(xfInfo* xfi, uint8* data, int size)
396 {
397         RDP_CB_DATA_RESPONSE_EVENT* event;
398         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
399
400         event = (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR,
401                 RDP_EVENT_TYPE_CB_DATA_RESPONSE, NULL, NULL);
402
403         event->data = data;
404         event->size = size;
405
406         freerdp_channels_send_event(cb->channels, (RDP_EVENT*) event);
407 }
408
409 static void xf_cliprdr_send_null_data_response(xfInfo* xfi)
410 {
411         xf_cliprdr_send_data_response(xfi, NULL, 0);
412 }
413
414 static void xf_cliprdr_process_cb_monitor_ready_event(xfInfo* xfi)
415 {
416         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
417
418         xf_cliprdr_send_format_list(xfi);
419         cb->sync = true;
420 }
421
422 static void xf_cliprdr_process_cb_data_request_event(xfInfo* xfi, RDP_CB_DATA_REQUEST_EVENT* event)
423 {
424         int i;
425         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
426
427         DEBUG_X11_CLIPRDR("format %d", event->format);
428
429         if (xf_cliprdr_is_self_owned(xfi))
430         {
431                 /* CB_FORMAT_RAW */
432                 i = 0;
433                 XChangeProperty(xfi->display, xfi->drawable, cb->property_atom,
434                         XA_INTEGER, 32, PropModeReplace, (uint8*) &event->format, 1);
435         }
436         else
437         {
438                 i = xf_cliprdr_select_format_by_id(cb, event->format);
439         }
440
441         if (i < 0)
442         {
443                 DEBUG_X11_CLIPRDR("unsupported format requested");
444                 xf_cliprdr_send_null_data_response(xfi);
445         }
446         else
447         {
448                 cb->request_index = i;
449
450                 DEBUG_X11_CLIPRDR("target=%d", (int) cb->format_mappings[i].target_format);
451
452                 XConvertSelection(xfi->display, cb->clipboard_atom,
453                         cb->format_mappings[i].target_format, cb->property_atom,
454                         xfi->drawable, CurrentTime);
455                 XFlush(xfi->display);
456                 /* After this point, we expect a SelectionNotify event from the clipboard owner. */
457         }
458 }
459
460 static void xf_cliprdr_get_requested_targets(xfInfo* xfi)
461 {
462         int num;
463         int i, j;
464         Atom atom;
465         int format;
466         uint8* data = NULL;
467         unsigned long length, bytes_left;
468         RDP_CB_FORMAT_LIST_EVENT* event;
469         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
470
471         XGetWindowProperty(xfi->display, xfi->drawable, cb->property_atom,
472                 0, 200, 0, XA_ATOM,
473                 &atom, &format, &length, &bytes_left, &data);
474
475         DEBUG_X11_CLIPRDR("type=%d format=%d length=%d bytes_left=%d",
476                 (int) atom, format, (int) length, (int) bytes_left);
477
478         if (length > 0)
479         {
480                 event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR,
481                         RDP_EVENT_TYPE_CB_FORMAT_LIST, NULL, NULL);
482
483                 event->formats = (uint32*) xmalloc(sizeof(uint32) * cb->num_format_mappings);
484                 num = 0;
485                 for (i = 0; i < length; i++)
486                 {
487                         atom = ((Atom*) data)[i];
488                         DEBUG_X11("atom %d", (int) atom);
489                         for (j = 0; j < cb->num_format_mappings; j++)
490                         {
491                                 if (cb->format_mappings[j].target_format == atom)
492                                 {
493                                         DEBUG_X11("found format %d for atom %d",
494                                                 cb->format_mappings[j].format_id, (int)atom);
495                                         event->formats[num++] = cb->format_mappings[j].format_id;
496                                         break;
497                                 }
498                         }
499                 }
500                 event->num_formats = num;
501                 XFree(data);
502
503                 freerdp_channels_send_event(cb->channels, (RDP_EVENT*) event);
504         }
505         else
506         {
507                 if (data)
508                         XFree(data);
509
510                 xf_cliprdr_send_null_format_list(xfi);
511         }
512 }
513
514 static uint8* xf_cliprdr_process_requested_raw(uint8* data, int* size)
515 {
516         uint8* outbuf;
517
518         outbuf = (uint8*) xmalloc(*size);
519         memcpy(outbuf, data, *size);
520         return outbuf;
521 }
522
523 static uint8* xf_cliprdr_process_requested_unicodetext(uint8* data, int* size)
524 {
525         uint8* inbuf;
526         uint8* outbuf;
527         size_t out_size;
528         UNICONV* uniconv;
529
530         inbuf = lf2crlf(data, size);
531
532         uniconv = freerdp_uniconv_new();
533         outbuf = (uint8*) freerdp_uniconv_out(uniconv, (char*) inbuf, &out_size);
534         freerdp_uniconv_free(uniconv);
535
536         xfree(inbuf);
537
538         *size = (int) out_size + 2;
539
540         return outbuf;
541 }
542
543 static uint8* xf_cliprdr_process_requested_text(uint8* data, int* size)
544 {
545         uint8* outbuf;
546
547         outbuf = lf2crlf(data, size);
548
549         return outbuf;
550 }
551
552 static uint8* xf_cliprdr_process_requested_dib(uint8* data, int* size)
553 {
554         uint8* outbuf;
555
556         /* length should be at least BMP header (14) + sizeof(BITMAPINFOHEADER) */
557         if (*size < 54)
558         {
559                 DEBUG_X11_CLIPRDR("bmp length %d too short", *size);
560                 return NULL;
561         }
562
563         *size -= 14;
564         outbuf = (uint8*) xzalloc(*size);
565         memcpy(outbuf, data + 14, *size);
566
567         return outbuf;
568 }
569
570 static uint8* xf_cliprdr_process_requested_html(uint8* data, int* size)
571 {
572         uint8* inbuf;
573         uint8* in;
574         uint8* outbuf;
575         char num[11];
576         UNICONV* uniconv;
577
578         inbuf = NULL;
579         if (*size > 2)
580         {
581                 if ((uint8) data[0] == 0xFE && (uint8) data[1] == 0xFF)
582                 {
583                         be2le(data, *size);
584                 }
585
586                 if ((uint8) data[0] == 0xFF && (uint8) data[1] == 0xFE)
587                 {
588                         uniconv = freerdp_uniconv_new();
589                         inbuf = (uint8*) freerdp_uniconv_in(uniconv, data + 2, *size - 2);
590                         freerdp_uniconv_free(uniconv);
591                 }
592         }
593         if (inbuf == NULL)
594         {
595                 inbuf = xzalloc(*size + 1);
596                 memcpy(inbuf, data, *size);
597         }
598
599         outbuf = (uint8*) xzalloc(*size + 200);
600         strcpy((char*) outbuf,
601                 "Version:0.9\r\n"
602                 "StartHTML:0000000000\r\n"
603                 "EndHTML:0000000000\r\n"
604                 "StartFragment:0000000000\r\n"
605                 "EndFragment:0000000000\r\n");
606
607         in = (uint8*) strstr((char*) inbuf, "<body");
608         if (in == NULL)
609         {
610                 in = (uint8*) strstr((char*) inbuf, "<BODY");
611         }
612         /* StartHTML */
613         snprintf(num, sizeof(num), "%010lu", (unsigned long) strlen((char*) outbuf));
614         memcpy(outbuf + 23, num, 10);
615         if (in == NULL)
616         {
617                 strcat((char*) outbuf, "<HTML><BODY>");
618         }
619         strcat((char*) outbuf, "<!--StartFragment-->");
620         /* StartFragment */
621         snprintf(num, sizeof(num), "%010lu", (unsigned long) strlen((char*) outbuf));
622         memcpy(outbuf + 69, num, 10);
623         strcat((char*) outbuf, (char*) inbuf);
624         /* EndFragment */
625         snprintf(num, sizeof(num), "%010lu", (unsigned long) strlen((char*) outbuf));
626         memcpy(outbuf + 93, num, 10);
627         strcat((char*) outbuf, "<!--EndFragment-->");
628         if (in == NULL)
629         {
630                 strcat((char*) outbuf, "</BODY></HTML>");
631         }
632         /* EndHTML */
633         snprintf(num, sizeof(num), "%010lu", (unsigned long) strlen((char*) outbuf));
634         memcpy(outbuf + 43, num, 10);
635
636         *size = strlen((char*) outbuf) + 1;
637         xfree(inbuf);
638
639         return outbuf;
640 }
641
642 static void xf_cliprdr_process_requested_data(xfInfo* xfi, boolean has_data, uint8* data, int size)
643 {
644         uint8* outbuf;
645         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
646
647         if (cb->incr_starts && has_data)
648                 return;
649
650         if (!has_data || data == NULL)
651         {
652                 xf_cliprdr_send_null_data_response(xfi);
653                 return;
654         }
655
656         switch (cb->format_mappings[cb->request_index].format_id)
657         {
658                 case CB_FORMAT_RAW:
659                 case CB_FORMAT_PNG:
660                 case CB_FORMAT_JPEG:
661                 case CB_FORMAT_GIF:
662                         outbuf = xf_cliprdr_process_requested_raw(data, &size);
663                         break;
664
665                 case CB_FORMAT_UNICODETEXT:
666                         outbuf = xf_cliprdr_process_requested_unicodetext(data, &size);
667                         break;
668
669                 case CB_FORMAT_TEXT:
670                         outbuf = xf_cliprdr_process_requested_text(data, &size);
671                         break;
672
673                 case CB_FORMAT_DIB:
674                         outbuf = xf_cliprdr_process_requested_dib(data, &size);
675                         break;
676
677                 case CB_FORMAT_HTML:
678                         outbuf = xf_cliprdr_process_requested_html(data, &size);
679                         break;
680
681                 default:
682                         outbuf = NULL;
683                         break;
684         }
685
686         if (outbuf)
687                 xf_cliprdr_send_data_response(xfi, outbuf, size);
688         else
689                 xf_cliprdr_send_null_data_response(xfi);
690
691         /* Resend the format list, otherwise the server won't request again for the next paste */
692         xf_cliprdr_send_format_list(xfi);
693 }
694
695 static boolean xf_cliprdr_get_requested_data(xfInfo* xfi, Atom target)
696 {
697         Atom type;
698         int format;
699         uint8* data = NULL;
700         boolean has_data = false;
701         unsigned long length, bytes_left, dummy;
702         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
703
704         if ((cb->request_index < 0) ||
705                 (cb->format_mappings[cb->request_index].target_format != target))
706         {
707                 DEBUG_X11_CLIPRDR("invalid target");
708                 xf_cliprdr_send_null_data_response(xfi);
709                 return false;
710         }
711
712         XGetWindowProperty(xfi->display, xfi->drawable,
713                 cb->property_atom, 0, 0, 0, target,
714                 &type, &format, &length, &bytes_left, &data);
715
716         DEBUG_X11_CLIPRDR("type=%d format=%d bytes=%d request_index=%d",
717                 (int) type, format, (int) bytes_left, cb->request_index);
718
719         if (data)
720         {
721                 XFree(data);
722                 data = NULL;
723         }
724         if (bytes_left <= 0 && !cb->incr_starts)
725         {
726                 DEBUG_X11("no data");
727         }
728         else if (type == cb->incr_atom)
729         {
730                 DEBUG_X11("INCR started");
731                 cb->incr_starts = true;
732                 if (cb->incr_data)
733                 {
734                         xfree(cb->incr_data);
735                         cb->incr_data = NULL;
736                 }
737                 cb->incr_data_length = 0;
738                 /* Data will be followed in PropertyNotify event */
739                 has_data = true;
740         }
741         else
742         {
743                 if (bytes_left <= 0)
744                 {
745                         /* INCR finish */
746                         data = cb->incr_data;
747                         cb->incr_data = NULL;
748                         bytes_left = cb->incr_data_length;
749                         cb->incr_data_length = 0;
750                         cb->incr_starts = 0;
751                         DEBUG_X11("INCR finished");
752                         has_data = true;
753                 }
754                 else if (XGetWindowProperty(xfi->display, xfi->drawable,
755                         cb->property_atom, 0, bytes_left, 0, target,
756                         &type, &format, &length, &dummy, &data) == Success)
757                 {
758                         if (cb->incr_starts)
759                         {
760                                 bytes_left = length * format / 8;
761                                 DEBUG_X11("%d bytes", (int)bytes_left);
762                                 cb->incr_data = (uint8*) xrealloc(cb->incr_data, cb->incr_data_length + bytes_left);
763                                 memcpy(cb->incr_data + cb->incr_data_length, data, bytes_left);
764                                 cb->incr_data_length += bytes_left;
765                                 XFree(data);
766                                 data = NULL;
767                         }
768                         has_data = true;
769                 }
770                 else
771                 {
772                         DEBUG_X11_CLIPRDR("XGetWindowProperty failed");
773                 }
774         }
775         XDeleteProperty(xfi->display, xfi->drawable, cb->property_atom);
776
777         xf_cliprdr_process_requested_data(xfi, has_data, data, (int) bytes_left);
778
779         if (data)
780                 XFree(data);
781
782         return true;
783 }
784
785 static void xf_cliprdr_append_target(clipboardContext* cb, Atom target)
786 {
787         int i;
788
789         if (cb->num_targets >= sizeof(cb->targets) / sizeof(Atom))
790                 return;
791
792         for (i = 0; i < cb->num_targets; i++)
793         {
794                 if (cb->targets[i] == target)
795                         return;
796         }
797
798         cb->targets[cb->num_targets++] = target;
799 }
800
801 static void xf_cliprdr_provide_targets(xfInfo* xfi, XEvent* respond)
802 {
803         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
804
805         if (respond->xselection.property != None)
806         {
807                 XChangeProperty(xfi->display,
808                         respond->xselection.requestor,
809                         respond->xselection.property,
810                         XA_ATOM, 32, PropModeReplace,
811                         (uint8*) cb->targets, cb->num_targets);
812         }
813 }
814
815 static void xf_cliprdr_provide_data(xfInfo* xfi, XEvent* respond)
816 {
817         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
818
819         if (respond->xselection.property != None)
820         {
821                 XChangeProperty(xfi->display,
822                         respond->xselection.requestor,
823                         respond->xselection.property,
824                         respond->xselection.target, 8, PropModeReplace,
825                         (uint8*) cb->data, cb->data_length);
826         }
827 }
828
829 static void xf_cliprdr_process_cb_format_list_event(xfInfo* xfi, RDP_CB_FORMAT_LIST_EVENT* event)
830 {
831         int i, j;
832         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
833
834         if (cb->data)
835         {
836                 xfree(cb->data);
837                 cb->data = NULL;
838         }
839
840         if (cb->formats)
841                 xfree(cb->formats);
842
843         cb->formats = event->formats;
844         cb->num_formats = event->num_formats;
845         event->formats = NULL;
846         event->num_formats = 0;
847
848         cb->num_targets = 2;
849         for (i = 0; i < cb->num_formats; i++)
850         {
851                 for (j = 0; j < cb->num_format_mappings; j++)
852                 {
853                         if (cb->formats[i] == cb->format_mappings[j].format_id)
854                         {
855                                 DEBUG_X11("announce format#%d : %d", i, cb->formats[i]);
856                                 xf_cliprdr_append_target(cb, cb->format_mappings[j].target_format);
857                         }
858                 }
859         }
860
861         XSetSelectionOwner(xfi->display, cb->clipboard_atom, xfi->drawable, CurrentTime);
862         if (event->raw_format_data)
863         {
864                 XChangeProperty(xfi->display, cb->root_window, cb->property_atom,
865                         XA_STRING, 8, PropModeReplace,
866                         event->raw_format_data, event->raw_format_data_size);
867         }
868
869         XFlush(xfi->display);
870 }
871
872 static void xf_cliprdr_process_text(clipboardContext* cb, uint8* data, int size)
873 {
874         cb->data = (uint8*) xmalloc(size);
875         memcpy(cb->data, data, size);
876         cb->data_length = size;
877         crlf2lf(cb->data, &cb->data_length);
878 }
879
880 static void xf_cliprdr_process_unicodetext(clipboardContext* cb, uint8* data, int size)
881 {
882         UNICONV* uniconv;
883
884         uniconv = freerdp_uniconv_new();
885         cb->data = (uint8*) freerdp_uniconv_in(uniconv, data, size);
886         freerdp_uniconv_free(uniconv);
887         cb->data_length = strlen((char*) cb->data);
888         crlf2lf(cb->data, &cb->data_length);
889 }
890
891 static void xf_cliprdr_process_dib(clipboardContext* cb, uint8* data, int size)
892 {
893         STREAM* s;
894         uint16 bpp;
895         uint32 offset;
896         uint32 ncolors;
897
898         /* size should be at least sizeof(BITMAPINFOHEADER) */
899         if (size < 40)
900         {
901                 DEBUG_X11_CLIPRDR("dib size %d too short", size);
902                 return;
903         }
904
905         s = stream_new(0);
906         stream_attach(s, data, size);
907         stream_seek(s, 14);
908         stream_read_uint16(s, bpp);
909         stream_read_uint32(s, ncolors);
910         offset = 14 + 40 + (bpp <= 8 ? (ncolors == 0 ? (1 << bpp) : ncolors) * 4 : 0);
911         stream_detach(s);
912         stream_free(s);
913
914         DEBUG_X11_CLIPRDR("offset=%d bpp=%d ncolors=%d", offset, bpp, ncolors);
915
916         s = stream_new(14 + size);
917         stream_write_uint8(s, 'B');
918         stream_write_uint8(s, 'M');
919         stream_write_uint32(s, 14 + size);
920         stream_write_uint32(s, 0);
921         stream_write_uint32(s, offset);
922         stream_write(s, data, size);
923
924         cb->data = stream_get_head(s);
925         cb->data_length = stream_get_length(s);
926         stream_detach(s);
927         stream_free(s);
928 }
929
930 static void xf_cliprdr_process_html(clipboardContext* cb, uint8* data, int size)
931 {
932         char* start_str;
933         char* end_str;
934         int start;
935         int end;
936
937         start_str = strstr((char*) data, "StartHTML:");
938         end_str = strstr((char*) data, "EndHTML:");
939         if (start_str == NULL || end_str == NULL)
940         {
941                 DEBUG_X11_CLIPRDR("invalid HTML clipboard format");
942                 return;
943         }
944         start = atoi(start_str + 10);
945         end = atoi(end_str + 8);
946         if (start > size || end > size || start >= end)
947         {
948                 DEBUG_X11_CLIPRDR("invalid HTML offset");
949                 return;
950         }
951
952         cb->data = (uint8*) xmalloc(size - start + 1);
953         memcpy(cb->data, data + start, end - start);
954         cb->data_length = end - start;
955         crlf2lf(cb->data, &cb->data_length);
956 }
957
958 static void xf_cliprdr_process_cb_data_response_event(xfInfo* xfi, RDP_CB_DATA_RESPONSE_EVENT* event)
959 {
960         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
961
962         DEBUG_X11_CLIPRDR("size=%d", event->size);
963
964         if (cb->respond == NULL)
965         {
966                 DEBUG_X11_CLIPRDR("unexpected data");
967                 return;
968         }
969
970         if (event->size == 0)
971         {
972                 cb->respond->xselection.property = None;
973         }
974         else
975         {
976                 if (cb->data)
977                 {
978                         xfree(cb->data);
979                         cb->data = NULL;
980                 }
981                 switch (cb->data_format)
982                 {
983                         case CB_FORMAT_RAW:
984                         case CB_FORMAT_PNG:
985                         case CB_FORMAT_JPEG:
986                         case CB_FORMAT_GIF:
987                                 cb->data = event->data;
988                                 cb->data_length = event->size;
989                                 event->data = NULL;
990                                 event->size = 0;
991                                 break;
992
993                         case CB_FORMAT_TEXT:
994                                 xf_cliprdr_process_text(cb, event->data, event->size);
995                                 break;
996
997                         case CB_FORMAT_UNICODETEXT:
998                                 xf_cliprdr_process_unicodetext(cb, event->data, event->size);
999                                 break;
1000
1001                         case CB_FORMAT_DIB:
1002                                 xf_cliprdr_process_dib(cb, event->data, event->size);
1003                                 break;
1004
1005                         case CB_FORMAT_HTML:
1006                                 xf_cliprdr_process_html(cb, event->data, event->size);
1007                                 break;
1008
1009                         default:
1010                                 cb->respond->xselection.property = None;
1011                                 break;
1012                 }
1013                 xf_cliprdr_provide_data(xfi, cb->respond);
1014         }
1015
1016         XSendEvent(xfi->display, cb->respond->xselection.requestor, 0, 0, cb->respond);
1017         XFlush(xfi->display);
1018         xfree(cb->respond);
1019         cb->respond = NULL;
1020 }
1021
1022 void xf_process_cliprdr_event(xfInfo* xfi, RDP_EVENT* event)
1023 {
1024         switch (event->event_type)
1025         {
1026                 case RDP_EVENT_TYPE_CB_MONITOR_READY:
1027                         xf_cliprdr_process_cb_monitor_ready_event(xfi);
1028                         break;
1029
1030                 case RDP_EVENT_TYPE_CB_FORMAT_LIST:
1031                         xf_cliprdr_process_cb_format_list_event(xfi, (RDP_CB_FORMAT_LIST_EVENT*) event);
1032                         break;
1033
1034                 case RDP_EVENT_TYPE_CB_DATA_REQUEST:
1035                         xf_cliprdr_process_cb_data_request_event(xfi, (RDP_CB_DATA_REQUEST_EVENT*) event);
1036                         break;
1037
1038                 case RDP_EVENT_TYPE_CB_DATA_RESPONSE:
1039                         xf_cliprdr_process_cb_data_response_event(xfi, (RDP_CB_DATA_RESPONSE_EVENT*) event);
1040                         break;
1041
1042                 default:
1043                         DEBUG_X11_CLIPRDR("unknown event type %d", event->event_type);
1044                         break;
1045         }
1046 }
1047
1048 boolean xf_cliprdr_process_selection_notify(xfInfo* xfi, XEvent* xevent)
1049 {
1050         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
1051
1052         if (xevent->xselection.target == cb->targets[1])
1053         {
1054                 if (xevent->xselection.property == None)
1055                 {
1056                         DEBUG_X11_CLIPRDR("owner not support TARGETS. sending all format.");
1057                         xf_cliprdr_send_supported_format_list(xfi);
1058                 }
1059                 else
1060                 {
1061                         xf_cliprdr_get_requested_targets(xfi);
1062                 }
1063
1064                 return true;
1065         }
1066         else
1067         {
1068                 return xf_cliprdr_get_requested_data(xfi, xevent->xselection.target);
1069         }
1070 }
1071
1072 boolean xf_cliprdr_process_selection_request(xfInfo* xfi, XEvent* xevent)
1073 {
1074         int i;
1075         int fmt;
1076         Atom type;
1077         uint32 format;
1078         XEvent* respond;
1079         uint32 alt_format;
1080         uint8* data = NULL;
1081         boolean delay_respond;
1082         unsigned long length, bytes_left;
1083         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
1084
1085         DEBUG_X11_CLIPRDR("target=%d", (int) xevent->xselectionrequest.target);
1086
1087         if (xevent->xselectionrequest.owner != xfi->drawable)
1088         {
1089                 DEBUG_X11_CLIPRDR("not owner");
1090                 return false;
1091         }
1092
1093         delay_respond = false;
1094         respond = xnew(XEvent);
1095         respond->xselection.property = None;
1096         respond->xselection.type = SelectionNotify;
1097         respond->xselection.display = xevent->xselectionrequest.display;
1098         respond->xselection.requestor = xevent->xselectionrequest.requestor;
1099         respond->xselection.selection = xevent->xselectionrequest.selection;
1100         respond->xselection.target = xevent->xselectionrequest.target;
1101         respond->xselection.time = xevent->xselectionrequest.time;
1102
1103         if (xevent->xselectionrequest.target == cb->targets[0]) /* TIMESTAMP */
1104         {
1105                 /* TODO */
1106                 DEBUG_X11_CLIPRDR("target: TIMESTAMP (unimplemented)");
1107         }
1108         else if (xevent->xselectionrequest.target == cb->targets[1]) /* TARGETS */
1109         {
1110                 /* Someone else requests our available formats */
1111                 DEBUG_X11_CLIPRDR("target: TARGETS");
1112                 respond->xselection.property = xevent->xselectionrequest.property;
1113                 xf_cliprdr_provide_targets(xfi, respond);
1114         }
1115         else
1116         {
1117                 DEBUG_X11_CLIPRDR("target: other");
1118
1119                 i = xf_cliprdr_select_format_by_atom(cb, xevent->xselectionrequest.target);
1120
1121                 if (i >= 0 && xevent->xselectionrequest.requestor != xfi->drawable)
1122                 {
1123                         format = cb->format_mappings[i].format_id;
1124                         alt_format = format;
1125                         if (format == CB_FORMAT_RAW)
1126                         {
1127                                 if (XGetWindowProperty(xfi->display, xevent->xselectionrequest.requestor,
1128                                         cb->property_atom, 0, 4, 0, XA_INTEGER,
1129                                         &type, &fmt, &length, &bytes_left, &data) != Success)
1130                                 {
1131                                         DEBUG_X11_CLIPRDR("XGetWindowProperty failed");
1132                                 }
1133                                 if (data)
1134                                 {
1135                                         memcpy(&alt_format, data, 4);
1136                                         XFree(data);
1137                                 }
1138                         }
1139                         DEBUG_X11_CLIPRDR("provide format 0x%04x alt_format 0x%04x", format, alt_format);
1140                         if ((cb->data != 0) && (format == cb->data_format) && (alt_format == cb->data_alt_format))
1141                         {
1142                                 /* Cached clipboard data available. Send it now */
1143                                 respond->xselection.property = xevent->xselectionrequest.property;
1144                                 xf_cliprdr_provide_data(xfi, respond);
1145                         }
1146                         else if (cb->respond)
1147                         {
1148                                 DEBUG_X11_CLIPRDR("duplicated request");
1149                         }
1150                         else
1151                         {
1152                                 /**
1153                                  * Send clipboard data request to the server.
1154                                  * Response will be postponed after receiving the data
1155                                  */
1156                                 if (cb->data)
1157                                 {
1158                                         xfree(cb->data);
1159                                         cb->data = NULL;
1160                                 }
1161
1162                                 respond->xselection.property = xevent->xselectionrequest.property;
1163                                 cb->respond = respond;
1164                                 cb->data_format = format;
1165                                 cb->data_alt_format = alt_format;
1166                                 delay_respond = true;
1167
1168                                 xf_cliprdr_send_data_request(xfi, alt_format);
1169                         }
1170                 }
1171         }
1172
1173         if (delay_respond == false)
1174         {
1175                 XSendEvent(xfi->display, xevent->xselectionrequest.requestor, 0, 0, respond);
1176                 XFlush(xfi->display);
1177                 xfree(respond);
1178         }
1179
1180         return true;
1181 }
1182
1183 boolean xf_cliprdr_process_selection_clear(xfInfo* xfi, XEvent* xevent)
1184 {
1185         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
1186
1187         if (xf_cliprdr_is_self_owned(xfi))
1188                 return false;
1189
1190         XDeleteProperty(xfi->display, cb->root_window, cb->property_atom);
1191
1192         return true;
1193 }
1194
1195 boolean xf_cliprdr_process_property_notify(xfInfo* xfi, XEvent* xevent)
1196 {
1197         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
1198
1199         if (xevent->xproperty.atom != cb->property_atom)
1200                 return false; /* Not cliprdr-related */
1201
1202         if (xevent->xproperty.window == cb->root_window)
1203         {
1204                 DEBUG_X11_CLIPRDR("root window PropertyNotify");
1205                 xf_cliprdr_send_format_list(xfi);
1206         }
1207         else if (xevent->xproperty.window == xfi->drawable &&
1208                 xevent->xproperty.state == PropertyNewValue &&
1209                 cb->incr_starts && cb->request_index >= 0)
1210         {
1211                 DEBUG_X11_CLIPRDR("cliprdr window PropertyNotify");
1212                 xf_cliprdr_get_requested_data(xfi,
1213                         cb->format_mappings[cb->request_index].target_format);
1214         }
1215
1216         return true;
1217 }
1218
1219 void xf_cliprdr_check_owner(xfInfo* xfi)
1220 {
1221         Window owner;
1222         clipboardContext* cb = (clipboardContext*) xfi->clipboard_context;
1223
1224         if (cb->sync)
1225         {
1226                 owner = XGetSelectionOwner(xfi->display, cb->clipboard_atom);
1227
1228                 if (cb->owner != owner)
1229                 {
1230                         cb->owner = owner;
1231                         xf_cliprdr_send_format_list(xfi);
1232                 }
1233         }
1234 }
1235