/[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 389 - (show annotations)
Fri Jun 6 09:26:49 2003 UTC (20 years, 11 months ago) by forsberg
File MIME type: text/plain
File size: 14498 byte(s)
Added one to the length of sent data, since XGetProperty is not reporting the
null byte (although space is allocated for it as it seems).

Resend format announces if they fail, with a small delay. Ugly hack, but
it works..

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

  ViewVC Help
Powered by ViewVC 1.1.26