blob: bdde32538271381e7b2fb80a6422f535a7dffb09 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman615d16b2019-05-03 10:53:06 +02002 * Copyright 2011-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 */
Pierre Ossmandd45b442018-10-31 17:08:59 +010019#include <assert.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000020#include <stdio.h>
21#include <string.h>
Pierre Ossman0068a4f2015-11-09 15:48:19 +010022
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000023#include <rfb/Exception.h>
Pierre Ossman0ff26552016-02-05 10:26:56 +010024#include <rfb/clipboardTypes.h>
Pierre Ossmanc754cce2011-11-14 15:44:11 +000025#include <rfb/fenceTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010026#include <rfb/CMsgReader.h>
27#include <rfb/CMsgWriter.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000028#include <rfb/CSecurity.h>
Pierre Ossman96728352018-06-20 11:35:05 +020029#include <rfb/Decoder.h>
Adam Tkac5a0caed2010-04-23 13:58:10 +000030#include <rfb/Security.h>
Pierre Ossman0068a4f2015-11-09 15:48:19 +010031#include <rfb/SecurityClient.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000032#include <rfb/CConnection.h>
33#include <rfb/util.h>
34
35#include <rfb/LogWriter.h>
36
Pierre Ossman0068a4f2015-11-09 15:48:19 +010037#include <rdr/InStream.h>
38#include <rdr/OutStream.h>
39
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000040using namespace rfb;
41
42static LogWriter vlog("CConnection");
43
44CConnection::CConnection()
Pierre Ossmanb03512c2018-06-20 16:03:23 +020045 : csecurity(0),
46 supportsLocalCursor(false), supportsDesktopResize(false),
47 supportsLEDState(false),
48 is(0), os(0), reader_(0), writer_(0),
Adam Tkac05a0cd62010-07-20 15:07:44 +000049 shared(false),
Pierre Ossmancedce602019-04-01 14:39:39 +020050 state_(RFBSTATE_UNINITIALISED),
Pierre Ossmanef6881b2018-06-20 11:26:18 +020051 pendingPFChange(false), preferredEncoding(encodingTight),
Pierre Ossmanb03512c2018-06-20 16:03:23 +020052 compressLevel(2), qualityLevel(-1),
Pierre Ossmanef6881b2018-06-20 11:26:18 +020053 formatChange(false), encodingChange(false),
54 firstUpdate(true), pendingUpdate(false), continuousUpdates(false),
55 forceNonincremental(true),
Pierre Ossman615d16b2019-05-03 10:53:06 +020056 framebuffer(NULL), decoder(this),
Pierre Ossman0ff26552016-02-05 10:26:56 +010057 serverClipboard(NULL), hasLocalClipboard(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000058{
59}
60
61CConnection::~CConnection()
62{
Pierre Ossman9f273e92015-11-09 16:34:54 +010063 setFramebuffer(NULL);
Pierre Ossman82d22e62018-09-21 15:26:37 +020064 if (csecurity)
65 delete csecurity;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000066 delete reader_;
67 reader_ = 0;
68 delete writer_;
69 writer_ = 0;
Pierre Ossman615d16b2019-05-03 10:53:06 +020070 strFree(serverClipboard);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000071}
72
73void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
74{
75 is = is_;
76 os = os_;
77}
78
Pierre Ossman9f273e92015-11-09 16:34:54 +010079void CConnection::setFramebuffer(ModifiablePixelBuffer* fb)
80{
Pierre Ossman504afa22015-11-12 12:21:58 +010081 decoder.flush();
82
Pierre Ossman6ea58ba2018-06-20 15:47:49 +020083 if (fb) {
84 assert(fb->width() == server.width());
85 assert(fb->height() == server.height());
86 }
87
Pierre Ossman9f273e92015-11-09 16:34:54 +010088 if ((framebuffer != NULL) && (fb != NULL)) {
89 Rect rect;
90
91 const rdr::U8* data;
92 int stride;
93
94 const rdr::U8 black[4] = { 0, 0, 0, 0 };
95
96 // Copy still valid area
97
98 rect.setXYWH(0, 0,
99 __rfbmin(fb->width(), framebuffer->width()),
100 __rfbmin(fb->height(), framebuffer->height()));
101 data = framebuffer->getBuffer(framebuffer->getRect(), &stride);
102 fb->imageRect(rect, data, stride);
103
104 // Black out any new areas
105
106 if (fb->width() > framebuffer->width()) {
107 rect.setXYWH(framebuffer->width(), 0,
Brian P. Hinz5d663052016-09-05 09:15:50 -0400108 fb->width() - framebuffer->width(),
Pierre Ossman9f273e92015-11-09 16:34:54 +0100109 fb->height());
110 fb->fillRect(rect, black);
111 }
112
113 if (fb->height() > framebuffer->height()) {
114 rect.setXYWH(0, framebuffer->height(),
115 fb->width(),
116 fb->height() - framebuffer->height());
117 fb->fillRect(rect, black);
118 }
119 }
120
121 delete framebuffer;
122 framebuffer = fb;
123}
124
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000125void CConnection::initialiseProtocol()
126{
127 state_ = RFBSTATE_PROTOCOL_VERSION;
128}
129
130void CConnection::processMsg()
131{
132 switch (state_) {
133
134 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
135 case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
136 case RFBSTATE_SECURITY: processSecurityMsg(); break;
137 case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
138 case RFBSTATE_INITIALISATION: processInitMsg(); break;
139 case RFBSTATE_NORMAL: reader_->readMsg(); break;
140 case RFBSTATE_UNINITIALISED:
141 throw Exception("CConnection::processMsg: not initialised yet?");
142 default:
143 throw Exception("CConnection::processMsg: invalid state");
144 }
145}
146
147void CConnection::processVersionMsg()
148{
Pierre Ossman6a85e7a2019-04-01 14:55:40 +0200149 char verStr[27]; // FIXME: gcc has some bug in format-overflow
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200150 int majorVersion;
151 int minorVersion;
152
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000153 vlog.debug("reading protocol version");
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200154
155 if (!is->checkNoWait(12))
156 return;
157
158 is->readBytes(verStr, 12);
159 verStr[12] = '\0';
160
161 if (sscanf(verStr, "RFB %03d.%03d\n",
162 &majorVersion, &minorVersion) != 2) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000163 state_ = RFBSTATE_INVALID;
164 throw Exception("reading version failed: not an RFB server?");
165 }
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200166
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200167 server.setVersion(majorVersion, minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000168
169 vlog.info("Server supports RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200170 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000171
172 // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200173 if (server.beforeVersion(3,3)) {
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100174 vlog.error("Server gave unsupported RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200175 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000176 state_ = RFBSTATE_INVALID;
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100177 throw Exception("Server gave unsupported RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200178 server.majorVersion, server.minorVersion);
Pierre Ossmancedce602019-04-01 14:39:39 +0200179 } else if (server.beforeVersion(3,7)) {
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200180 server.setVersion(3,3);
181 } else if (server.afterVersion(3,8)) {
182 server.setVersion(3,8);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000183 }
184
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200185 sprintf(verStr, "RFB %03d.%03d\n",
186 server.majorVersion, server.minorVersion);
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200187 os->writeBytes(verStr, 12);
188 os->flush();
189
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000190 state_ = RFBSTATE_SECURITY_TYPES;
191
192 vlog.info("Using RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200193 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000194}
195
196
197void CConnection::processSecurityTypesMsg()
198{
199 vlog.debug("processing security types message");
200
201 int secType = secTypeInvalid;
202
Adam Tkac05a0cd62010-07-20 15:07:44 +0000203 std::list<rdr::U8> secTypes;
Michal Srbdccb5f72017-03-27 13:55:46 +0300204 secTypes = security.GetEnabledSecTypes();
Adam Tkac05a0cd62010-07-20 15:07:44 +0000205
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200206 if (server.isVersion(3,3)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000207
208 // legacy 3.3 server may only offer "vnc authentication" or "none"
209
210 secType = is->readU32();
211 if (secType == secTypeInvalid) {
212 throwConnFailedException();
213
214 } else if (secType == secTypeNone || secType == secTypeVncAuth) {
Adam Tkac05a0cd62010-07-20 15:07:44 +0000215 std::list<rdr::U8>::iterator i;
216 for (i = secTypes.begin(); i != secTypes.end(); i++)
217 if (*i == secType) {
218 secType = *i;
219 break;
220 }
221
222 if (i == secTypes.end())
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000223 secType = secTypeInvalid;
224 } else {
225 vlog.error("Unknown 3.3 security type %d", secType);
226 throw Exception("Unknown 3.3 security type");
227 }
228
229 } else {
230
231 // >=3.7 server will offer us a list
232
233 int nServerSecTypes = is->readU8();
234 if (nServerSecTypes == 0)
235 throwConnFailedException();
236
Adam Tkac05a0cd62010-07-20 15:07:44 +0000237 std::list<rdr::U8>::iterator j;
Adam Tkac05a0cd62010-07-20 15:07:44 +0000238
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000239 for (int i = 0; i < nServerSecTypes; i++) {
240 rdr::U8 serverSecType = is->readU8();
241 vlog.debug("Server offers security type %s(%d)",
Adam Tkac7cb47d62011-02-21 12:55:24 +0000242 secTypeName(serverSecType), serverSecType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000243
Adam Tkac7cb47d62011-02-21 12:55:24 +0000244 /*
245 * Use the first type sent by server which matches client's type.
246 * It means server's order specifies priority.
247 */
248 if (secType == secTypeInvalid) {
249 for (j = secTypes.begin(); j != secTypes.end(); j++)
250 if (*j == serverSecType) {
251 secType = *j;
252 break;
253 }
254 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000255 }
256
257 // Inform the server of our decision
258 if (secType != secTypeInvalid) {
259 os->writeU8(secType);
260 os->flush();
Pierre Ossman71d66662014-11-11 13:42:51 +0100261 vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000262 }
263 }
264
265 if (secType == secTypeInvalid) {
266 state_ = RFBSTATE_INVALID;
267 vlog.error("No matching security types");
268 throw Exception("No matching security types");
269 }
270
271 state_ = RFBSTATE_SECURITY;
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200272 csecurity = security.GetCSecurity(this, secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000273 processSecurityMsg();
274}
275
276void CConnection::processSecurityMsg()
277{
278 vlog.debug("processing security message");
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200279 if (csecurity->processMsg()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000280 state_ = RFBSTATE_SECURITY_RESULT;
281 processSecurityResultMsg();
282 }
283}
284
285void CConnection::processSecurityResultMsg()
286{
287 vlog.debug("processing security result message");
288 int result;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200289 if (server.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000290 result = secResultOK;
291 } else {
292 if (!is->checkNoWait(1)) return;
293 result = is->readU32();
294 }
295 switch (result) {
296 case secResultOK:
297 securityCompleted();
298 return;
299 case secResultFailed:
300 vlog.debug("auth failed");
301 break;
302 case secResultTooMany:
303 vlog.debug("auth failed - too many tries");
304 break;
305 default:
306 throw Exception("Unknown security result from server");
307 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000308 state_ = RFBSTATE_INVALID;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200309 if (server.beforeVersion(3,8))
Pierre Ossman19225502017-10-12 15:05:07 +0200310 throw AuthFailureException();
311 CharArray reason(is->readString());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000312 throw AuthFailureException(reason.buf);
313}
314
315void CConnection::processInitMsg()
316{
317 vlog.debug("reading server initialisation");
318 reader_->readServerInit();
319}
320
321void CConnection::throwConnFailedException()
322{
323 state_ = RFBSTATE_INVALID;
324 CharArray reason;
325 reason.buf = is->readString();
326 throw ConnFailedException(reason.buf);
327}
328
329void CConnection::securityCompleted()
330{
331 state_ = RFBSTATE_INITIALISATION;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100332 reader_ = new CMsgReader(this, is);
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200333 writer_ = new CMsgWriter(&server, os);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000334 vlog.debug("Authentication success!");
335 authSuccess();
336 writer_->writeClientInit(shared);
337}
338
Pierre Ossman3da238d2015-11-12 12:20:05 +0100339void CConnection::setDesktopSize(int w, int h)
340{
Pierre Ossman504afa22015-11-12 12:21:58 +0100341 decoder.flush();
342
Pierre Ossman3da238d2015-11-12 12:20:05 +0100343 CMsgHandler::setDesktopSize(w,h);
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200344
345 if (continuousUpdates)
346 writer()->writeEnableContinuousUpdates(true, 0, 0,
347 server.width(),
348 server.height());
Pierre Ossman6ea58ba2018-06-20 15:47:49 +0200349
350 resizeFramebuffer();
351 assert(framebuffer != NULL);
352 assert(framebuffer->width() == server.width());
353 assert(framebuffer->height() == server.height());
Pierre Ossman3da238d2015-11-12 12:20:05 +0100354}
355
356void CConnection::setExtendedDesktopSize(unsigned reason,
357 unsigned result,
358 int w, int h,
359 const ScreenSet& layout)
360{
Pierre Ossman504afa22015-11-12 12:21:58 +0100361 decoder.flush();
362
Pierre Ossman3da238d2015-11-12 12:20:05 +0100363 CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout);
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200364
365 if (continuousUpdates)
366 writer()->writeEnableContinuousUpdates(true, 0, 0,
367 server.width(),
368 server.height());
Pierre Ossman6ea58ba2018-06-20 15:47:49 +0200369
370 resizeFramebuffer();
371 assert(framebuffer != NULL);
372 assert(framebuffer->width() == server.width());
373 assert(framebuffer->height() == server.height());
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200374}
375
376void CConnection::endOfContinuousUpdates()
377{
378 CMsgHandler::endOfContinuousUpdates();
379
380 // We've gotten the marker for a format change, so make the pending
381 // one active
382 if (pendingPFChange) {
383 server.setPF(pendingPF);
384 pendingPFChange = false;
385
386 // We might have another change pending
387 if (formatChange)
388 requestNewUpdate();
389 }
Pierre Ossman3da238d2015-11-12 12:20:05 +0100390}
391
Pierre Ossmandd45b442018-10-31 17:08:59 +0100392void CConnection::serverInit(int width, int height,
393 const PixelFormat& pf,
394 const char* name)
Pierre Ossman2affd772018-06-20 07:03:10 +0200395{
Pierre Ossmandd45b442018-10-31 17:08:59 +0100396 CMsgHandler::serverInit(width, height, pf, name);
397
Pierre Ossman2affd772018-06-20 07:03:10 +0200398 state_ = RFBSTATE_NORMAL;
399 vlog.debug("initialisation done");
400
401 initDone();
Pierre Ossmandd45b442018-10-31 17:08:59 +0100402 assert(framebuffer != NULL);
403 assert(framebuffer->width() == server.width());
404 assert(framebuffer->height() == server.height());
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200405
406 // We want to make sure we call SetEncodings at least once
407 encodingChange = true;
408
409 requestNewUpdate();
410
411 // This initial update request is a bit of a corner case, so we need
412 // to help out setting the correct format here.
413 if (pendingPFChange) {
414 server.setPF(pendingPF);
415 pendingPFChange = false;
416 }
Pierre Ossman2affd772018-06-20 07:03:10 +0200417}
418
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100419void CConnection::readAndDecodeRect(const Rect& r, int encoding,
420 ModifiablePixelBuffer* pb)
421{
422 decoder.decodeRect(r, encoding, pb);
423 decoder.flush();
424}
425
Pierre Ossman3da238d2015-11-12 12:20:05 +0100426void CConnection::framebufferUpdateStart()
427{
428 CMsgHandler::framebufferUpdateStart();
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200429
430 assert(framebuffer != NULL);
431
432 // Note: This might not be true if continuous updates are supported
433 pendingUpdate = false;
434
435 requestNewUpdate();
Pierre Ossman3da238d2015-11-12 12:20:05 +0100436}
437
438void CConnection::framebufferUpdateEnd()
439{
Pierre Ossman504afa22015-11-12 12:21:58 +0100440 decoder.flush();
441
Pierre Ossman3da238d2015-11-12 12:20:05 +0100442 CMsgHandler::framebufferUpdateEnd();
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200443
444 // A format change has been scheduled and we are now past the update
445 // with the old format. Time to active the new one.
446 if (pendingPFChange && !continuousUpdates) {
447 server.setPF(pendingPF);
448 pendingPFChange = false;
449 }
450
451 if (firstUpdate) {
452 if (server.supportsContinuousUpdates) {
453 vlog.info("Enabling continuous updates");
454 continuousUpdates = true;
455 writer()->writeEnableContinuousUpdates(true, 0, 0,
456 server.width(),
457 server.height());
458 }
459
460 firstUpdate = false;
461 }
Pierre Ossman3da238d2015-11-12 12:20:05 +0100462}
463
Pierre Ossman9f273e92015-11-09 16:34:54 +0100464void CConnection::dataRect(const Rect& r, int encoding)
465{
466 decoder.decodeRect(r, encoding, framebuffer);
467}
468
Pierre Ossman615d16b2019-05-03 10:53:06 +0200469void CConnection::serverCutText(const char* str)
470{
Pierre Ossman0ff26552016-02-05 10:26:56 +0100471 hasLocalClipboard = false;
472
Pierre Ossman615d16b2019-05-03 10:53:06 +0200473 strFree(serverClipboard);
474 serverClipboard = NULL;
475
Pierre Ossman5fbbe102019-05-10 11:44:19 +0200476 serverClipboard = latin1ToUTF8(str);
Pierre Ossman615d16b2019-05-03 10:53:06 +0200477
478 handleClipboardAnnounce(true);
479}
480
Pierre Ossman0ff26552016-02-05 10:26:56 +0100481void CConnection::handleClipboardCaps(rdr::U32 flags,
482 const rdr::U32* lengths)
483{
484 rdr::U32 sizes[] = { 0 };
485
486 CMsgHandler::handleClipboardCaps(flags, lengths);
487
488 writer()->writeClipboardCaps(rfb::clipboardUTF8 |
489 rfb::clipboardRequest |
490 rfb::clipboardPeek |
491 rfb::clipboardNotify |
492 rfb::clipboardProvide,
493 sizes);
494}
495
496void CConnection::handleClipboardRequest(rdr::U32 flags)
497{
498 if (!(flags & rfb::clipboardUTF8))
499 return;
500 if (!hasLocalClipboard)
501 return;
502 handleClipboardRequest();
503}
504
505void CConnection::handleClipboardPeek(rdr::U32 flags)
506{
507 if (!hasLocalClipboard)
508 return;
509 if (server.clipboardFlags() & rfb::clipboardNotify)
510 writer()->writeClipboardNotify(rfb::clipboardUTF8);
511}
512
513void CConnection::handleClipboardNotify(rdr::U32 flags)
514{
515 strFree(serverClipboard);
516 serverClipboard = NULL;
517
518 if (flags & rfb::clipboardUTF8) {
519 hasLocalClipboard = false;
520 handleClipboardAnnounce(true);
521 } else {
522 handleClipboardAnnounce(false);
523 }
524}
525
526void CConnection::handleClipboardProvide(rdr::U32 flags,
527 const size_t* lengths,
528 const rdr::U8* const* data)
529{
530 if (!(flags & rfb::clipboardUTF8))
531 return;
532
533 strFree(serverClipboard);
534 serverClipboard = NULL;
535
536 serverClipboard = convertLF((const char*)data[0], lengths[0]);
537
538 // FIXME: Should probably verify that this data was actually requested
539 handleClipboardData(serverClipboard);
540}
541
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000542void CConnection::authSuccess()
543{
544}
545
Pierre Ossman2affd772018-06-20 07:03:10 +0200546void CConnection::initDone()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000547{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000548}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000549
Pierre Ossman6ea58ba2018-06-20 15:47:49 +0200550void CConnection::resizeFramebuffer()
551{
552 assert(false);
553}
554
Pierre Ossman615d16b2019-05-03 10:53:06 +0200555void CConnection::handleClipboardRequest()
556{
557}
558
559void CConnection::handleClipboardAnnounce(bool available)
560{
561}
562
563void CConnection::handleClipboardData(const char* data)
564{
565}
566
567void CConnection::requestClipboard()
568{
569 if (serverClipboard != NULL) {
570 handleClipboardData(serverClipboard);
571 return;
572 }
Pierre Ossman0ff26552016-02-05 10:26:56 +0100573
574 if (server.clipboardFlags() & rfb::clipboardRequest)
575 writer()->writeClipboardRequest(rfb::clipboardUTF8);
Pierre Ossman615d16b2019-05-03 10:53:06 +0200576}
577
578void CConnection::announceClipboard(bool available)
579{
Pierre Ossman0ff26552016-02-05 10:26:56 +0100580 hasLocalClipboard = available;
581
582 if (server.clipboardFlags() & rfb::clipboardNotify)
583 writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
584 else {
585 if (available)
586 handleClipboardRequest();
587 }
Pierre Ossman615d16b2019-05-03 10:53:06 +0200588}
589
590void CConnection::sendClipboardData(const char* data)
591{
Pierre Ossman0ff26552016-02-05 10:26:56 +0100592 if (server.clipboardFlags() & rfb::clipboardProvide) {
593 CharArray filtered(convertCRLF(data));
594 size_t sizes[1] = { strlen(filtered.buf) + 1 };
595 const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf };
596 writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data);
597 } else {
598 CharArray latin1(utf8ToLatin1(data));
Pierre Ossman5fbbe102019-05-10 11:44:19 +0200599
Pierre Ossman0ff26552016-02-05 10:26:56 +0100600 writer()->writeClientCutText(latin1.buf);
601 }
Pierre Ossman615d16b2019-05-03 10:53:06 +0200602}
603
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200604void CConnection::refreshFramebuffer()
605{
606 forceNonincremental = true;
607
608 // Without continuous updates we have to make sure we only have a
609 // single update in flight, so we'll have to wait to do the refresh
610 if (continuousUpdates)
611 requestNewUpdate();
612}
613
614void CConnection::setPreferredEncoding(int encoding)
615{
616 if (preferredEncoding == encoding)
617 return;
618
619 preferredEncoding = encoding;
620 encodingChange = true;
621}
622
623int CConnection::getPreferredEncoding()
624{
625 return preferredEncoding;
626}
627
628void CConnection::setCompressLevel(int level)
629{
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200630 if (compressLevel == level)
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200631 return;
632
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200633 compressLevel = level;
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200634 encodingChange = true;
635}
636
637void CConnection::setQualityLevel(int level)
638{
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200639 if (qualityLevel == level)
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200640 return;
641
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200642 qualityLevel = level;
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200643 encodingChange = true;
644}
645
646void CConnection::setPF(const PixelFormat& pf)
647{
648 if (server.pf().equal(pf) && !formatChange)
649 return;
650
651 nextPF = pf;
652 formatChange = true;
653}
654
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000655void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
656{
657 CMsgHandler::fence(flags, len, data);
658
659 if (!(flags & fenceFlagRequest))
660 return;
661
662 // We cannot guarantee any synchronisation at this level
663 flags = 0;
664
665 writer()->writeFence(flags, len, data);
666}
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200667
668// requestNewUpdate() requests an update from the server, having set the
669// format and encoding appropriately.
670void CConnection::requestNewUpdate()
671{
672 if (formatChange && !pendingPFChange) {
673 /* Catch incorrect requestNewUpdate calls */
674 assert(!pendingUpdate || continuousUpdates);
675
676 // We have to make sure we switch the internal format at a safe
677 // time. For continuous updates we temporarily disable updates and
678 // look for a EndOfContinuousUpdates message to see when to switch.
679 // For classical updates we just got a new update right before this
680 // function was called, so we need to make sure we finish that
681 // update before we can switch.
682
683 pendingPFChange = true;
684 pendingPF = nextPF;
685
686 if (continuousUpdates)
687 writer()->writeEnableContinuousUpdates(false, 0, 0, 0, 0);
688
689 writer()->writeSetPixelFormat(pendingPF);
690
691 if (continuousUpdates)
692 writer()->writeEnableContinuousUpdates(true, 0, 0,
693 server.width(),
694 server.height());
695
696 formatChange = false;
697 }
698
699 if (encodingChange) {
Pierre Ossman96728352018-06-20 11:35:05 +0200700 updateEncodings();
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200701 encodingChange = false;
702 }
703
704 if (forceNonincremental || !continuousUpdates) {
705 pendingUpdate = true;
706 writer()->writeFramebufferUpdateRequest(Rect(0, 0,
707 server.width(),
708 server.height()),
709 !forceNonincremental);
710 }
711
712 forceNonincremental = false;
713}
Pierre Ossman96728352018-06-20 11:35:05 +0200714
715// Ask for encodings based on which decoders are supported. Assumes higher
716// encoding numbers are more desirable.
717
718void CConnection::updateEncodings()
719{
Pierre Ossman1143ee62018-06-20 11:40:37 +0200720 std::list<rdr::U32> encodings;
Pierre Ossman96728352018-06-20 11:35:05 +0200721
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200722 if (supportsLocalCursor) {
Pierre Ossman1143ee62018-06-20 11:40:37 +0200723 encodings.push_back(pseudoEncodingCursorWithAlpha);
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100724 encodings.push_back(pseudoEncodingVMwareCursor);
Pierre Ossman1143ee62018-06-20 11:40:37 +0200725 encodings.push_back(pseudoEncodingCursor);
726 encodings.push_back(pseudoEncodingXCursor);
Pierre Ossman96728352018-06-20 11:35:05 +0200727 }
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200728 if (supportsDesktopResize) {
Pierre Ossman1143ee62018-06-20 11:40:37 +0200729 encodings.push_back(pseudoEncodingDesktopSize);
Pierre Ossman1143ee62018-06-20 11:40:37 +0200730 encodings.push_back(pseudoEncodingExtendedDesktopSize);
Pierre Ossman6ea58ba2018-06-20 15:47:49 +0200731 }
Pierre Ossman62b07862018-11-05 16:28:57 +0100732 if (supportsLEDState) {
Pierre Ossman1143ee62018-06-20 11:40:37 +0200733 encodings.push_back(pseudoEncodingLEDState);
Pierre Ossman62b07862018-11-05 16:28:57 +0100734 encodings.push_back(pseudoEncodingVMwareLEDState);
735 }
Pierre Ossman96728352018-06-20 11:35:05 +0200736
Pierre Ossman5588f4f2018-06-20 11:48:17 +0200737 encodings.push_back(pseudoEncodingDesktopName);
Pierre Ossman1143ee62018-06-20 11:40:37 +0200738 encodings.push_back(pseudoEncodingLastRect);
Pierre Ossman0ff26552016-02-05 10:26:56 +0100739 encodings.push_back(pseudoEncodingExtendedClipboard);
Pierre Ossman1143ee62018-06-20 11:40:37 +0200740 encodings.push_back(pseudoEncodingContinuousUpdates);
741 encodings.push_back(pseudoEncodingFence);
742 encodings.push_back(pseudoEncodingQEMUKeyEvent);
Pierre Ossman96728352018-06-20 11:35:05 +0200743
744 if (Decoder::supported(preferredEncoding)) {
Pierre Ossman1143ee62018-06-20 11:40:37 +0200745 encodings.push_back(preferredEncoding);
Pierre Ossman96728352018-06-20 11:35:05 +0200746 }
747
Pierre Ossman1143ee62018-06-20 11:40:37 +0200748 encodings.push_back(encodingCopyRect);
Pierre Ossman96728352018-06-20 11:35:05 +0200749
Pierre Ossman96728352018-06-20 11:35:05 +0200750 for (int i = encodingMax; i >= 0; i--) {
Pierre Ossman3bbe8d72018-06-20 11:42:36 +0200751 if ((i != preferredEncoding) && Decoder::supported(i))
752 encodings.push_back(i);
Pierre Ossman96728352018-06-20 11:35:05 +0200753 }
754
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200755 if (compressLevel >= 0 && compressLevel <= 9)
756 encodings.push_back(pseudoEncodingCompressLevel0 + compressLevel);
757 if (qualityLevel >= 0 && qualityLevel <= 9)
758 encodings.push_back(pseudoEncodingQualityLevel0 + qualityLevel);
Pierre Ossman96728352018-06-20 11:35:05 +0200759
Pierre Ossman1143ee62018-06-20 11:40:37 +0200760 writer()->writeSetEncodings(encodings);
Pierre Ossman96728352018-06-20 11:35:05 +0200761}