/[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 390 - (show annotations)
Fri Jun 6 09:28:21 2003 UTC (21 years ago) by forsberg
File MIME type: text/plain
File size: 14559 byte(s)
Don't hardcode clipboard channel.

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;
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
38 static void
39 cliprdr_print_server_formats(void)
40 {
41 #ifdef WITH_DEBUG_CLIPBOARD
42 cliprdr_dataformat *this;
43 uint16 i = 0;
44 this = server_formats;
45 DEBUG_CLIPBOARD(("There should be %d server formats.\n", num_server_formats));
46 while (NULL != this)
47 {
48 DEBUG_CLIPBOARD(("Format code %d\n", this->identifier));
49 i++;
50 this = this->next;
51 }
52 DEBUG_CLIPBOARD(("There were %d server formats.\n", i));
53 #endif
54 }
55 /*
56 static void
57 cliprdr_set_selection_timestamp(void)
58 {
59 XEvent xev;
60 DEBUG_CLIPBOARD(("Changing a property in order to get a timestamp\n"));
61 fflush(stdout);
62 XChangeProperty(display, wnd, rdesktop_clipboard_target_atom,
63 XA_ATOM, 32, PropModeAppend, 0, 0);
64 DEBUG_CLIPBOARD(("Waiting for PropertyChange on wnd\n"));
65 fflush(stdout);
66 XWindowEvent(display, wnd,
67 PropertyChangeMask, &xev);
68 DEBUG_CLIPBOARD(("Setting selection_timestamp\n"));
69 fflush(stdout);
70 selection_timestamp = xev.xproperty.time;
71 }
72 */
73
74 static void
75 cliprdr_send_format_announce(void)
76 {
77 DEBUG_CLIPBOARD(("Sending format announce\n"));
78
79 STREAM s;
80 int number_of_formats = 1;
81 s = sec_init(encryption ? SEC_ENCRYPT : 0, number_of_formats*36+12+4+4);
82 out_uint32_le(s, number_of_formats*36+12);
83 out_uint32_le(s, 0x13);
84 out_uint16_le(s, 2);
85 out_uint16_le(s, 0);
86 out_uint32_le(s, number_of_formats*36);
87
88 // out_uint32_le(s, 0xd); // FIXME: This is a rather bogus unicode text description..
89 // rdp_out_unistr(s, "", 16);
90 // out_uint8s(s, 32);
91
92
93 out_uint32_le(s, 1); // FIXME: This is a rather bogus text description..
94 out_uint8s(s, 32);
95
96 out_uint32_le(s, 0);
97
98 s_mark_end(s);
99 sec_send_to_channel(s, encryption ? SEC_ENCRYPT : 0,
100 clipboard_channelno);
101 }
102
103
104 static void
105 cliprdr_send_empty_datapacket(void)
106 {
107 STREAM out;
108 out = sec_init(encryption ? SEC_ENCRYPT : 0,
109 20);
110 out_uint32_le(out, 12);
111 out_uint32_le(out, 0x13);
112 out_uint16_le(out, 5);
113 out_uint16_le(out, 1);
114 out_uint32_le(out, 0);
115 /* Insert null string here? */
116 out_uint32_le(out, 0);
117 s_mark_end(out);
118
119 sec_send_to_channel(out, encryption ? SEC_ENCRYPT : 0,
120 clipboard_channelno);
121 }
122
123
124 void
125 cliprdr_handle_SelectionNotify(XSelectionEvent *event)
126 {
127
128 unsigned char *data;
129 unsigned long nitems, bytes_left;
130 int res;
131
132 int format;
133 Atom type_return;
134 Atom best_target;
135
136 STREAM out;
137
138 DEBUG_CLIPBOARD(("cliprdr_handle_SelectionNotify\n"));
139
140 if (None == event->property) {
141 cliprdr_send_empty_datapacket();
142 return; /* Selection failed */
143 }
144
145 DEBUG_CLIPBOARD(("selection: %s, target: %s, property: %s\n",
146 XGetAtomName(display, event->selection),
147 XGetAtomName(display, event->target),
148 XGetAtomName(display, event->property)));
149
150 if (targets_atom == event->target) {
151 /* Response to TARGETS request. Let's find the target
152 we want and request that */
153 res = XGetWindowProperty(display, wnd,
154 rdesktop_clipboard_target_atom,
155 0L, 4096L, False, AnyPropertyType,
156 &type_return,
157 &format, &nitems, &bytes_left, &data);
158
159 if (Success != res)
160 {
161 DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
162 cliprdr_send_empty_datapacket();
163 return;
164 }
165
166 if (None == type_return)
167 /* The owner might no support TARGETS. Just try
168 STRING */
169 best_target = XA_STRING;
170 else
171 {
172 /* FIXME: We should choose format here based
173 on what the server wanted */
174 best_target = XInternAtom(display, "TEXT", False);
175
176
177 }
178
179 XConvertSelection(display, primary_atom,
180 best_target,
181 rdesktop_clipboard_target_atom,
182 wnd, event->time);
183
184 }
185 else /* Other clipboard data */
186 {
187
188 res = XGetWindowProperty(display, wnd,
189 rdesktop_clipboard_target_atom,
190 0L, 4096L, False, AnyPropertyType,
191 &type_return,
192 &format, &nitems, &bytes_left, &data);
193
194 if (Success != res)
195 {
196 DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
197 cliprdr_send_empty_datapacket();
198 return;
199 }
200
201 /* We need to handle INCR as well */
202
203 out = sec_init(encryption ? SEC_ENCRYPT : 0,
204 20+nitems+1);
205 out_uint32_le(out, 12+nitems+1);
206 out_uint32_le(out, 0x13);
207 out_uint16_le(out, 5);
208 out_uint16_le(out, 1);
209 out_uint32_le(out, nitems+1);
210 out_uint8p(out, data, nitems+1);
211 /* Insert null string here? */
212 out_uint32_le(out, 0);
213 s_mark_end(out);
214
215 sec_send_to_channel(out, encryption ? SEC_ENCRYPT : 0,
216 clipboard_channelno);
217
218 cliprdr_send_format_announce();
219
220 }
221
222
223 }
224
225 void
226 cliprdr_handle_SelectionClear(void)
227 {
228 DEBUG_CLIPBOARD(("cliprdr_handle_SelectionClear\n"));
229 cliprdr_send_format_announce();
230 }
231
232
233 static void
234 cliprdr_request_clipboard_data(uint32 formatcode)
235 {
236 STREAM s;
237 s = sec_init(encryption ? SEC_ENCRYPT : 0, 24);
238 out_uint32_le(s, 16);
239 out_uint32_le(s, 0x13);
240 out_uint16_le(s, 4);
241 out_uint16_le(s, 0);
242 out_uint32_le(s, 4); // Remaining length
243 out_uint32_le(s, formatcode);
244 out_uint32_le(s, 0); // Unknown. Garbage pad?
245
246 s_mark_end(s);
247
248 sec_send_to_channel(s, encryption ? SEC_ENCRYPT : 0,
249 clipboard_channelno);
250 }
251
252
253 void
254 cliprdr_handle_SelectionRequest(XSelectionRequestEvent *xevent)
255 {
256
257 Atom *targets;
258 int res;
259
260 XSelectionEvent xev;
261 DEBUG_CLIPBOARD(("cliprdr_handle_SelectionRequest\n"));
262 DEBUG_CLIPBOARD(("Requestor window id 0x%x ",
263 (unsigned)xevent->requestor));
264 if (clipboard_atom == xevent->selection) {
265 DEBUG_CLIPBOARD(("wants CLIPBOARD\n"));
266 }
267 if (primary_atom == xevent->selection) {
268 DEBUG_CLIPBOARD(("wants PRIMARY\n"));
269 }
270 DEBUG_CLIPBOARD(("Target is %s (0x%x), property is %s (0x%x)\n",
271 XGetAtomName(display, xevent->target),
272 (unsigned)xevent->target,
273 XGetAtomName(display, xevent->property),
274 (unsigned)xevent->property));
275
276 xev.type = SelectionNotify;
277 xev.serial = 0;
278 xev.send_event = True;
279 xev.requestor = xevent->requestor;
280 xev.selection = xevent->selection;
281 xev.target = xevent->target;
282 xev.property = xevent->property;
283 xev.time = xevent->time;
284
285 if (targets_atom == xevent->target)
286 {
287 DEBUG_CLIPBOARD(("TARGETS requested, sending list..\n"));
288 targets = xmalloc(4*sizeof(Atom));
289 targets[0] = xevent->target;
290 targets[1] = XInternAtom(display, "TEXT", True);
291 targets[2] = XInternAtom(display, "UTF8_STRING", True);
292 targets[3] = XInternAtom(display, "TIMESTAMP", True);
293 res = XChangeProperty(display,
294 xevent->requestor,
295 xevent->property,
296 XA_ATOM,
297 32,
298 PropModeAppend,
299 (unsigned char *)targets,
300 3);
301
302 res = XSendEvent(display,
303 xevent->requestor,
304 False,
305 NoEventMask,
306 (XEvent *)&xev);
307 return;
308 } else if (timestamp_atom == xevent->target)
309 {
310 DEBUG_CLIPBOARD(("TIMESTAMP requested... sending 0x%x\n",
311 (unsigned)last_gesturetime));
312 res = XChangeProperty(display,
313 xevent->requestor,
314 xevent->property,
315 XA_INTEGER,
316 32,
317 PropModeAppend,
318 (unsigned char *)&last_gesturetime,
319 1);
320 res = XSendEvent(display,
321 xevent->requestor,
322 False,
323 NoEventMask,
324 (XEvent *)&xev);
325 } else /* Some other target */
326 {
327 cliprdr_request_clipboard_data(CF_TEXT);
328 memcpy(&selection_event, &xev, sizeof(xev));
329 /* Return and wait for data, handled by
330 cliprdr_handle_server_data */
331 }
332 }
333
334
335 static void
336 cliprdr_ack_format_list(void)
337 {
338 STREAM s;
339 s = sec_init(encryption ? SEC_ENCRYPT : 0, 20);
340 out_uint32_le(s, 12);
341 out_uint32_le(s, 0x13);
342 out_uint16_le(s, 3);
343 out_uint16_le(s, 1);
344 out_uint32_le(s, 0);
345 out_uint32_le(s, 0x0000c0da);
346
347 s_mark_end(s);
348
349 sec_send_to_channel(s, encryption ? SEC_ENCRYPT : 0,
350 clipboard_channelno);
351 }
352
353
354
355
356
357 static void
358 cliprdr_register_server_formats(STREAM s)
359 {
360 uint32 remaining_length, pad;
361 uint16 num_formats;
362 cliprdr_dataformat *this, *next;
363
364 DEBUG_CLIPBOARD(("cliprdr_register_server_formats\n"));
365 in_uint32_le(s, remaining_length);
366
367 num_formats = remaining_length / 36;
368 if (NULL != server_formats) {
369 this = server_formats;
370 next = this->next;
371 while (NULL != next) {
372 xfree(this);
373 this = NULL;
374 this = next;
375 next = this->next;
376 }
377 }
378 this = xmalloc(sizeof(cliprdr_dataformat));
379 this->next = NULL;
380 server_formats = this;
381 num_server_formats = num_formats;
382 while (1 < num_formats) {
383 in_uint32_le(s, this->identifier);
384 in_uint8a(s, this->textual_description, 32);
385 DEBUG_CLIPBOARD(("Stored format description with numeric id %d\n",
386 this->identifier));
387 this-> next = xmalloc(sizeof(cliprdr_dataformat));
388 this = this->next;
389 num_formats--;
390 }
391 in_uint32_le(s, this->identifier);
392 DEBUG_CLIPBOARD(("Stored format description with numeric id %d\n", this->identifier));
393 in_uint8a(s, this->textual_description, 32);
394 this -> next = NULL;
395 in_uint32_le(s, pad);
396 cliprdr_print_server_formats();
397 }
398
399 static void
400 cliprdr_select_X_clipboards(void)
401 {
402 XSetSelectionOwner(display, primary_atom, wnd, last_gesturetime);
403 if (wnd != XGetSelectionOwner(display, primary_atom))
404 {
405 warning("Failed to aquire ownership of PRIMARY clipboard\n");
406 }
407 XSetSelectionOwner(display, clipboard_atom, wnd, last_gesturetime);
408 if (wnd != XGetSelectionOwner(display, clipboard_atom))
409 {
410 warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
411 }
412
413 }
414
415
416
417
418
419 static void
420 cliprdr_handle_first_handshake(STREAM s)
421 {
422 uint32 remaining_length, pad;
423 in_uint32_le(s, remaining_length);
424 in_uint32_le(s, pad);
425 DEBUG_CLIPBOARD(("Remaining length in first handshake frm server is %d, pad is %d\n",
426 remaining_length, pad));
427 cliprdr_send_format_announce();
428 }
429
430 void cliprdr_handle_server_data(uint32 length, STREAM s)
431 {
432 uint32 remaining_length;
433 char *data;
434 in_uint32_le(s, remaining_length);
435 data = s->p;
436 XChangeProperty(display,
437 selection_event.requestor,
438 selection_event.property,
439 XInternAtom(display, "STRING", False),
440 8,
441 PropModeAppend,
442 data,
443 remaining_length);
444
445 XSendEvent(display,
446 selection_event.requestor,
447 False,
448 NoEventMask,
449 (XEvent *)&selection_event);
450
451
452 }
453
454 void cliprdr_handle_server_data_request(STREAM s)
455 {
456 Window selectionowner;
457 uint32 remaining_length;
458 uint32 wanted_formatcode, pad;
459
460 in_uint32_le(s, remaining_length);
461 in_uint32_le(s, wanted_formatcode);
462 in_uint32_le(s, pad);
463
464 /* FIXME: Check that we support this formatcode */
465
466 DEBUG_CLIPBOARD(("Request from server for format %d\n",
467 wanted_formatcode));
468
469 selectionowner = XGetSelectionOwner(display, primary_atom);
470
471 if (None != selectionowner)
472 {
473
474 /* FIXME: Perhaps we should check if we are the owner? */
475
476 XConvertSelection(display, primary_atom,
477 targets_atom,
478 rdesktop_clipboard_target_atom,
479 wnd, CurrentTime);
480
481 /* The rest of the transfer is handled in
482 cliprdr_handle_SelectionNotify */
483
484 } else
485 {
486 DEBUG_CLIPBOARD(("There were no owner for PRIMARY, sending empty string\n")); // FIXME: Should we always send an empty string?
487
488 cliprdr_send_empty_datapacket();
489 }
490
491
492 }
493
494
495 void cliprdr_callback(STREAM s, uint16 channelno)
496 {
497 static int failed_clipboard_acks = 0;
498 struct timeval timeval;
499 uint32 length, flags;
500 uint16 ptype0, ptype1;
501 clipboard_channelno = channelno;
502 DEBUG_CLIPBOARD(("cliprdr_callback called with channelno %d, clipboard data:\n", channelno));
503 #ifdef WITH_DEBUG_CLIPBOARD
504 hexdump(s->p, s->end - s->p);
505 #endif
506 in_uint32_le(s, length);
507 in_uint32_le(s, flags);
508
509 DEBUG_CLIPBOARD(("length is %d, flags are %d\n", length, flags));
510
511 if (flags & 0x03 || flags & 0x01) /* Single-write op or first-packet-of-several op */
512 {
513 in_uint16_le(s, ptype0);
514 in_uint16_le(s, ptype1);
515 DEBUG_CLIPBOARD(("ptype0 is %d, ptype1 is %d\n", ptype0, ptype1));
516 if (1 == ptype0 && 0 == ptype1) {
517 cliprdr_handle_first_handshake(s);
518 return;
519 } else if (3 == ptype0 && 1 == ptype1)
520 {
521 // Acknowledgment on our format announce. Do we care? Not right now.
522 // There is a strange pad in this packet that we might need some time,
523 // but probably not.
524 DEBUG_CLIPBOARD(("Received format announce ACK\n"));
525 failed_clipboard_acks = 0;
526 return;
527
528 } else if (3 == ptype0 && 2 == ptype1)
529 {
530 DEBUG_CLIPBOARD(("Received failed clipboard format announce ACK, retrying\n"));
531
532 /* This is a fairly portable way to sleep 1/10 of
533 a second.. */
534 timeval.tv_sec = 0;
535 timeval.tv_usec = 100;
536 select(0, NULL, NULL, NULL, &timeval);
537 if (failed_clipboard_acks < 3)
538 {
539
540 cliprdr_send_format_announce();
541 /* Make sure we don't get stuck in this loop */
542 failed_clipboard_acks++;
543 }
544 else
545 {
546 warning("Reached maximum number of clipboard format announce attempts. Pasting in Windows probably won't work well now.\n");
547 }
548 } else if (2 == ptype0 && 0 == ptype1)
549 {
550 cliprdr_register_server_formats(s);
551 cliprdr_select_X_clipboards();
552 cliprdr_ack_format_list();
553 return;
554 } else if (5 == ptype0 && 1 == ptype1)
555 {
556 cliprdr_handle_server_data(length, s);
557 } else if (4 == ptype0 && 0 == ptype1)
558 {
559 cliprdr_handle_server_data_request(s);
560 }
561
562
563 }
564 }
565
566
567 void cliprdr_init(void)
568 {
569 primary_atom = XInternAtom(display, "PRIMARY", False);
570 clipboard_atom = XInternAtom(display, "CLIPBOARD", False);
571 targets_atom = XInternAtom(display, "TARGETS", False);
572 timestamp_atom = XInternAtom(display, "TIMESTAMP", False);
573 rdesktop_clipboard_target_atom = XInternAtom(display, "_RDESKTOP_CLIPBOARD_TARGET", False);
574 }

  ViewVC Help
Powered by ViewVC 1.1.26