/[rdesktop]/sourceforge.net/trunk/rdesktop/cliprdr.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /sourceforge.net/trunk/rdesktop/cliprdr.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 391 - (show annotations)
Fri Jun 6 09:29:01 2003 UTC (21 years ago) by forsberg
File MIME type: text/plain
File size: 16989 byte(s)
Handle large clipboard transfers X->Windows.

Can't handle INCR yet, but at least we handle larger transfers than
1592 bytes.

1 /* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 Protocol services - Clipboard functions
4 Copyright (C) Erik Forsberg <forsberg@cendio.se> 2003
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <X11/Xlib.h>
22 #include <X11/Xatom.h>
23 #include "rdesktop.h"
24
25 extern BOOL encryption;
26 extern Display *display;
27 extern Window wnd;
28 extern Time last_gesturetime;
29
30 // static Time selection_timestamp;
31 static Atom clipboard_atom, primary_atom, targets_atom, timestamp_atom;
32 static Atom rdesktop_clipboard_target_atom, incr_atom;
33 static cliprdr_dataformat *server_formats = NULL;
34 static uint16 num_server_formats = 0;
35 static XSelectionEvent selection_event;
36 static uint16 clipboard_channelno;
37 static Atom targets[NUM_TARGETS];
38
39 static void
40 cliprdr_print_server_formats(void)
41 {
42 #ifdef WITH_DEBUG_CLIPBOARD
43 cliprdr_dataformat *this;
44 uint16 i = 0;
45 this = server_formats;
46 DEBUG_CLIPBOARD(("There should be %d server formats.\n", num_server_formats));
47 while (NULL != this)
48 {
49 DEBUG_CLIPBOARD(("Format code %d\n", this->identifier));
50 i++;
51 this = this->next;
52 }
53 DEBUG_CLIPBOARD(("There were %d server formats.\n", i));
54 #endif
55 }
56 /*
57 static void
58 cliprdr_set_selection_timestamp(void)
59 {
60 XEvent xev;
61 DEBUG_CLIPBOARD(("Changing a property in order to get a timestamp\n"));
62 fflush(stdout);
63 XChangeProperty(display, wnd, rdesktop_clipboard_target_atom,
64 XA_ATOM, 32, PropModeAppend, 0, 0);
65 DEBUG_CLIPBOARD(("Waiting for PropertyChange on wnd\n"));
66 fflush(stdout);
67 XWindowEvent(display, wnd,
68 PropertyChangeMask, &xev);
69 DEBUG_CLIPBOARD(("Setting selection_timestamp\n"));
70 fflush(stdout);
71 selection_timestamp = xev.xproperty.time;
72 }
73 */
74
75 static void
76 cliprdr_send_format_announce(void)
77 {
78 DEBUG_CLIPBOARD(("Sending format announce\n"));
79
80 STREAM s;
81 int number_of_formats = 1;
82 s = sec_init(encryption ? SEC_ENCRYPT : 0, number_of_formats*36+12+4+4);
83 out_uint32_le(s, number_of_formats*36+12);
84 out_uint32_le(s, 0x13);
85 out_uint16_le(s, 2);
86 out_uint16_le(s, 0);
87 out_uint32_le(s, number_of_formats*36);
88
89 // out_uint32_le(s, 0xd); // FIXME: This is a rather bogus unicode text description..
90 // rdp_out_unistr(s, "", 16);
91 // out_uint8s(s, 32);
92
93
94 out_uint32_le(s, 1); // FIXME: This is a rather bogus text description..
95 out_uint8s(s, 32);
96
97 out_uint32_le(s, 0);
98
99 s_mark_end(s);
100 sec_send_to_channel(s, encryption ? SEC_ENCRYPT : 0,
101 clipboard_channelno);
102 }
103
104
105 static void
106 cliprdr_send_empty_datapacket(void)
107 {
108 STREAM out;
109 out = sec_init(encryption ? SEC_ENCRYPT : 0,
110 20);
111 out_uint32_le(out, 12);
112 out_uint32_le(out, 0x13);
113 out_uint16_le(out, 5);
114 out_uint16_le(out, 1);
115 out_uint32_le(out, 0);
116 /* Insert null string here? */
117 out_uint32_le(out, 0);
118 s_mark_end(out);
119
120 sec_send_to_channel(out, encryption ? SEC_ENCRYPT : 0,
121 clipboard_channelno);
122 }
123
124
125 void
126 cliprdr_handle_SelectionNotify(XSelectionEvent *event)
127 {
128
129 unsigned char *data, *datap;
130 unsigned long nitems, bytes_left;
131
132 unsigned long bytes_left_to_transfer;
133 int res, i;
134
135 int format;
136 Atom type_return;
137 Atom best_target;
138 Atom *supported_targets;
139
140 STREAM out;
141
142 DEBUG_CLIPBOARD(("cliprdr_handle_SelectionNotify\n"));
143
144 if (None == event->property) {
145 cliprdr_send_empty_datapacket();
146 return; /* Selection failed */
147 }
148
149 DEBUG_CLIPBOARD(("selection: %s, target: %s, property: %s\n",
150 XGetAtomName(display, event->selection),
151 XGetAtomName(display, event->target),
152 XGetAtomName(display, event->property)));
153
154 if (targets_atom == event->target) {
155 /* Response to TARGETS request. Let's find the target
156 we want and request that */
157 res = XGetWindowProperty(display, wnd,
158 rdesktop_clipboard_target_atom,
159 0L, 4096L, False, AnyPropertyType,
160 &type_return,
161 &format, &nitems, &bytes_left, &data);
162
163 if (Success != res)
164 {
165 DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
166 cliprdr_send_empty_datapacket();
167 return;
168 }
169
170 if (None == type_return)
171 /* The owner might no support TARGETS. Just try
172 STRING */
173 best_target = XA_STRING;
174 else
175 {
176 /* FIXME: We should choose format here based
177 on what the server wanted */
178 supported_targets = (Atom *)data;
179 for (i=0;i<nitems;i++)
180 {
181 DEBUG_CLIPBOARD(("Target %d: %s\n",
182 i, XGetAtomName(display,
183 supported_targets[i])));
184 }
185 best_target = XInternAtom(display, "TEXT", False);
186
187
188 }
189
190 XConvertSelection(display, primary_atom,
191 best_target,
192 rdesktop_clipboard_target_atom,
193 wnd, event->time);
194
195 }
196 else /* Other clipboard data */
197 {
198
199 res = XGetWindowProperty(display, wnd,
200 rdesktop_clipboard_target_atom,
201 0L, 0x1FFFFFF,
202 True, AnyPropertyType,
203 &type_return,
204 &format, &nitems, &bytes_left, &data);
205
206
207 /* FIXME: We need to handle INCR as well,
208 this is a temporary solution. */
209
210 if (incr_atom == type_return)
211 {
212 warning("We don't support INCR transfers at this time. Try cutting less data\n");
213 cliprdr_send_empty_datapacket();
214 }
215
216
217 if (Success != res)
218 {
219 DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
220 cliprdr_send_empty_datapacket();
221 return;
222 }
223
224 DEBUG_CLIPBOARD(("Received %d bytes of clipboard data from X, there is %d remaining\n",
225 nitems, bytes_left));
226 DEBUG_CLIPBOARD(("type_return is %s\n",
227 XGetAtomName(display, type_return)));
228
229 datap = data;
230
231 if (nitems+1 <= MAX_CLIPRDR_STANDALONE_DATASIZE)
232 {
233 out = sec_init(encryption ? SEC_ENCRYPT : 0,
234 20+nitems+1);
235 out_uint32_le(out, 12+nitems+1);
236 out_uint32_le(out, 0x13);
237 out_uint16_le(out, 5);
238 out_uint16_le(out, 1);
239 out_uint32_le(out, nitems+1);
240 out_uint8p(out, datap, nitems+1);
241 out_uint32_le(out, 0);
242 s_mark_end(out);
243
244 sec_send_to_channel(out, encryption ? SEC_ENCRYPT : 0,
245 clipboard_channelno);
246
247 }
248 else
249 {
250 DEBUG_CLIPBOARD(("Sending %d bytes of data\n",
251 16+MAX_CLIPRDR_STANDALONE_DATASIZE));
252 out = sec_init(encryption ? SEC_ENCRYPT : 0,
253 16+MAX_CLIPRDR_STANDALONE_DATASIZE);
254 out_uint32_le(out, nitems+12);
255 out_uint32_le(out, 0x11);
256 out_uint16_le(out, 5);
257 out_uint16_le(out, 1);
258 out_uint32_le(out, nitems);
259 out_uint8p(out, datap,
260 MAX_CLIPRDR_STANDALONE_DATASIZE);
261 s_mark_end(out);
262
263 sec_send_to_channel(out, encryption ? SEC_ENCRYPT : 0,
264 clipboard_channelno);
265
266 bytes_left_to_transfer = nitems - MAX_CLIPRDR_STANDALONE_DATASIZE;
267 datap+=MAX_CLIPRDR_STANDALONE_DATASIZE;
268
269 while (bytes_left_to_transfer > MAX_CLIPRDR_STANDALONE_DATASIZE)
270 {
271 DEBUG_CLIPBOARD(("Sending %d bytes of data\n",
272 16+MAX_CLIPRDR_CONTINUATION_DATASIZE));
273 out = sec_init(encryption ? SEC_ENCRYPT : 0,
274 8+MAX_CLIPRDR_CONTINUATION_DATASIZE);
275 out_uint32_le(out, nitems);
276 out_uint32_le(out, 0x10);
277 out_uint8p(out, datap,
278 MAX_CLIPRDR_CONTINUATION_DATASIZE);
279 s_mark_end(out);
280
281 sec_send_to_channel(out,
282 encryption ? SEC_ENCRYPT : 0,
283 clipboard_channelno);
284 bytes_left_to_transfer-= MAX_CLIPRDR_CONTINUATION_DATASIZE;
285 datap+=MAX_CLIPRDR_CONTINUATION_DATASIZE;
286
287 }
288 DEBUG_CLIPBOARD(("Sending %d bytes of data\n",
289 12+bytes_left_to_transfer));
290 out = sec_init(encryption ? SEC_ENCRYPT : 0,
291 12+bytes_left_to_transfer);
292 out_uint32_le(out, nitems);
293 out_uint32_le(out, 0x12);
294 out_uint8p(out, datap,
295 bytes_left_to_transfer);
296 out_uint32_le(out, 0x0);
297 s_mark_end(out);
298
299 sec_send_to_channel(out, encryption ? SEC_ENCRYPT : 0,
300 clipboard_channelno);
301
302 }
303
304
305 XFree(data);
306 cliprdr_send_format_announce();
307
308 }
309
310
311 }
312
313 void
314 cliprdr_handle_SelectionClear(void)
315 {
316 DEBUG_CLIPBOARD(("cliprdr_handle_SelectionClear\n"));
317 cliprdr_send_format_announce();
318 }
319
320
321 static void
322 cliprdr_request_clipboard_data(uint32 formatcode)
323 {
324 STREAM s;
325 s = sec_init(encryption ? SEC_ENCRYPT : 0, 24);
326 out_uint32_le(s, 16);
327 out_uint32_le(s, 0x13);
328 out_uint16_le(s, 4);
329 out_uint16_le(s, 0);
330 out_uint32_le(s, 4); // Remaining length
331 out_uint32_le(s, formatcode);
332 out_uint32_le(s, 0); // Unknown. Garbage pad?
333
334 s_mark_end(s);
335
336 sec_send_to_channel(s, encryption ? SEC_ENCRYPT : 0,
337 clipboard_channelno);
338 }
339
340
341 void
342 cliprdr_handle_SelectionRequest(XSelectionRequestEvent *xevent)
343 {
344
345 XSelectionEvent xev;
346 DEBUG_CLIPBOARD(("cliprdr_handle_SelectionRequest\n"));
347 DEBUG_CLIPBOARD(("Requestor window id 0x%x ",
348 (unsigned)xevent->requestor));
349 if (clipboard_atom == xevent->selection) {
350 DEBUG_CLIPBOARD(("wants CLIPBOARD\n"));
351 }
352 if (primary_atom == xevent->selection) {
353 DEBUG_CLIPBOARD(("wants PRIMARY\n"));
354 }
355 DEBUG_CLIPBOARD(("Target is %s (0x%x), property is %s (0x%x)\n",
356 XGetAtomName(display, xevent->target),
357 (unsigned)xevent->target,
358 XGetAtomName(display, xevent->property),
359 (unsigned)xevent->property));
360
361 xev.type = SelectionNotify;
362 xev.serial = 0;
363 xev.send_event = True;
364 xev.requestor = xevent->requestor;
365 xev.selection = xevent->selection;
366 xev.target = xevent->target;
367 xev.property = xevent->property;
368 xev.time = xevent->time;
369
370 if (targets_atom == xevent->target)
371 {
372 DEBUG_CLIPBOARD(("TARGETS requested, sending list..\n"));
373 XChangeProperty(display,
374 xevent->requestor,
375 xevent->property,
376 XA_ATOM,
377 32,
378 PropModeAppend,
379 (unsigned char *)&targets,
380 NUM_TARGETS);
381
382 XSendEvent(display,
383 xevent->requestor,
384 False,
385 NoEventMask,
386 (XEvent *)&xev);
387 return;
388 } else if (timestamp_atom == xevent->target)
389 {
390 XChangeProperty(display,
391 xevent->requestor,
392 xevent->property,
393 XA_INTEGER,
394 32,
395 PropModeAppend,
396 (unsigned char *)&last_gesturetime,
397 1);
398 XSendEvent(display,
399 xevent->requestor,
400 False,
401 NoEventMask,
402 (XEvent *)&xev);
403 } else /* Some other target */
404 {
405 cliprdr_request_clipboard_data(CF_TEXT);
406 memcpy(&selection_event, &xev, sizeof(xev));
407 /* Return and wait for data, handled by
408 cliprdr_handle_server_data */
409 }
410 }
411
412
413 static void
414 cliprdr_ack_format_list(void)
415 {
416 STREAM s;
417 s = sec_init(encryption ? SEC_ENCRYPT : 0, 20);
418 out_uint32_le(s, 12);
419 out_uint32_le(s, 0x13);
420 out_uint16_le(s, 3);
421 out_uint16_le(s, 1);
422 out_uint32_le(s, 0);
423 out_uint32_le(s, 0x0000c0da);
424
425 s_mark_end(s);
426
427 sec_send_to_channel(s, encryption ? SEC_ENCRYPT : 0,
428 clipboard_channelno);
429 }
430
431
432
433
434
435 static void
436 cliprdr_register_server_formats(STREAM s)
437 {
438 uint32 remaining_length, pad;
439 uint16 num_formats;
440 cliprdr_dataformat *this, *next;
441
442 DEBUG_CLIPBOARD(("cliprdr_register_server_formats\n"));
443 in_uint32_le(s, remaining_length);
444
445 num_formats = remaining_length / 36;
446 if (NULL != server_formats) {
447 this = server_formats;
448 next = this->next;
449 while (NULL != next) {
450 xfree(this);
451 this = NULL;
452 this = next;
453 next = this->next;
454 }
455 }
456 this = xmalloc(sizeof(cliprdr_dataformat));
457 this->next = NULL;
458 server_formats = this;
459 num_server_formats = num_formats;
460 while (1 < num_formats) {
461 in_uint32_le(s, this->identifier);
462 in_uint8a(s, this->textual_description, 32);
463 DEBUG_CLIPBOARD(("Stored format description with numeric id %d\n",
464 this->identifier));
465 this-> next = xmalloc(sizeof(cliprdr_dataformat));
466 this = this->next;
467 num_formats--;
468 }
469 in_uint32_le(s, this->identifier);
470 DEBUG_CLIPBOARD(("Stored format description with numeric id %d\n", this->identifier));
471 in_uint8a(s, this->textual_description, 32);
472 this -> next = NULL;
473 in_uint32_le(s, pad);
474 cliprdr_print_server_formats();
475 }
476
477 static void
478 cliprdr_select_X_clipboards(void)
479 {
480 XSetSelectionOwner(display, primary_atom, wnd, last_gesturetime);
481 if (wnd != XGetSelectionOwner(display, primary_atom))
482 {
483 warning("Failed to aquire ownership of PRIMARY clipboard\n");
484 }
485 XSetSelectionOwner(display, clipboard_atom, wnd, last_gesturetime);
486 if (wnd != XGetSelectionOwner(display, clipboard_atom))
487 {
488 warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
489 }
490
491 }
492
493
494
495
496
497 static void
498 cliprdr_handle_first_handshake(STREAM s)
499 {
500 uint32 remaining_length, pad;
501 in_uint32_le(s, remaining_length);
502 in_uint32_le(s, pad);
503 DEBUG_CLIPBOARD(("Remaining length in first handshake frm server is %d, pad is %d\n",
504 remaining_length, pad));
505 cliprdr_send_format_announce();
506 }
507
508 void cliprdr_handle_server_data(uint32 length, STREAM s)
509 {
510 uint32 remaining_length;
511 char *data;
512 in_uint32_le(s, remaining_length);
513 data = s->p;
514 XChangeProperty(display,
515 selection_event.requestor,
516 selection_event.property,
517 XInternAtom(display, "STRING", False),
518 8,
519 PropModeAppend,
520 data,
521 remaining_length);
522
523 XSendEvent(display,
524 selection_event.requestor,
525 False,
526 NoEventMask,
527 (XEvent *)&selection_event);
528
529
530 }
531
532 void cliprdr_handle_server_data_request(STREAM s)
533 {
534 Window selectionowner;
535 uint32 remaining_length;
536 uint32 wanted_formatcode, pad;
537
538 in_uint32_le(s, remaining_length);
539 in_uint32_le(s, wanted_formatcode);
540 in_uint32_le(s, pad);
541
542 /* FIXME: Check that we support this formatcode */
543
544 DEBUG_CLIPBOARD(("Request from server for format %d\n",
545 wanted_formatcode));
546
547 selectionowner = XGetSelectionOwner(display, primary_atom);
548
549 if (None != selectionowner)
550 {
551
552 /* FIXME: Perhaps we should check if we are the owner? */
553
554 XConvertSelection(display, primary_atom,
555 targets_atom,
556 rdesktop_clipboard_target_atom,
557 wnd, CurrentTime);
558
559 /* The rest of the transfer is handled in
560 cliprdr_handle_SelectionNotify */
561
562 } else
563 {
564 DEBUG_CLIPBOARD(("There were no owner for PRIMARY, sending empty string\n")); // FIXME: Should we always send an empty string?
565
566 cliprdr_send_empty_datapacket();
567 }
568
569
570 }
571
572
573 void cliprdr_callback(STREAM s, uint16 channelno)
574 {
575 static int failed_clipboard_acks = 0;
576 struct timeval timeval;
577 uint32 length, flags;
578 uint16 ptype0, ptype1;
579 clipboard_channelno = channelno;
580 DEBUG_CLIPBOARD(("cliprdr_callback called with channelno %d, clipboard data:\n", channelno));
581 #ifdef WITH_DEBUG_CLIPBOARD
582 hexdump(s->p, s->end - s->p);
583 #endif
584 in_uint32_le(s, length);
585 in_uint32_le(s, flags);
586
587 DEBUG_CLIPBOARD(("length is %d, flags are %d\n", length, flags));
588
589 if (flags & 0x03 || flags & 0x01) /* Single-write op or first-packet-of-several op */
590 {
591 in_uint16_le(s, ptype0);
592 in_uint16_le(s, ptype1);
593 DEBUG_CLIPBOARD(("ptype0 is %d, ptype1 is %d\n", ptype0, ptype1));
594 if (1 == ptype0 && 0 == ptype1) {
595 cliprdr_handle_first_handshake(s);
596 return;
597 } else if (3 == ptype0 && 1 == ptype1)
598 {
599 // Acknowledgment on our format announce. Do we care? Not right now.
600 // There is a strange pad in this packet that we might need some time,
601 // but probably not.
602 DEBUG_CLIPBOARD(("Received format announce ACK\n"));
603 failed_clipboard_acks = 0;
604 return;
605
606 } else if (3 == ptype0 && 2 == ptype1)
607 {
608 DEBUG_CLIPBOARD(("Received failed clipboard format announce ACK, retrying\n"));
609
610 /* This is a fairly portable way to sleep 1/10 of
611 a second.. */
612 timeval.tv_sec = 0;
613 timeval.tv_usec = 100;
614 select(0, NULL, NULL, NULL, &timeval);
615 if (failed_clipboard_acks < 3)
616 {
617
618 cliprdr_send_format_announce();
619 /* Make sure we don't get stuck in this loop */
620 failed_clipboard_acks++;
621 }
622 else
623 {
624 warning("Reached maximum number of clipboard format announce attempts. Pasting in Windows probably won't work well now.\n");
625 }
626 } else if (2 == ptype0 && 0 == ptype1)
627 {
628 cliprdr_register_server_formats(s);
629 cliprdr_select_X_clipboards();
630 cliprdr_ack_format_list();
631 return;
632 } else if (5 == ptype0 && 1 == ptype1)
633 {
634 cliprdr_handle_server_data(length, s);
635 } else if (4 == ptype0 && 0 == ptype1)
636 {
637 cliprdr_handle_server_data_request(s);
638 }
639
640
641 }
642 }
643
644
645 void cliprdr_init(void)
646 {
647 primary_atom = XInternAtom(display, "PRIMARY", False);
648 clipboard_atom = XInternAtom(display, "CLIPBOARD", False);
649 targets_atom = XInternAtom(display, "TARGETS", False);
650 timestamp_atom = XInternAtom(display, "TIMESTAMP", False);
651 rdesktop_clipboard_target_atom = XInternAtom(display, "_RDESKTOP_CLIPBOARD_TARGET", False);
652 incr_atom = XInternAtom(display, "INCR", False);
653 targets[0] = targets_atom;
654 targets[1] = XInternAtom(display, "TEXT", False);
655 targets[2] = XInternAtom(display, "UTF8_STRING", False);
656 targets[3] = XInternAtom(display, "text/unicode", False);
657 targets[4] = XInternAtom(display, "TIMESTAMP", False);
658 targets[5] = XInternAtom(display, "STRING", False);
659
660 }

  ViewVC Help
Powered by ViewVC 1.1.26