blob: ce48b3ecca39378bbc6ec33fd2472fd5693910d9 [file] [log] [blame]
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00001/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19#include <rfb/VNCSConnectionST.h>
20#include <rfb/LogWriter.h>
21#include <rfb/secTypes.h>
22#include <rfb/ServerCore.h>
23#include <rfb/ComparingUpdateTracker.h>
24#define XK_MISCELLANY
25#define XK_XKB_KEYS
26#include <rfb/keysymdef.h>
27
28using namespace rfb;
29
30static LogWriter vlog("VNCSConnST");
31
32VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
33 bool reverse)
34 : sock(s), reverseConnection(reverse), server(server_),
35 image_getter(server->useEconomicTranslate),
36 drawRenderedCursor(false), removeRenderedCursor(false),
37 pointerEventTime(0), accessRights(AccessDefault)
38{
39 setStreams(&sock->inStream(), &sock->outStream());
40 peerEndpoint.buf = sock->getPeerEndpoint();
41 VNCServerST::connectionsLog.write(1,"accepted: %s", peerEndpoint.buf);
42
43 setSocketTimeouts();
44 lastEventTime = time(0);
45
46 // Initialise security
47 CharArray sec_types_str;
48 if (reverseConnection)
49 sec_types_str.buf = rfb::Server::rev_sec_types.getData();
50 else
51 sec_types_str.buf = rfb::Server::sec_types.getData();
52 std::list<int> sec_types = parseSecTypes(sec_types_str.buf);
53 std::list<int>::iterator i;
54 for (i=sec_types.begin(); i!=sec_types.end(); i++) {
55 addSecType(*i);
56 }
57
58 server->clients.push_front(this);
59}
60
61
62VNCSConnectionST::~VNCSConnectionST()
63{
64 // If we reach here then VNCServerST is deleting us!
65 VNCServerST::connectionsLog.write(1,"closed: %s (%s)",
66 peerEndpoint.buf, closeReason.buf);
67
68 // Release any keys the client still had pressed
69 std::set<rdr::U32>::iterator i;
70 for (i=pressedKeys.begin(); i!=pressedKeys.end(); i++)
71 server->desktop->keyEvent(*i, false);
72 if (server->pointerClient == this)
73 server->pointerClient = 0;
74
75 // Remove this client from the server
76 server->clients.remove(this);
77}
78
79
80// Methods called from VNCServerST
81
82bool VNCSConnectionST::init()
83{
84 try {
85 initialiseProtocol();
86 } catch (rdr::Exception& e) {
87 close(e.str());
88 return false;
89 }
90 return true;
91}
92
93void VNCSConnectionST::close(const char* reason)
94{
95 // Log the reason for the close
96 if (!closeReason.buf)
97 closeReason.buf = strDup(reason);
98 else
99 vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
100
Peter Åstrand43aa1a12005-02-21 09:58:31 +0000101 if (authenticated()) {
102 server->lastDisconnectTime = time(0);
103 }
104
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000105 // Just shutdown the socket. This will cause processMessages to
106 // eventually fail, causing us and our socket to be deleted.
107 sock->shutdown();
108 setState(RFBSTATE_CLOSING);
109}
110
111
112bool VNCSConnectionST::processMessages()
113{
114 if (state() == RFBSTATE_CLOSING) return false;
115 try {
116 // - Now set appropriate socket timeouts and process data
117 setSocketTimeouts();
118 bool clientsReadyBefore = server->clientsReadyForUpdate();
119
120 while (getInStream()->checkNoWait(1)) {
121 processMsg();
122 }
123
124 if (!clientsReadyBefore && !requested.is_empty())
125 server->desktop->framebufferUpdateRequest();
126
127 return true;
128
129 } catch (rdr::EndOfStream&) {
130 close("Clean disconnection");
131 } catch (rdr::Exception &e) {
132 close(e.str());
133 }
134 return false;
135}
136
137void VNCSConnectionST::writeFramebufferUpdateOrClose()
138{
139 try {
140 writeFramebufferUpdate();
141 } catch(rdr::Exception &e) {
142 close(e.str());
143 }
144}
145
146void VNCSConnectionST::pixelBufferChange()
147{
148 try {
149 if (!authenticated()) return;
150 if (cp.width && cp.height && (server->pb->width() != cp.width ||
151 server->pb->height() != cp.height))
152 {
153 // We need to clip the next update to the new size, but also add any
154 // extra bits if it's bigger. If we wanted to do this exactly, something
155 // like the code below would do it, but at the moment we just update the
156 // entire new size. However, we do need to clip the renderedCursorRect
157 // because that might be added to updates in writeFramebufferUpdate().
158
159 //updates.intersect(server->pb->getRect());
160 //
161 //if (server->pb->width() > cp.width)
162 // updates.add_changed(Rect(cp.width, 0, server->pb->width(),
163 // server->pb->height()));
164 //if (server->pb->height() > cp.height)
165 // updates.add_changed(Rect(0, cp.height, cp.width,
166 // server->pb->height()));
167
168 renderedCursorRect = renderedCursorRect.intersect(server->pb->getRect());
169
170 cp.width = server->pb->width();
171 cp.height = server->pb->height();
172 if (!writer()->writeSetDesktopSize()) {
173 close("Client does not support desktop resize");
174 return;
175 }
176 }
177 // Just update the whole screen at the moment because we're too lazy to
178 // work out what's actually changed.
179 updates.clear();
180 updates.add_changed(server->pb->getRect());
181 vlog.debug("pixel buffer changed - re-initialising image getter");
182 image_getter.init(server->pb, cp.pf(), writer());
183 if (writer()->needFakeUpdate())
184 writeFramebufferUpdate();
185 } catch(rdr::Exception &e) {
186 close(e.str());
187 }
188}
189
190void VNCSConnectionST::setColourMapEntriesOrClose(int firstColour,int nColours)
191{
192 try {
193 setColourMapEntries(firstColour, nColours);
194 } catch(rdr::Exception& e) {
195 close(e.str());
196 }
197}
198
199void VNCSConnectionST::bell()
200{
201 try {
202 if (state() == RFBSTATE_NORMAL) writer()->writeBell();
203 } catch(rdr::Exception& e) {
204 close(e.str());
205 }
206}
207
208void VNCSConnectionST::serverCutText(const char *str, int len)
209{
210 try {
211 if (!(accessRights & AccessCutText)) return;
212 if (!rfb::Server::sendCutText) return;
213 if (state() == RFBSTATE_NORMAL)
214 writer()->writeServerCutText(str, len);
215 } catch(rdr::Exception& e) {
216 close(e.str());
217 }
218}
219
220void VNCSConnectionST::setCursorOrClose()
221{
222 try {
223 setCursor();
224 } catch(rdr::Exception& e) {
225 close(e.str());
226 }
227}
228
229
230int VNCSConnectionST::checkIdleTimeout()
231{
232 int idleTimeout = rfb::Server::idleTimeout;
233 if (idleTimeout == 0) return 0;
234 if (state() != RFBSTATE_NORMAL && idleTimeout < 15)
235 idleTimeout = 15; // minimum of 15 seconds while authenticating
236 time_t now = time(0);
237 if (now < lastEventTime) {
238 // Someone must have set the time backwards. Set lastEventTime so that the
239 // idleTimeout will count from now.
240 vlog.info("Time has gone backwards - resetting idle timeout");
241 lastEventTime = now;
242 }
243 int timeLeft = lastEventTime + idleTimeout - now;
244 if (timeLeft < -60) {
245 // Our callback is over a minute late - someone must have set the time
246 // forwards. Set lastEventTime so that the idleTimeout will count from
247 // now.
248 vlog.info("Time has gone forwards - resetting idle timeout");
249 lastEventTime = now;
250 return idleTimeout;
251 }
252 if (timeLeft <= 0) {
253 close("Idle timeout");
254 return 0;
255 }
256 return timeLeft * 1000;
257}
258
259// renderedCursorChange() is called whenever the server-side rendered cursor
260// changes shape or position. It ensures that the next update will clean up
261// the old rendered cursor and if necessary draw the new rendered cursor.
262
263void VNCSConnectionST::renderedCursorChange()
264{
265 if (state() != RFBSTATE_NORMAL) return;
266 removeRenderedCursor = true;
267 if (needRenderedCursor())
268 drawRenderedCursor = true;
269}
270
271// needRenderedCursor() returns true if this client needs the server-side
272// rendered cursor. This may be because it does not support local cursor or
273// because the current cursor position has not been set by this client.
274// Unfortunately we can't know for sure when the current cursor position has
275// been set by this client. We guess that this is the case when the current
276// cursor position is the same as the last pointer event from this client, or
277// if it is a very short time since this client's last pointer event (up to a
278// second). [ Ideally we should do finer-grained timing here and make the time
279// configurable, but I don't think it's that important. ]
280
281bool VNCSConnectionST::needRenderedCursor()
282{
283 return (state() == RFBSTATE_NORMAL
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000284 && (!cp.supportsLocalCursor && !cp.supportsLocalXCursor
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000285 || (!server->cursorPos.equals(pointerEventPos) &&
286 (time(0) - pointerEventTime) > 0)));
287}
288
289
290void VNCSConnectionST::approveConnectionOrClose(bool accept,
291 const char* reason)
292{
293 try {
294 approveConnection(accept, reason);
295 } catch (rdr::Exception& e) {
296 close(e.str());
297 }
298}
299
300
301
302// -=- Callbacks from SConnection
303
304void VNCSConnectionST::versionReceived() {
305 CharArray address(sock->getPeerAddress());
306 if ((rfb::Server::blacklistLevel == 1) &&
307 server->blHosts->isBlackmarked(address.buf)) {
308 server->connectionsLog.error("blacklisted: %s", address.buf);
309 throwConnFailedException("Too many security failures");
310 }
311}
312
313SSecurity* VNCSConnectionST::getSSecurity(int secType) {
314 if (!server->securityFactory)
315 throw rdr::Exception("no SSecurityFactory registered!");
316 return server->securityFactory->getSSecurity(secType, reverseConnection);
317}
318
319void VNCSConnectionST::authSuccess()
320{
321 lastEventTime = time(0);
322
323 // - Authentication succeeded - clear from blacklist
324 CharArray name; name.buf = sock->getPeerAddress();
325 server->blHosts->clearBlackmark(name.buf);
326
327 server->startDesktop();
328
329 // - Set the connection parameters appropriately
330 cp.width = server->pb->width();
331 cp.height = server->pb->height();
332 cp.setName(server->getName());
333
334 // - Set the default pixel format
335 cp.setPF(server->pb->getPF());
336 char buffer[256];
337 cp.pf().print(buffer, 256);
338 vlog.info("Server default pixel format %s", buffer);
339 image_getter.init(server->pb, cp.pf(), 0);
340
341 // - Mark the entire display as "dirty"
342 updates.add_changed(server->pb->getRect());
343}
344
345void VNCSConnectionST::queryConnection(const char* userName)
346{
347 // - Does the client have the right to bypass the query?
348 if (reverseConnection || !rfb::Server::queryConnect ||
349 (accessRights & AccessNoQuery))
350 {
351 approveConnection(true);
352 return;
353 }
354
355 CharArray reason;
356 VNCServerST::queryResult qr = server->queryConnection(sock, userName,
357 &reason.buf);
358 if (qr == VNCServerST::PENDING) return;
359 approveConnection(qr == VNCServerST::ACCEPT, reason.buf);
360}
361
362void VNCSConnectionST::clientInit(bool shared)
363{
364 lastEventTime = time(0);
365 if (rfb::Server::alwaysShared || reverseConnection) shared = true;
366 if (rfb::Server::neverShared) shared = false;
367 if (!shared) {
368 if (rfb::Server::disconnectClients) {
369 // - Close all the other connected clients
370 vlog.debug("non-shared connection - closing clients");
371 server->closeClients("Non-shared connection requested", getSock());
372 } else {
373 // - Refuse this connection if there are existing clients, in addition to this one
374 if (server->authClientCount() > 1) {
375 close("Server is already in use");
376 return;
377 }
378 }
379 }
380 SConnection::clientInit(shared);
381}
382
383void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
384{
385 SConnection::setPixelFormat(pf);
386 char buffer[256];
387 pf.print(buffer, 256);
388 vlog.info("Client pixel format %s", buffer);
389 image_getter.init(server->pb, pf, writer());
390 setCursor();
391}
392
393void VNCSConnectionST::pointerEvent(int x, int y, int buttonMask)
394{
395 pointerEventTime = lastEventTime = time(0);
Peter Åstrand43aa1a12005-02-21 09:58:31 +0000396 server->lastUserInputTime = lastEventTime;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000397 if (!(accessRights & AccessPtrEvents)) return;
398 if (!rfb::Server::acceptPointerEvents) return;
399 if (!server->pointerClient || server->pointerClient == this) {
400 pointerEventPos = Point(x, y);
401 if (buttonMask)
402 server->pointerClient = this;
403 else
404 server->pointerClient = 0;
405 server->desktop->pointerEvent(pointerEventPos, buttonMask);
406 }
407}
408
409
410class VNCSConnectionSTShiftPresser {
411public:
412 VNCSConnectionSTShiftPresser(SDesktop* desktop_)
413 : desktop(desktop_), pressed(false) {}
414 ~VNCSConnectionSTShiftPresser() {
415 if (pressed) { desktop->keyEvent(XK_Shift_L, false); }
416 }
417 void press() {
418 desktop->keyEvent(XK_Shift_L, true);
419 pressed = true;
420 }
421 SDesktop* desktop;
422 bool pressed;
423};
424
425// keyEvent() - record in the pressedKeys which keys were pressed. Allow
426// multiple down events (for autorepeat), but only allow a single up event.
427void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) {
428 lastEventTime = time(0);
Peter Åstrand43aa1a12005-02-21 09:58:31 +0000429 server->lastUserInputTime = lastEventTime;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000430 if (!(accessRights & AccessKeyEvents)) return;
431 if (!rfb::Server::acceptKeyEvents) return;
432
433 // Turn ISO_Left_Tab into shifted Tab.
434 VNCSConnectionSTShiftPresser shiftPresser(server->desktop);
435 if (key == XK_ISO_Left_Tab) {
436 if (pressedKeys.find(XK_Shift_L) == pressedKeys.end() &&
437 pressedKeys.find(XK_Shift_R) == pressedKeys.end())
438 shiftPresser.press();
439 key = XK_Tab;
440 }
441
442 if (down) {
443 pressedKeys.insert(key);
444 } else {
445 if (!pressedKeys.erase(key)) return;
446 }
447 server->desktop->keyEvent(key, down);
448}
449
450void VNCSConnectionST::clientCutText(const char* str, int len)
451{
452 if (!(accessRights & AccessCutText)) return;
453 if (!rfb::Server::acceptCutText) return;
454 server->desktop->clientCutText(str, len);
455}
456
457void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
458{
459 if (!(accessRights & AccessView)) return;
460
461 SConnection::framebufferUpdateRequest(r, incremental);
462
463 Region reqRgn(r);
464 requested.assign_union(reqRgn);
465
466 if (!incremental) {
467 // Non-incremental update - treat as if area requested has changed
468 updates.add_changed(reqRgn);
469 server->comparer->add_changed(reqRgn);
470 }
471
472 writeFramebufferUpdate();
473}
474
475void VNCSConnectionST::setInitialColourMap()
476{
477 setColourMapEntries(0, 0);
478}
479
480// supportsLocalCursor() is called whenever the status of
481// cp.supportsLocalCursor has changed. If the client does now support local
482// cursor, we make sure that the old server-side rendered cursor is cleaned up
483// and the cursor is sent to the client.
484
485void VNCSConnectionST::supportsLocalCursor()
486{
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000487 if (cp.supportsLocalCursor || cp.supportsLocalXCursor) {
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000488 removeRenderedCursor = true;
489 drawRenderedCursor = false;
490 setCursor();
491 }
492}
493
494void VNCSConnectionST::writeSetCursorCallback()
495{
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000496 if (cp.supportsLocalXCursor) {
497 Pixel pix0, pix1;
498 rdr::U8Array bitmap(server->cursor.getBitmap(&pix0, &pix1));
499 if (bitmap.buf) {
500 // The client supports XCursor and the cursor only has two
501 // colors. Use the XCursor encoding.
502 writer()->writeSetXCursor(server->cursor.width(),
503 server->cursor.height(),
504 server->cursor.hotspot.x,
505 server->cursor.hotspot.y,
506 bitmap.buf, server->cursor.mask.buf);
507 return;
508 } else {
509 // More than two colors
510 if (!cp.supportsLocalCursor) {
511 // FIXME: We could reduce to two colors.
512 vlog.info("Unable to send multicolor cursor: RichCursor not supported by client");
513 return;
514 }
515 }
516 }
517
518 // Use RichCursor
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000519 rdr::U8* transData = writer()->getImageBuf(server->cursor.area());
520 image_getter.translatePixels(server->cursor.data, transData,
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000521 server->cursor.area());
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000522 writer()->writeSetCursor(server->cursor.width(),
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000523 server->cursor.height(),
524 server->cursor.hotspot.x,
525 server->cursor.hotspot.y,
526 transData, server->cursor.mask.buf);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000527}
528
529
530void VNCSConnectionST::writeFramebufferUpdate()
531{
532 if (state() != RFBSTATE_NORMAL || requested.is_empty()) return;
533
534 server->checkUpdate();
535
536 // If the previous position of the rendered cursor overlaps the source of the
537 // copy, then when the copy happens the corresponding rectangle in the
538 // destination will be wrong, so add it to the changed region.
539
540 if (!updates.get_copied().is_empty() && !renderedCursorRect.is_empty()) {
541 Rect bogusCopiedCursor = (renderedCursorRect.translate(updates.get_delta())
542 .intersect(server->pb->getRect()));
543 if (!updates.get_copied().intersect(bogusCopiedCursor).is_empty()) {
544 updates.add_changed(bogusCopiedCursor);
545 }
546 }
547
548 // If we need to remove the old rendered cursor, just add the rectangle to
549 // the changed region.
550
551 if (removeRenderedCursor) {
552 updates.add_changed(renderedCursorRect);
553 renderedCursorRect.clear();
554 removeRenderedCursor = false;
555 }
556
557 // Return if there is nothing to send the client.
558
559 if (updates.is_empty() && !writer()->needFakeUpdate() && !drawRenderedCursor)
560 return;
561
562 // If the client needs a server-side rendered cursor, work out the cursor
563 // rectangle. If it's empty then don't bother drawing it, but if it overlaps
564 // with the update region, we need to draw the rendered cursor regardless of
565 // whether it has changed.
566
567 if (needRenderedCursor()) {
568 renderedCursorRect
569 = (server->renderedCursor.getRect(server->renderedCursorTL)
570 .intersect(requested.get_bounding_rect()));
571
572 if (renderedCursorRect.is_empty()) {
573 drawRenderedCursor = false;
574 } else if (!updates.get_changed().union_(updates.get_copied())
575 .intersect(renderedCursorRect).is_empty()) {
576 drawRenderedCursor = true;
577 }
578
579 // We could remove the new cursor rect from updates here. It's not clear
580 // whether this is worth it. If we do remove it, then we won't draw over
581 // the same bit of screen twice, but we have the overhead of a more complex
582 // region.
583
584 //if (drawRenderedCursor)
585 // updates.subtract(renderedCursorRect);
586 }
587
588 UpdateInfo update;
589 updates.enable_copyrect(cp.useCopyRect);
590 updates.get_update(&update, requested);
591 if (!update.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {
Peter Åstrand016dc192005-02-10 16:41:06 +0000592 // Compute the number of rectangles. Tight encoder makes the things more
593 // complicated as compared to the original RealVNC.
594 writer()->setupCurrentEncoder();
595 int nRects = update.copied.numRects() + (drawRenderedCursor ? 1 : 0);
596 std::vector<Rect> rects;
597 std::vector<Rect>::const_iterator i;
598 update.changed.get_rects(&rects);
599 for (i = rects.begin(); i != rects.end(); i++) {
600 if (i->width() && i->height())
601 nRects += writer()->getNumRects(*i);
602 }
603
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000604 writer()->writeFramebufferUpdateStart(nRects);
605 Region updatedRegion;
606 writer()->writeRects(update, &image_getter, &updatedRegion);
607 updates.subtract(updatedRegion);
608 if (drawRenderedCursor)
609 writeRenderedCursorRect();
610 writer()->writeFramebufferUpdateEnd();
611 requested.clear();
612 }
613}
614
615
616// writeRenderedCursorRect() writes a single rectangle drawing the rendered
617// cursor on the client.
618
619void VNCSConnectionST::writeRenderedCursorRect()
620{
621 image_getter.setPixelBuffer(&server->renderedCursor);
622 image_getter.setOffset(server->renderedCursorTL);
623
624 Rect actual;
625 writer()->writeRect(renderedCursorRect, &image_getter, &actual);
626
627 image_getter.setPixelBuffer(server->pb);
628 image_getter.setOffset(Point(0,0));
629
630 drawRenderedCursor = false;
631}
632
633void VNCSConnectionST::setColourMapEntries(int firstColour, int nColours)
634{
635 if (!readyForSetColourMapEntries) return;
636 if (server->pb->getPF().trueColour) return;
637
638 image_getter.setColourMapEntries(firstColour, nColours, writer());
639
640 if (cp.pf().trueColour) {
641 updates.add_changed(server->pb->getRect());
642 }
643}
644
645
646// setCursor() is called whenever the cursor has changed shape or pixel format.
647// If the client supports local cursor then it will arrange for the cursor to
648// be sent to the client.
649
650void VNCSConnectionST::setCursor()
651{
652 if (state() != RFBSTATE_NORMAL || !cp.supportsLocalCursor) return;
653 writer()->cursorChange(this);
654 if (writer()->needFakeUpdate())
655 writeFramebufferUpdate();
656}
657
658void VNCSConnectionST::setSocketTimeouts()
659{
660 int timeoutms = rfb::Server::clientWaitTimeMillis;
661 if (timeoutms == 0 || timeoutms > rfb::Server::idleTimeout * 1000) {
662 timeoutms = rfb::Server::idleTimeout * 1000;
663 if (timeoutms == 0)
664 timeoutms = -1;
665 }
666 sock->inStream().setTimeout(timeoutms);
667 sock->outStream().setTimeout(timeoutms);
668}