/[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 386 - (show annotations)
Fri Jun 6 09:24:15 2003 UTC (21 years ago) by forsberg
File MIME type: text/plain
File size: 13929 byte(s)
Rudimentary client->server clipboard transfer.

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

  ViewVC Help
Powered by ViewVC 1.1.26