blob: ab42e59a66306042f581c96b4584a2f753957152 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman0ff26552016-02-05 10:26:56 +01002 * Copyright 2009-2019 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19#include <stdio.h>
Pierre Ossman0ff26552016-02-05 10:26:56 +010020
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000021#include <rdr/InStream.h>
Pierre Ossman0ff26552016-02-05 10:26:56 +010022#include <rdr/ZlibInStream.h>
23
Pierre Ossman7638e9c2014-01-16 13:12:40 +010024#include <rfb/msgTypes.h>
Pierre Ossman5ae28212017-05-16 14:30:38 +020025#include <rfb/qemuTypes.h>
Pierre Ossman0ff26552016-02-05 10:26:56 +010026#include <rfb/clipboardTypes.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000027#include <rfb/Exception.h>
28#include <rfb/util.h>
29#include <rfb/SMsgHandler.h>
30#include <rfb/SMsgReader.h>
31#include <rfb/Configuration.h>
Constantin Kaplinskyc7755da2008-04-24 11:01:55 +000032#include <rfb/LogWriter.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000033
34using namespace rfb;
35
Constantin Kaplinskyc7755da2008-04-24 11:01:55 +000036static LogWriter vlog("SMsgReader");
37
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000038static IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024);
39
40SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_)
41 : handler(handler_), is(is_)
42{
43}
44
45SMsgReader::~SMsgReader()
46{
47}
48
Pierre Ossman7638e9c2014-01-16 13:12:40 +010049void SMsgReader::readClientInit()
50{
51 bool shared = is->readU8();
52 handler->clientInit(shared);
53}
54
55void SMsgReader::readMsg()
56{
57 int msgType = is->readU8();
58 switch (msgType) {
59 case msgTypeSetPixelFormat:
60 readSetPixelFormat();
61 break;
62 case msgTypeSetEncodings:
63 readSetEncodings();
64 break;
65 case msgTypeSetDesktopSize:
66 readSetDesktopSize();
67 break;
68 case msgTypeFramebufferUpdateRequest:
69 readFramebufferUpdateRequest();
70 break;
71 case msgTypeEnableContinuousUpdates:
72 readEnableContinuousUpdates();
73 break;
74 case msgTypeClientFence:
75 readFence();
76 break;
77 case msgTypeKeyEvent:
78 readKeyEvent();
79 break;
80 case msgTypePointerEvent:
81 readPointerEvent();
82 break;
83 case msgTypeClientCutText:
84 readClientCutText();
85 break;
Pierre Ossman5ae28212017-05-16 14:30:38 +020086 case msgTypeQEMUClientMessage:
87 readQEMUMessage();
88 break;
Pierre Ossman7638e9c2014-01-16 13:12:40 +010089 default:
Pierre Ossmane9e7da92016-04-20 09:38:06 +020090 vlog.error("unknown message type %d", msgType);
Pierre Ossman7638e9c2014-01-16 13:12:40 +010091 throw Exception("unknown message type");
92 }
93}
94
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000095void SMsgReader::readSetPixelFormat()
96{
97 is->skip(3);
98 PixelFormat pf;
99 pf.read(is);
100 handler->setPixelFormat(pf);
101}
102
103void SMsgReader::readSetEncodings()
104{
105 is->skip(1);
106 int nEncodings = is->readU16();
Peter Åstrand98fe98c2010-02-10 07:43:02 +0000107 rdr::S32Array encodings(nEncodings);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000108 for (int i = 0; i < nEncodings; i++)
109 encodings.buf[i] = is->readU32();
110 handler->setEncodings(nEncodings, encodings.buf);
111}
112
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100113void SMsgReader::readSetDesktopSize()
114{
115 int width, height;
116 int screens, i;
117 rdr::U32 id, flags;
118 int sx, sy, sw, sh;
119 ScreenSet layout;
120
121 is->skip(1);
122
123 width = is->readU16();
124 height = is->readU16();
125
126 screens = is->readU8();
127 is->skip(1);
128
129 for (i = 0;i < screens;i++) {
130 id = is->readU32();
131 sx = is->readU16();
132 sy = is->readU16();
133 sw = is->readU16();
134 sh = is->readU16();
135 flags = is->readU32();
136
137 layout.add_screen(Screen(id, sx, sy, sw, sh, flags));
138 }
139
140 handler->setDesktopSize(width, height, layout);
141}
142
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000143void SMsgReader::readFramebufferUpdateRequest()
144{
145 bool inc = is->readU8();
146 int x = is->readU16();
147 int y = is->readU16();
148 int w = is->readU16();
149 int h = is->readU16();
150 handler->framebufferUpdateRequest(Rect(x, y, x+w, y+h), inc);
151}
152
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100153void SMsgReader::readEnableContinuousUpdates()
154{
155 bool enable;
156 int x, y, w, h;
157
158 enable = is->readU8();
159
160 x = is->readU16();
161 y = is->readU16();
162 w = is->readU16();
163 h = is->readU16();
164
165 handler->enableContinuousUpdates(enable, x, y, w, h);
166}
167
168void SMsgReader::readFence()
169{
170 rdr::U32 flags;
171 rdr::U8 len;
172 char data[64];
173
174 is->skip(3);
175
176 flags = is->readU32();
177
178 len = is->readU8();
179 if (len > sizeof(data)) {
Pierre Ossmane9e7da92016-04-20 09:38:06 +0200180 vlog.error("Ignoring fence with too large payload");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100181 is->skip(len);
182 return;
183 }
184
185 is->readBytes(data, len);
186
187 handler->fence(flags, len, data);
188}
189
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000190void SMsgReader::readKeyEvent()
191{
192 bool down = is->readU8();
193 is->skip(2);
194 rdr::U32 key = is->readU32();
Pierre Ossman5ae28212017-05-16 14:30:38 +0200195 handler->keyEvent(key, 0, down);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000196}
197
198void SMsgReader::readPointerEvent()
199{
200 int mask = is->readU8();
201 int x = is->readU16();
202 int y = is->readU16();
203 handler->pointerEvent(Point(x, y), mask);
204}
205
206
207void SMsgReader::readClientCutText()
208{
209 is->skip(3);
Pierre Ossman0ff26552016-02-05 10:26:56 +0100210 rdr::U32 len = is->readU32();
211
212 if (len & 0x80000000) {
213 rdr::S32 slen = len;
214 slen = -slen;
215 readExtendedClipboard(slen);
216 return;
Michal Srbbf3bdac2017-03-27 13:37:11 +0300217 }
Pierre Ossman0ff26552016-02-05 10:26:56 +0100218
219 if (len > (size_t)maxCutText) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000220 is->skip(len);
Constantin Kaplinskyc7755da2008-04-24 11:01:55 +0000221 vlog.error("Cut text too long (%d bytes) - ignoring", len);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000222 return;
223 }
Pierre Ossman546b2ad2019-05-02 12:32:03 +0200224 CharArray ca(len);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000225 is->readBytes(ca.buf, len);
Pierre Ossman546b2ad2019-05-02 12:32:03 +0200226 CharArray filtered(convertLF(ca.buf, len));
Pierre Ossman66f1db52019-05-02 12:32:03 +0200227 handler->clientCutText(filtered.buf);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000228}
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000229
Pierre Ossman0ff26552016-02-05 10:26:56 +0100230void SMsgReader::readExtendedClipboard(rdr::S32 len)
231{
232 rdr::U32 flags;
233 rdr::U32 action;
234
235 if (len < 4)
236 throw Exception("Invalid extended clipboard message");
237 if (len > maxCutText) {
238 vlog.error("Extended clipboard message too long (%d bytes) - ignoring", len);
239 is->skip(len);
240 return;
241 }
242
243 flags = is->readU32();
244 action = flags & clipboardActionMask;
245
246 if (action & clipboardCaps) {
247 int i;
248 size_t num;
249 rdr::U32 lengths[16];
250
251 num = 0;
252 for (i = 0;i < 16;i++) {
253 if (flags & (1 << i))
254 num++;
255 }
256
257 if (len < (rdr::S32)(4 + 4*num))
258 throw Exception("Invalid extended clipboard message");
259
260 num = 0;
261 for (i = 0;i < 16;i++) {
262 if (flags & (1 << i))
263 lengths[num++] = is->readU32();
264 }
265
266 handler->handleClipboardCaps(flags, lengths);
267 } else if (action == clipboardProvide) {
268 rdr::ZlibInStream zis;
269
270 int i;
271 size_t num;
272 size_t lengths[16];
273 rdr::U8* buffers[16];
274
275 zis.setUnderlying(is, len - 4);
276
277 num = 0;
278 for (i = 0;i < 16;i++) {
279 if (!(flags & 1 << i))
280 continue;
281
282 lengths[num] = zis.readU32();
283 if (lengths[num] > (size_t)maxCutText) {
284 vlog.error("Extended clipboard data too long (%d bytes) - ignoring",
285 (unsigned)lengths[num]);
286 zis.skip(lengths[num]);
287 flags &= ~(1 << i);
288 continue;
289 }
290
291 buffers[num] = new rdr::U8[lengths[num]];
292 zis.readBytes(buffers[num], lengths[num]);
293 num++;
294 }
295
296 zis.removeUnderlying();
297
298 handler->handleClipboardProvide(flags, lengths, buffers);
299
300 num = 0;
301 for (i = 0;i < 16;i++) {
302 if (!(flags & 1 << i))
303 continue;
304 delete [] buffers[num++];
305 }
306 } else {
307 switch (action) {
308 case clipboardRequest:
309 handler->handleClipboardRequest(flags);
310 break;
311 case clipboardPeek:
312 handler->handleClipboardPeek(flags);
313 break;
314 case clipboardNotify:
315 handler->handleClipboardNotify(flags);
316 break;
317 default:
318 throw Exception("Invalid extended clipboard action");
319 }
320 }
321}
322
Pierre Ossman5ae28212017-05-16 14:30:38 +0200323void SMsgReader::readQEMUMessage()
324{
325 int subType = is->readU8();
326 switch (subType) {
327 case qemuExtendedKeyEvent:
328 readQEMUKeyEvent();
329 break;
330 default:
331 throw Exception("unknown QEMU submessage type %d", subType);
332 }
333}
334
335void SMsgReader::readQEMUKeyEvent()
336{
337 bool down = is->readU16();
338 rdr::U32 keysym = is->readU32();
339 rdr::U32 keycode = is->readU32();
340 if (!keycode) {
341 vlog.error("Key event without keycode - ignoring");
342 return;
343 }
344 handler->keyEvent(keysym, keycode, down);
345}