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    Copyright (C) Matthew Chapman 2003
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License along
18    with this program; if not, write to the Free Software Foundation, Inc.,
19    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21 
22 #include "rdesktop.h"
23 
24 #define CLIPRDR_CONNECT			1
25 #define CLIPRDR_FORMAT_ANNOUNCE		2
26 #define CLIPRDR_FORMAT_ACK		3
27 #define CLIPRDR_DATA_REQUEST		4
28 #define CLIPRDR_DATA_RESPONSE		5
29 
30 #define CLIPRDR_REQUEST			0
31 #define CLIPRDR_RESPONSE		1
32 #define CLIPRDR_ERROR			2
33 
34 static void
cliprdr_send_packet(RDPCLIENT * This,uint16 type,uint16 status,uint8 * data,uint32 length)35 cliprdr_send_packet(RDPCLIENT * This, uint16 type, uint16 status, uint8 * data, uint32 length)
36 {
37 	STREAM s;
38 
39 	DEBUG_CLIPBOARD(("CLIPRDR send: type=%d, status=%d, length=%d\n", type, status, length));
40 
41 	s = channel_init(This, This->cliprdr.channel, length + 12);
42 	out_uint16_le(s, type);
43 	out_uint16_le(s, status);
44 	out_uint32_le(s, length);
45 	out_uint8p(s, data, length);
46 	out_uint32(s, 0);	/* pad? */
47 	s_mark_end(s);
48 	channel_send(This, s, This->cliprdr.channel);
49 }
50 
51 /* Helper which announces our readiness to supply clipboard data
52    in a single format (such as CF_TEXT) to the RDP side.
53    To announce more than one format at a time, use
54    cliprdr_send_native_format_announce.
55  */
56 void
cliprdr_send_simple_native_format_announce(RDPCLIENT * This,uint32 format)57 cliprdr_send_simple_native_format_announce(RDPCLIENT * This, uint32 format)
58 {
59 	uint8 buffer[36];
60 
61 	DEBUG_CLIPBOARD(("cliprdr_send_simple_native_format_announce\n"));
62 
63 	buf_out_uint32(buffer, format);
64 	memset(buffer + 4, 0, sizeof(buffer) - 4);	/* description */
65 	cliprdr_send_native_format_announce(This, buffer, sizeof(buffer));
66 }
67 
68 /* Announces our readiness to supply clipboard data in multiple
69    formats, each denoted by a 36-byte format descriptor of
70    [ uint32 format + 32-byte description ].
71  */
72 void
cliprdr_send_native_format_announce(RDPCLIENT * This,uint8 * formats_data,uint32 formats_data_length)73 cliprdr_send_native_format_announce(RDPCLIENT * This, uint8 * formats_data, uint32 formats_data_length)
74 {
75 	DEBUG_CLIPBOARD(("cliprdr_send_native_format_announce\n"));
76 
77 	cliprdr_send_packet(This, CLIPRDR_FORMAT_ANNOUNCE, CLIPRDR_REQUEST, formats_data,
78 			    formats_data_length);
79 
80 	if (formats_data != This->cliprdr.last_formats)
81 	{
82 		if (This->cliprdr.last_formats)
83 			xfree(This->cliprdr.last_formats);
84 
85 		This->cliprdr.last_formats = xmalloc(formats_data_length);
86 		memcpy(This->cliprdr.last_formats, formats_data, formats_data_length);
87 		This->cliprdr.last_formats_length = formats_data_length;
88 	}
89 }
90 
91 void
cliprdr_send_data_request(RDPCLIENT * This,uint32 format)92 cliprdr_send_data_request(RDPCLIENT * This, uint32 format)
93 {
94 	uint8 buffer[4];
95 
96 	DEBUG_CLIPBOARD(("cliprdr_send_data_request\n"));
97 	buf_out_uint32(buffer, format);
98 	cliprdr_send_packet(This, CLIPRDR_DATA_REQUEST, CLIPRDR_REQUEST, buffer, sizeof(buffer));
99 }
100 
101 void
cliprdr_send_data(RDPCLIENT * This,uint8 * data,uint32 length)102 cliprdr_send_data(RDPCLIENT * This, uint8 * data, uint32 length)
103 {
104 	DEBUG_CLIPBOARD(("cliprdr_send_data\n"));
105 	cliprdr_send_packet(This, CLIPRDR_DATA_RESPONSE, CLIPRDR_RESPONSE, data, length);
106 }
107 
108 static void
cliprdr_process(RDPCLIENT * This,STREAM s)109 cliprdr_process(RDPCLIENT * This, STREAM s)
110 {
111 	uint16 type, status;
112 	uint32 length, format;
113 	uint8 *data;
114 
115 	in_uint16_le(s, type);
116 	in_uint16_le(s, status);
117 	in_uint32_le(s, length);
118 	data = s->p;
119 
120 	DEBUG_CLIPBOARD(("CLIPRDR recv: type=%d, status=%d, length=%d\n", type, status, length));
121 
122 	if (status == CLIPRDR_ERROR)
123 	{
124 		switch (type)
125 		{
126 			case CLIPRDR_FORMAT_ACK:
127 				/* FIXME: We seem to get this when we send an announce while the server is
128 				   still processing a paste. Try sending another announce. */
129 				cliprdr_send_native_format_announce(This, This->cliprdr.last_formats,
130 								    This->cliprdr.last_formats_length);
131 				break;
132 			case CLIPRDR_DATA_RESPONSE:
133 				ui_clip_request_failed(This);
134 				break;
135 			default:
136 				DEBUG_CLIPBOARD(("CLIPRDR error (type=%d)\n", type));
137 		}
138 
139 		return;
140 	}
141 
142 	switch (type)
143 	{
144 		case CLIPRDR_CONNECT:
145 			ui_clip_sync(This);
146 			break;
147 		case CLIPRDR_FORMAT_ANNOUNCE:
148 			ui_clip_format_announce(This, data, length);
149 			cliprdr_send_packet(This, CLIPRDR_FORMAT_ACK, CLIPRDR_RESPONSE, NULL, 0);
150 			return;
151 		case CLIPRDR_FORMAT_ACK:
152 			break;
153 		case CLIPRDR_DATA_REQUEST:
154 			in_uint32_le(s, format);
155 			ui_clip_request_data(This, format);
156 			break;
157 		case CLIPRDR_DATA_RESPONSE:
158 			ui_clip_handle_data(This, data, length);
159 			break;
160 		case 7:	/* TODO: W2K3 SP1 sends this on connect with a value of 1 */
161 			break;
162 		default:
163 			unimpl("CLIPRDR packet type %d\n", type);
164 	}
165 }
166 
167 void
cliprdr_set_mode(RDPCLIENT * This,const char * optarg)168 cliprdr_set_mode(RDPCLIENT * This, const char *optarg)
169 {
170 	ui_clip_set_mode(This, optarg);
171 }
172 
173 BOOL
cliprdr_init(RDPCLIENT * This)174 cliprdr_init(RDPCLIENT * This)
175 {
176 	This->cliprdr.channel =
177 		channel_register(This, "cliprdr",
178 				 CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
179 				 CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL,
180 				 cliprdr_process);
181 	return (This->cliprdr.channel != NULL);
182 }
183