blob: 4869199a088328ebd8d649f452ea95401000405c [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 */
19#include <stdio.h>
20#include <string.h>
21#include <rfb/Exception.h>
Adam Tkac5a0caed2010-04-23 13:58:10 +000022#include <rfb/Security.h>
Pierre Ossman0ff26552016-02-05 10:26:56 +010023#include <rfb/clipboardTypes.h>
Constantin Kaplinskydafbb012007-04-05 08:43:25 +000024#include <rfb/msgTypes.h>
Pierre Ossmanc754cce2011-11-14 15:44:11 +000025#include <rfb/fenceTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010026#include <rfb/SMsgReader.h>
27#include <rfb/SMsgWriter.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000028#include <rfb/SConnection.h>
29#include <rfb/ServerCore.h>
Pierre Ossman48700812014-09-17 17:11:56 +020030#include <rfb/encodings.h>
31#include <rfb/EncodeManager.h>
Michal Srb8d1ee002014-11-10 09:15:41 +020032#include <rfb/SSecurity.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000033
34#include <rfb/LogWriter.h>
35
36using namespace rfb;
37
38static LogWriter vlog("SConnection");
39
40// AccessRights values
Michal Srbb318b8f2014-11-24 13:18:28 +020041const SConnection::AccessRights SConnection::AccessView = 0x0001;
42const SConnection::AccessRights SConnection::AccessKeyEvents = 0x0002;
43const SConnection::AccessRights SConnection::AccessPtrEvents = 0x0004;
44const SConnection::AccessRights SConnection::AccessCutText = 0x0008;
45const SConnection::AccessRights SConnection::AccessSetDesktopSize = 0x0010;
Pierre Ossmane7be49b2014-12-02 14:33:17 +010046const SConnection::AccessRights SConnection::AccessNonShared = 0x0020;
Michal Srbb318b8f2014-11-24 13:18:28 +020047const SConnection::AccessRights SConnection::AccessDefault = 0x03ff;
48const SConnection::AccessRights SConnection::AccessNoQuery = 0x0400;
49const SConnection::AccessRights SConnection::AccessFull = 0xffff;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000050
51
Pierre Ossman7069bdd2015-02-06 14:41:58 +010052SConnection::SConnection()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000053 : readyForSetColourMapEntries(false),
54 is(0), os(0), reader_(0), writer_(0),
Michal Srbdccb5f72017-03-27 13:55:46 +030055 ssecurity(0), state_(RFBSTATE_UNINITIALISED),
Pierre Ossman615d16b2019-05-03 10:53:06 +020056 preferredEncoding(encodingRaw),
Pierre Ossman0ff26552016-02-05 10:26:56 +010057 clientClipboard(NULL), hasLocalClipboard(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000058{
59 defaultMajorVersion = 3;
60 defaultMinorVersion = 8;
61 if (rfb::Server::protocol3_3)
62 defaultMinorVersion = 3;
63
Pierre Ossman0d3ce872018-06-18 15:59:00 +020064 client.setVersion(defaultMajorVersion, defaultMinorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000065}
66
67SConnection::~SConnection()
68{
Pierre Ossman82d22e62018-09-21 15:26:37 +020069 if (ssecurity)
70 delete ssecurity;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000071 delete reader_;
72 reader_ = 0;
73 delete writer_;
74 writer_ = 0;
Pierre Ossman615d16b2019-05-03 10:53:06 +020075 strFree(clientClipboard);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000076}
77
78void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
79{
80 is = is_;
81 os = os_;
82}
83
84void SConnection::initialiseProtocol()
85{
Pierre Ossmanea7ede92018-06-18 16:51:53 +020086 char str[13];
87
88 sprintf(str, "RFB %03d.%03d\n", defaultMajorVersion, defaultMinorVersion);
89 os->writeBytes(str, 12);
90 os->flush();
91
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000092 state_ = RFBSTATE_PROTOCOL_VERSION;
93}
94
95void SConnection::processMsg()
96{
97 switch (state_) {
98 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
99 case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
100 case RFBSTATE_SECURITY: processSecurityMsg(); break;
101 case RFBSTATE_INITIALISATION: processInitMsg(); break;
102 case RFBSTATE_NORMAL: reader_->readMsg(); break;
103 case RFBSTATE_QUERYING:
104 throw Exception("SConnection::processMsg: bogus data from client while "
105 "querying");
106 case RFBSTATE_UNINITIALISED:
107 throw Exception("SConnection::processMsg: not initialised yet?");
108 default:
109 throw Exception("SConnection::processMsg: invalid state");
110 }
111}
112
113void SConnection::processVersionMsg()
114{
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200115 char verStr[13];
116 int majorVersion;
117 int minorVersion;
118
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000119 vlog.debug("reading protocol version");
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200120
121 if (!is->checkNoWait(12))
122 return;
123
124 is->readBytes(verStr, 12);
125 verStr[12] = '\0';
126
127 if (sscanf(verStr, "RFB %03d.%03d\n",
128 &majorVersion, &minorVersion) != 2) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000129 state_ = RFBSTATE_INVALID;
130 throw Exception("reading version failed: not an RFB client?");
131 }
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200132
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200133 client.setVersion(majorVersion, minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000134
135 vlog.info("Client needs protocol version %d.%d",
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200136 client.majorVersion, client.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000137
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200138 if (client.majorVersion != 3) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000139 // unknown protocol version
Pierre Ossman19225502017-10-12 15:05:07 +0200140 throwConnFailedException("Client needs protocol version %d.%d, server has %d.%d",
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200141 client.majorVersion, client.minorVersion,
Pierre Ossman19225502017-10-12 15:05:07 +0200142 defaultMajorVersion, defaultMinorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000143 }
144
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200145 if (client.minorVersion != 3 && client.minorVersion != 7 && client.minorVersion != 8) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000146 vlog.error("Client uses unofficial protocol version %d.%d",
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200147 client.majorVersion,client.minorVersion);
148 if (client.minorVersion >= 8)
149 client.minorVersion = 8;
150 else if (client.minorVersion == 7)
151 client.minorVersion = 7;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000152 else
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200153 client.minorVersion = 3;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000154 vlog.error("Assuming compatibility with version %d.%d",
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200155 client.majorVersion,client.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000156 }
157
158 versionReceived();
159
160 std::list<rdr::U8> secTypes;
161 std::list<rdr::U8>::iterator i;
Michal Srbdccb5f72017-03-27 13:55:46 +0300162 secTypes = security.GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000163
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200164 if (client.isVersion(3,3)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000165
166 // cope with legacy 3.3 client only if "no authentication" or "vnc
167 // authentication" is supported.
168 for (i=secTypes.begin(); i!=secTypes.end(); i++) {
169 if (*i == secTypeNone || *i == secTypeVncAuth) break;
170 }
171 if (i == secTypes.end()) {
Pierre Ossman19225502017-10-12 15:05:07 +0200172 throwConnFailedException("No supported security type for %d.%d client",
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200173 client.majorVersion, client.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000174 }
175
176 os->writeU32(*i);
177 if (*i == secTypeNone) os->flush();
178 state_ = RFBSTATE_SECURITY;
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200179 ssecurity = security.GetSSecurity(this, *i);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000180 processSecurityMsg();
181 return;
182 }
183
184 // list supported security types for >=3.7 clients
185
186 if (secTypes.empty())
187 throwConnFailedException("No supported security types");
188
189 os->writeU8(secTypes.size());
190 for (i=secTypes.begin(); i!=secTypes.end(); i++)
191 os->writeU8(*i);
192 os->flush();
193 state_ = RFBSTATE_SECURITY_TYPE;
194}
195
196
197void SConnection::processSecurityTypeMsg()
198{
199 vlog.debug("processing security type message");
200 int secType = is->readU8();
201
Constantin Kaplinsky5fa9d222006-09-06 10:32:06 +0000202 processSecurityType(secType);
203}
204
205void SConnection::processSecurityType(int secType)
206{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000207 // Verify that the requested security type should be offered
208 std::list<rdr::U8> secTypes;
209 std::list<rdr::U8>::iterator i;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000210
Michal Srbdccb5f72017-03-27 13:55:46 +0300211 secTypes = security.GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000212 for (i=secTypes.begin(); i!=secTypes.end(); i++)
213 if (*i == secType) break;
214 if (i == secTypes.end())
215 throw Exception("Requested security type not available");
216
217 vlog.info("Client requests security type %s(%d)",
218 secTypeName(secType),secType);
219
220 try {
221 state_ = RFBSTATE_SECURITY;
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200222 ssecurity = security.GetSSecurity(this, secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000223 } catch (rdr::Exception& e) {
Pierre Ossman19225502017-10-12 15:05:07 +0200224 throwConnFailedException("%s", e.str());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000225 }
226
227 processSecurityMsg();
228}
229
230void SConnection::processSecurityMsg()
231{
232 vlog.debug("processing security message");
233 try {
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200234 bool done = ssecurity->processMsg();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000235 if (done) {
236 state_ = RFBSTATE_QUERYING;
Michal Srb8d1ee002014-11-10 09:15:41 +0200237 setAccessRights(ssecurity->getAccessRights());
Henrik Anderssonc1cbc702016-01-27 14:00:44 +0100238 queryConnection(ssecurity->getUserName());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000239 }
240 } catch (AuthFailureException& e) {
241 vlog.error("AuthFailureException: %s", e.str());
Pierre Ossman88a94ed2019-04-01 14:22:01 +0200242 state_ = RFBSTATE_SECURITY_FAILURE;
243 authFailure(e.str());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000244 }
245}
246
247void SConnection::processInitMsg()
248{
249 vlog.debug("reading client initialisation");
250 reader_->readClientInit();
251}
252
Pierre Ossman19225502017-10-12 15:05:07 +0200253void SConnection::throwConnFailedException(const char* format, ...)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000254{
Pierre Ossman19225502017-10-12 15:05:07 +0200255 va_list ap;
256 char str[256];
257
258 va_start(ap, format);
259 (void) vsnprintf(str, sizeof(str), format, ap);
260 va_end(ap);
261
262 vlog.info("Connection failed: %s", str);
263
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000264 if (state_ == RFBSTATE_PROTOCOL_VERSION) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200265 if (client.majorVersion == 3 && client.minorVersion == 3) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000266 os->writeU32(0);
Pierre Ossman19225502017-10-12 15:05:07 +0200267 os->writeString(str);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000268 os->flush();
269 } else {
270 os->writeU8(0);
Pierre Ossman19225502017-10-12 15:05:07 +0200271 os->writeString(str);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000272 os->flush();
273 }
274 }
Pierre Ossman19225502017-10-12 15:05:07 +0200275
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000276 state_ = RFBSTATE_INVALID;
Pierre Ossman19225502017-10-12 15:05:07 +0200277 throw ConnFailedException(str);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000278}
279
Pierre Ossman7d64b332018-10-08 15:59:02 +0200280void SConnection::setAccessRights(AccessRights ar)
281{
282 accessRights = ar;
283}
284
285bool SConnection::accessCheck(AccessRights ar) const
286{
287 return (accessRights & ar) == ar;
288}
289
Pierre Ossmanf38e2432015-02-11 13:47:58 +0100290void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
Pierre Ossman48700812014-09-17 17:11:56 +0200291{
292 int i;
293
294 preferredEncoding = encodingRaw;
295 for (i = 0;i < nEncodings;i++) {
296 if (EncodeManager::supported(encodings[i])) {
297 preferredEncoding = encodings[i];
298 break;
299 }
300 }
301
302 SMsgHandler::setEncodings(nEncodings, encodings);
Pierre Ossman0ff26552016-02-05 10:26:56 +0100303
304 if (client.supportsEncoding(pseudoEncodingExtendedClipboard)) {
305 rdr::U32 sizes[] = { 0 };
306 writer()->writeClipboardCaps(rfb::clipboardUTF8 |
307 rfb::clipboardRequest |
308 rfb::clipboardPeek |
309 rfb::clipboardNotify |
310 rfb::clipboardProvide,
311 sizes);
312 }
Pierre Ossman48700812014-09-17 17:11:56 +0200313}
314
Pierre Ossman615d16b2019-05-03 10:53:06 +0200315void SConnection::clientCutText(const char* str)
316{
317 strFree(clientClipboard);
318 clientClipboard = NULL;
319
Pierre Ossman5fbbe102019-05-10 11:44:19 +0200320 clientClipboard = latin1ToUTF8(str);
Pierre Ossman615d16b2019-05-03 10:53:06 +0200321
322 handleClipboardAnnounce(true);
323}
324
Pierre Ossman0ff26552016-02-05 10:26:56 +0100325void SConnection::handleClipboardRequest(rdr::U32 flags)
326{
327 if (!(flags & rfb::clipboardUTF8))
328 return;
329 if (!hasLocalClipboard)
330 return;
331 handleClipboardRequest();
332}
333
334void SConnection::handleClipboardPeek(rdr::U32 flags)
335{
336 if (!hasLocalClipboard)
337 return;
338 if (client.clipboardFlags() & rfb::clipboardNotify)
339 writer()->writeClipboardNotify(rfb::clipboardUTF8);
340}
341
342void SConnection::handleClipboardNotify(rdr::U32 flags)
343{
344 strFree(clientClipboard);
345 clientClipboard = NULL;
346
347 if (flags & rfb::clipboardUTF8)
348 handleClipboardAnnounce(true);
349 else
350 handleClipboardAnnounce(false);
351}
352
353void SConnection::handleClipboardProvide(rdr::U32 flags,
354 const size_t* lengths,
355 const rdr::U8* const* data)
356{
357 if (!(flags & rfb::clipboardUTF8))
358 return;
359
360 strFree(clientClipboard);
361 clientClipboard = NULL;
362
363 clientClipboard = convertLF((const char*)data[0], lengths[0]);
364
365 handleClipboardData(clientClipboard);
366}
367
Pierre Ossman5ae28212017-05-16 14:30:38 +0200368void SConnection::supportsQEMUKeyEvent()
369{
370 writer()->writeQEMUKeyEvent();
371}
372
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000373void SConnection::versionReceived()
374{
375}
376
377void SConnection::authSuccess()
378{
379}
380
Pierre Ossman88a94ed2019-04-01 14:22:01 +0200381void SConnection::authFailure(const char* reason)
382{
383 if (state_ != RFBSTATE_SECURITY_FAILURE)
384 throw Exception("SConnection::authFailure: invalid state");
385
386 os->writeU32(secResultFailed);
387 if (!client.beforeVersion(3,8)) // 3.8 onwards have failure message
388 os->writeString(reason);
389 os->flush();
390
391 throw AuthFailureException(reason);
392}
393
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000394void SConnection::queryConnection(const char* userName)
395{
396 approveConnection(true);
397}
398
399void SConnection::approveConnection(bool accept, const char* reason)
400{
401 if (state_ != RFBSTATE_QUERYING)
402 throw Exception("SConnection::approveConnection: invalid state");
403
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200404 if (!client.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000405 if (accept) {
406 os->writeU32(secResultOK);
407 } else {
408 os->writeU32(secResultFailed);
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200409 if (!client.beforeVersion(3,8)) { // 3.8 onwards have failure message
Pierre Ossman19225502017-10-12 15:05:07 +0200410 if (reason)
411 os->writeString(reason);
412 else
413 os->writeString("Authentication failure");
414 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000415 }
416 os->flush();
417 }
418
419 if (accept) {
420 state_ = RFBSTATE_INITIALISATION;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100421 reader_ = new SMsgReader(this, is);
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200422 writer_ = new SMsgWriter(&client, os);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000423 authSuccess();
424 } else {
425 state_ = RFBSTATE_INVALID;
Pierre Ossman19225502017-10-12 15:05:07 +0200426 if (reason)
427 throw AuthFailureException(reason);
428 else
429 throw AuthFailureException();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000430 }
431}
432
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000433void SConnection::clientInit(bool shared)
434{
Pierre Ossman96728352018-06-20 11:35:05 +0200435 writer_->writeServerInit(client.width(), client.height(),
436 client.pf(), client.name());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000437 state_ = RFBSTATE_NORMAL;
438}
439
Pierre Ossman7d64b332018-10-08 15:59:02 +0200440void SConnection::close(const char* reason)
441{
442 state_ = RFBSTATE_CLOSING;
443}
444
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000445void SConnection::setPixelFormat(const PixelFormat& pf)
446{
447 SMsgHandler::setPixelFormat(pf);
448 readyForSetColourMapEntries = true;
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100449 if (!pf.trueColour)
450 writeFakeColourMap();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000451}
452
453void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
454{
455 if (!readyForSetColourMapEntries) {
456 readyForSetColourMapEntries = true;
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200457 if (!client.pf().trueColour) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100458 writeFakeColourMap();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000459 }
460 }
461}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000462
463void SConnection::fence(rdr::U32 flags, unsigned len, const char data[])
464{
465 if (!(flags & fenceFlagRequest))
466 return;
467
468 // We cannot guarantee any synchronisation at this level
469 flags = 0;
470
471 writer()->writeFence(flags, len, data);
472}
Pierre Ossmanc898d9a2011-11-14 16:22:23 +0000473
474void SConnection::enableContinuousUpdates(bool enable,
475 int x, int y, int w, int h)
476{
477}
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100478
Pierre Ossman615d16b2019-05-03 10:53:06 +0200479void SConnection::handleClipboardRequest()
480{
481}
482
483void SConnection::handleClipboardAnnounce(bool available)
484{
485}
486
487void SConnection::handleClipboardData(const char* data)
488{
489}
490
491void SConnection::requestClipboard()
492{
493 if (clientClipboard != NULL) {
494 handleClipboardData(clientClipboard);
495 return;
496 }
Pierre Ossman0ff26552016-02-05 10:26:56 +0100497
498 if (client.supportsEncoding(pseudoEncodingExtendedClipboard) &&
499 (client.clipboardFlags() & rfb::clipboardRequest))
500 writer()->writeClipboardRequest(rfb::clipboardUTF8);
Pierre Ossman615d16b2019-05-03 10:53:06 +0200501}
502
503void SConnection::announceClipboard(bool available)
504{
Pierre Ossman0ff26552016-02-05 10:26:56 +0100505 hasLocalClipboard = available;
506
507 if (client.supportsEncoding(pseudoEncodingExtendedClipboard) &&
508 (client.clipboardFlags() & rfb::clipboardNotify))
509 writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
510 else {
511 if (available)
512 handleClipboardRequest();
513 }
Pierre Ossman615d16b2019-05-03 10:53:06 +0200514}
515
516void SConnection::sendClipboardData(const char* data)
517{
Pierre Ossman0ff26552016-02-05 10:26:56 +0100518 if (client.supportsEncoding(pseudoEncodingExtendedClipboard) &&
519 (client.clipboardFlags() & rfb::clipboardProvide)) {
520 CharArray filtered(convertCRLF(data));
521 size_t sizes[1] = { strlen(filtered.buf) + 1 };
522 const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf };
523 writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data);
524 } else {
525 CharArray latin1(utf8ToLatin1(data));
Pierre Ossman5fbbe102019-05-10 11:44:19 +0200526
Pierre Ossman0ff26552016-02-05 10:26:56 +0100527 writer()->writeServerCutText(latin1.buf);
528 }
Pierre Ossman615d16b2019-05-03 10:53:06 +0200529}
530
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100531void SConnection::writeFakeColourMap(void)
532{
533 int i;
534 rdr::U16 red[256], green[256], blue[256];
535
536 for (i = 0;i < 256;i++)
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200537 client.pf().rgbFromPixel(i, &red[i], &green[i], &blue[i]);
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100538
539 writer()->writeSetColourMapEntries(0, 256, red, green, blue);
540}