1 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
2  * Copyright 2009-2019 Pierre Ossman for Cendio AB
3  *
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 
20 // -=- Single-Threaded VNC Server implementation
21 
22 
23 // Note about how sockets get closed:
24 //
25 // Closing sockets to clients is non-trivial because the code which calls
26 // VNCServerST must explicitly know about all the sockets (so that it can block
27 // on them appropriately).  However, VNCServerST may want to close clients for
28 // a number of reasons, and from a variety of entry points.  The simplest is
29 // when processSocketEvent() is called for a client, and the remote end has
30 // closed its socket.  A more complex reason is when processSocketEvent() is
31 // called for a client which has just sent a ClientInit with the shared flag
32 // set to false - in this case we want to close all other clients.  Yet another
33 // reason for disconnecting clients is when the desktop size has changed as a
34 // result of a call to setPixelBuffer().
35 //
36 // The responsibility for creating and deleting sockets is entirely with the
37 // calling code.  When VNCServerST wants to close a connection to a client it
38 // calls the VNCSConnectionST's close() method which calls shutdown() on the
39 // socket.  Eventually the calling code will notice that the socket has been
40 // shut down and call removeSocket() so that we can delete the
41 // VNCSConnectionST.  Note that the socket must not be deleted by the calling
42 // code until after removeSocket() has been called.
43 //
44 // One minor complication is that we don't allocate a VNCSConnectionST object
45 // for a blacklisted host (since we want to minimise the resources used for
46 // dealing with such a connection).  In order to properly implement the
47 // getSockets function, we must maintain a separate closingSockets list,
48 // otherwise blacklisted connections might be "forgotten".
49 
50 
51 #include <assert.h>
52 #include <stdlib.h>
53 
54 #include <rfb/ComparingUpdateTracker.h>
55 #include <rfb/KeyRemapper.h>
56 #include <rfb/LogWriter.h>
57 #include <rfb/Security.h>
58 #include <rfb/ServerCore.h>
59 #include <rfb/VNCServerST.h>
60 #include <rfb/VNCSConnectionST.h>
61 #include <rfb/util.h>
62 #include <rfb/ledStates.h>
63 
64 #include <rdr/types.h>
65 
66 using namespace rfb;
67 
68 static LogWriter slog("VNCServerST");
69 static LogWriter connectionsLog("Connections");
70 
71 //
72 // -=- VNCServerST Implementation
73 //
74 
75 // -=- Constructors/Destructor
76 
VNCServerST(const char * name_,SDesktop * desktop_)77 VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
78   : blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
79     blockCounter(0), pb(0), ledState(ledUnknown),
80     name(strDup(name_)), pointerClient(0), clipboardClient(0),
81     comparer(0), cursor(new Cursor(0, 0, Point(), NULL)),
82     renderedCursorInvalid(false),
83     keyRemapper(&KeyRemapper::defInstance),
84     idleTimer(this), disconnectTimer(this), connectTimer(this),
85     frameTimer(this)
86 {
87   slog.debug("creating single-threaded server %s", name.buf);
88 
89   // FIXME: Do we really want to kick off these right away?
90   if (rfb::Server::maxIdleTime)
91     idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
92   if (rfb::Server::maxDisconnectionTime)
93     disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime));
94 }
95 
~VNCServerST()96 VNCServerST::~VNCServerST()
97 {
98   slog.debug("shutting down server %s", name.buf);
99 
100   // Close any active clients, with appropriate logging & cleanup
101   closeClients("Server shutdown");
102 
103   // Stop trying to render things
104   stopFrameClock();
105 
106   // Delete all the clients, and their sockets, and any closing sockets
107   while (!clients.empty()) {
108     VNCSConnectionST* client;
109     client = clients.front();
110     clients.pop_front();
111     delete client;
112   }
113 
114   // Stop the desktop object if active, *only* after deleting all clients!
115   stopDesktop();
116 
117   if (comparer)
118     comparer->logStats();
119   delete comparer;
120 
121   delete cursor;
122 }
123 
124 
125 // SocketServer methods
126 
addSocket(network::Socket * sock,bool outgoing)127 void VNCServerST::addSocket(network::Socket* sock, bool outgoing)
128 {
129   // - Check the connection isn't black-marked
130   // *** do this in getSecurity instead?
131   CharArray address(sock->getPeerAddress());
132   if (blHosts->isBlackmarked(address.buf)) {
133     connectionsLog.error("blacklisted: %s", address.buf);
134     try {
135       rdr::OutStream& os = sock->outStream();
136 
137       // Shortest possible way to tell a client it is not welcome
138       os.writeBytes("RFB 003.003\n", 12);
139       os.writeU32(0);
140       const char* reason = "Too many security failures";
141       os.writeU32(strlen(reason));
142       os.writeBytes(reason, strlen(reason));
143       os.flush();
144     } catch (rdr::Exception&) {
145     }
146     sock->shutdown();
147     closingSockets.push_back(sock);
148     return;
149   }
150 
151   CharArray name;
152   name.buf = sock->getPeerEndpoint();
153   connectionsLog.status("accepted: %s", name.buf);
154 
155   // Adjust the exit timers
156   if (rfb::Server::maxConnectionTime && clients.empty())
157     connectTimer.start(secsToMillis(rfb::Server::maxConnectionTime));
158   disconnectTimer.stop();
159 
160   VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing);
161   clients.push_front(client);
162   client->init();
163 }
164 
removeSocket(network::Socket * sock)165 void VNCServerST::removeSocket(network::Socket* sock) {
166   // - If the socket has resources allocated to it, delete them
167   std::list<VNCSConnectionST*>::iterator ci;
168   for (ci = clients.begin(); ci != clients.end(); ci++) {
169     if ((*ci)->getSock() == sock) {
170       // - Remove any references to it
171       if (pointerClient == *ci)
172         pointerClient = NULL;
173       if (clipboardClient == *ci)
174         handleClipboardAnnounce(*ci, false);
175       clipboardRequestors.remove(*ci);
176 
177       CharArray name(strDup((*ci)->getPeerEndpoint()));
178 
179       // - Delete the per-Socket resources
180       delete *ci;
181 
182       clients.remove(*ci);
183 
184       connectionsLog.status("closed: %s", name.buf);
185 
186       // - Check that the desktop object is still required
187       if (authClientCount() == 0)
188         stopDesktop();
189 
190       if (comparer)
191         comparer->logStats();
192 
193       // Adjust the exit timers
194       connectTimer.stop();
195       if (rfb::Server::maxDisconnectionTime && clients.empty())
196         disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime));
197 
198       return;
199     }
200   }
201 
202   // - If the Socket has no resources, it may have been a closingSocket
203   closingSockets.remove(sock);
204 }
205 
processSocketReadEvent(network::Socket * sock)206 void VNCServerST::processSocketReadEvent(network::Socket* sock)
207 {
208   // - Find the appropriate VNCSConnectionST and process the event
209   std::list<VNCSConnectionST*>::iterator ci;
210   for (ci = clients.begin(); ci != clients.end(); ci++) {
211     if ((*ci)->getSock() == sock) {
212       (*ci)->processMessages();
213       return;
214     }
215   }
216   throw rdr::Exception("invalid Socket in VNCServerST");
217 }
218 
processSocketWriteEvent(network::Socket * sock)219 void VNCServerST::processSocketWriteEvent(network::Socket* sock)
220 {
221   // - Find the appropriate VNCSConnectionST and process the event
222   std::list<VNCSConnectionST*>::iterator ci;
223   for (ci = clients.begin(); ci != clients.end(); ci++) {
224     if ((*ci)->getSock() == sock) {
225       (*ci)->flushSocket();
226       return;
227     }
228   }
229   throw rdr::Exception("invalid Socket in VNCServerST");
230 }
231 
232 // VNCServer methods
233 
blockUpdates()234 void VNCServerST::blockUpdates()
235 {
236   blockCounter++;
237 
238   stopFrameClock();
239 }
240 
unblockUpdates()241 void VNCServerST::unblockUpdates()
242 {
243   assert(blockCounter > 0);
244 
245   blockCounter--;
246 
247   // Restart the frame clock if we have updates
248   if (blockCounter == 0) {
249     if (!comparer->is_empty())
250       startFrameClock();
251   }
252 }
253 
setPixelBuffer(PixelBuffer * pb_,const ScreenSet & layout)254 void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
255 {
256   if (comparer)
257     comparer->logStats();
258 
259   pb = pb_;
260   delete comparer;
261   comparer = 0;
262 
263   if (!pb) {
264     screenLayout = ScreenSet();
265 
266     if (desktopStarted)
267       throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
268 
269     return;
270   }
271 
272   if (!layout.validate(pb->width(), pb->height()))
273     throw Exception("setPixelBuffer: invalid screen layout");
274 
275   screenLayout = layout;
276 
277   // Assume the framebuffer contents wasn't saved and reset everything
278   // that tracks its contents
279   comparer = new ComparingUpdateTracker(pb);
280   renderedCursorInvalid = true;
281   add_changed(pb->getRect());
282 
283   std::list<VNCSConnectionST*>::iterator ci, ci_next;
284   for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
285     ci_next = ci; ci_next++;
286     (*ci)->pixelBufferChange();
287     // Since the new pixel buffer means an ExtendedDesktopSize needs to
288     // be sent anyway, we don't need to call screenLayoutChange.
289   }
290 }
291 
setPixelBuffer(PixelBuffer * pb_)292 void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
293 {
294   ScreenSet layout = screenLayout;
295 
296   // Check that the screen layout is still valid
297   if (pb_ && !layout.validate(pb_->width(), pb_->height())) {
298     Rect fbRect;
299     ScreenSet::iterator iter, iter_next;
300 
301     fbRect.setXYWH(0, 0, pb_->width(), pb_->height());
302 
303     for (iter = layout.begin();iter != layout.end();iter = iter_next) {
304       iter_next = iter; ++iter_next;
305       if (iter->dimensions.enclosed_by(fbRect))
306           continue;
307       iter->dimensions = iter->dimensions.intersect(fbRect);
308       if (iter->dimensions.is_empty()) {
309         slog.info("Removing screen %d (%x) as it is completely outside the new framebuffer",
310                   (int)iter->id, (unsigned)iter->id);
311         layout.remove_screen(iter->id);
312       }
313     }
314   }
315 
316   // Make sure that we have at least one screen
317   if (layout.num_screens() == 0)
318     layout.add_screen(Screen(0, 0, 0, pb_->width(), pb_->height(), 0));
319 
320   setPixelBuffer(pb_, layout);
321 }
322 
setScreenLayout(const ScreenSet & layout)323 void VNCServerST::setScreenLayout(const ScreenSet& layout)
324 {
325   if (!pb)
326     throw Exception("setScreenLayout: new screen layout without a PixelBuffer");
327   if (!layout.validate(pb->width(), pb->height()))
328     throw Exception("setScreenLayout: invalid screen layout");
329 
330   screenLayout = layout;
331 
332   std::list<VNCSConnectionST*>::iterator ci, ci_next;
333   for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
334     ci_next = ci; ci_next++;
335     (*ci)->screenLayoutChangeOrClose(reasonServer);
336   }
337 }
338 
requestClipboard()339 void VNCServerST::requestClipboard()
340 {
341   if (clipboardClient == NULL) {
342     slog.debug("Got request for client clipboard but no client currently owns the clipboard");
343     return;
344   }
345 
346   clipboardClient->requestClipboardOrClose();
347 }
348 
announceClipboard(bool available)349 void VNCServerST::announceClipboard(bool available)
350 {
351   std::list<VNCSConnectionST*>::iterator ci, ci_next;
352 
353   clipboardRequestors.clear();
354 
355   for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
356     ci_next = ci; ci_next++;
357     (*ci)->announceClipboardOrClose(available);
358   }
359 }
360 
sendClipboardData(const char * data)361 void VNCServerST::sendClipboardData(const char* data)
362 {
363   std::list<VNCSConnectionST*>::iterator ci, ci_next;
364 
365   if (strchr(data, '\r') != NULL)
366     throw Exception("Invalid carriage return in clipboard data");
367 
368   for (ci = clipboardRequestors.begin();
369        ci != clipboardRequestors.end(); ci = ci_next) {
370     ci_next = ci; ci_next++;
371     (*ci)->sendClipboardDataOrClose(data);
372   }
373 
374   clipboardRequestors.clear();
375 }
376 
bell()377 void VNCServerST::bell()
378 {
379   std::list<VNCSConnectionST*>::iterator ci, ci_next;
380   for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
381     ci_next = ci; ci_next++;
382     (*ci)->bellOrClose();
383   }
384 }
385 
setName(const char * name_)386 void VNCServerST::setName(const char* name_)
387 {
388   name.replaceBuf(strDup(name_));
389   std::list<VNCSConnectionST*>::iterator ci, ci_next;
390   for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
391     ci_next = ci; ci_next++;
392     (*ci)->setDesktopNameOrClose(name_);
393   }
394 }
395 
add_changed(const Region & region)396 void VNCServerST::add_changed(const Region& region)
397 {
398   if (comparer == NULL)
399     return;
400 
401   comparer->add_changed(region);
402   startFrameClock();
403 }
404 
add_copied(const Region & dest,const Point & delta)405 void VNCServerST::add_copied(const Region& dest, const Point& delta)
406 {
407   if (comparer == NULL)
408     return;
409 
410   comparer->add_copied(dest, delta);
411   startFrameClock();
412 }
413 
setCursor(int width,int height,const Point & newHotspot,const rdr::U8 * data)414 void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
415                             const rdr::U8* data)
416 {
417   delete cursor;
418   cursor = new Cursor(width, height, newHotspot, data);
419   cursor->crop();
420 
421   renderedCursorInvalid = true;
422 
423   std::list<VNCSConnectionST*>::iterator ci, ci_next;
424   for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
425     ci_next = ci; ci_next++;
426     (*ci)->renderedCursorChange();
427     (*ci)->setCursorOrClose();
428   }
429 }
430 
setCursorPos(const Point & pos,bool warped)431 void VNCServerST::setCursorPos(const Point& pos, bool warped)
432 {
433   if (!cursorPos.equals(pos)) {
434     cursorPos = pos;
435     renderedCursorInvalid = true;
436     std::list<VNCSConnectionST*>::iterator ci;
437     for (ci = clients.begin(); ci != clients.end(); ci++) {
438       (*ci)->renderedCursorChange();
439       if (warped)
440         (*ci)->cursorPositionChange();
441     }
442   }
443 }
444 
setLEDState(unsigned int state)445 void VNCServerST::setLEDState(unsigned int state)
446 {
447   std::list<VNCSConnectionST*>::iterator ci, ci_next;
448 
449   if (state == ledState)
450     return;
451 
452   ledState = state;
453 
454   for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
455     ci_next = ci; ci_next++;
456     (*ci)->setLEDStateOrClose(state);
457   }
458 }
459 
460 // Event handlers
461 
keyEvent(rdr::U32 keysym,rdr::U32 keycode,bool down)462 void VNCServerST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
463 {
464   if (rfb::Server::maxIdleTime)
465     idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
466 
467   // Remap the key if required
468   if (keyRemapper) {
469     rdr::U32 newkey;
470     newkey = keyRemapper->remapKey(keysym);
471     if (newkey != keysym) {
472       slog.debug("Key remapped to 0x%x", newkey);
473       keysym = newkey;
474     }
475   }
476 
477   desktop->keyEvent(keysym, keycode, down);
478 }
479 
pointerEvent(VNCSConnectionST * client,const Point & pos,int buttonMask)480 void VNCServerST::pointerEvent(VNCSConnectionST* client,
481                                const Point& pos, int buttonMask)
482 {
483   if (rfb::Server::maxIdleTime)
484     idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
485 
486   // Let one client own the cursor whilst buttons are pressed in order
487   // to provide a bit more sane user experience
488   if ((pointerClient != NULL) && (pointerClient != client))
489     return;
490 
491   if (buttonMask)
492     pointerClient = client;
493   else
494     pointerClient = NULL;
495 
496   desktop->pointerEvent(pos, buttonMask);
497 }
498 
handleClipboardRequest(VNCSConnectionST * client)499 void VNCServerST::handleClipboardRequest(VNCSConnectionST* client)
500 {
501   clipboardRequestors.push_back(client);
502   if (clipboardRequestors.size() == 1)
503     desktop->handleClipboardRequest();
504 }
505 
handleClipboardAnnounce(VNCSConnectionST * client,bool available)506 void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
507                                           bool available)
508 {
509   if (available)
510     clipboardClient = client;
511   else {
512     if (client != clipboardClient)
513       return;
514     clipboardClient = NULL;
515   }
516   desktop->handleClipboardAnnounce(available);
517 }
518 
handleClipboardData(VNCSConnectionST * client,const char * data)519 void VNCServerST::handleClipboardData(VNCSConnectionST* client,
520                                       const char* data)
521 {
522   if (client != clipboardClient) {
523     slog.debug("Ignoring unexpected clipboard data");
524     return;
525   }
526   desktop->handleClipboardData(data);
527 }
528 
setDesktopSize(VNCSConnectionST * requester,int fb_width,int fb_height,const ScreenSet & layout)529 unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester,
530                                          int fb_width, int fb_height,
531                                          const ScreenSet& layout)
532 {
533   unsigned int result;
534   std::list<VNCSConnectionST*>::iterator ci, ci_next;
535 
536   // We can't handle a framebuffer larger than this, so don't let a
537   // client set one (see PixelBuffer.cxx)
538   if ((fb_width > 16384) || (fb_height > 16384))
539     return resultProhibited;
540 
541   // Don't bother the desktop with an invalid configuration
542   if (!layout.validate(fb_width, fb_height))
543     return resultInvalid;
544 
545   // FIXME: the desktop will call back to VNCServerST and an extra set
546   // of ExtendedDesktopSize messages will be sent. This is okay
547   // protocol-wise, but unnecessary.
548   result = desktop->setScreenLayout(fb_width, fb_height, layout);
549   if (result != resultSuccess)
550     return result;
551 
552   // Sanity check
553   if (screenLayout != layout)
554     throw Exception("Desktop configured a different screen layout than requested");
555 
556   // Notify other clients
557   for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
558     ci_next = ci; ci_next++;
559     if ((*ci) == requester)
560       continue;
561     (*ci)->screenLayoutChangeOrClose(reasonOtherClient);
562   }
563 
564   return resultSuccess;
565 }
566 
567 // Other public methods
568 
approveConnection(network::Socket * sock,bool accept,const char * reason)569 void VNCServerST::approveConnection(network::Socket* sock, bool accept,
570                                     const char* reason)
571 {
572   std::list<VNCSConnectionST*>::iterator ci;
573   for (ci = clients.begin(); ci != clients.end(); ci++) {
574     if ((*ci)->getSock() == sock) {
575       (*ci)->approveConnectionOrClose(accept, reason);
576       return;
577     }
578   }
579 }
580 
closeClients(const char * reason,network::Socket * except)581 void VNCServerST::closeClients(const char* reason, network::Socket* except)
582 {
583   std::list<VNCSConnectionST*>::iterator i, next_i;
584   for (i=clients.begin(); i!=clients.end(); i=next_i) {
585     next_i = i; next_i++;
586     if ((*i)->getSock() != except)
587       (*i)->close(reason);
588   }
589 }
590 
getSockets(std::list<network::Socket * > * sockets)591 void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
592 {
593   sockets->clear();
594   std::list<VNCSConnectionST*>::iterator ci;
595   for (ci = clients.begin(); ci != clients.end(); ci++) {
596     sockets->push_back((*ci)->getSock());
597   }
598   std::list<network::Socket*>::iterator si;
599   for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
600     sockets->push_back(*si);
601   }
602 }
603 
getConnection(network::Socket * sock)604 SConnection* VNCServerST::getConnection(network::Socket* sock) {
605   std::list<VNCSConnectionST*>::iterator ci;
606   for (ci = clients.begin(); ci != clients.end(); ci++) {
607     if ((*ci)->getSock() == sock)
608       return (SConnection*)*ci;
609   }
610   return 0;
611 }
612 
handleTimeout(Timer * t)613 bool VNCServerST::handleTimeout(Timer* t)
614 {
615   if (t == &frameTimer) {
616     // We keep running until we go a full interval without any updates
617     if (comparer->is_empty())
618       return false;
619 
620     writeUpdate();
621 
622     // If this is the first iteration then we need to adjust the timeout
623     if (frameTimer.getTimeoutMs() != 1000/rfb::Server::frameRate) {
624       frameTimer.start(1000/rfb::Server::frameRate);
625       return false;
626     }
627 
628     return true;
629   } else if (t == &idleTimer) {
630     slog.info("MaxIdleTime reached, exiting");
631     desktop->terminate();
632   } else if (t == &disconnectTimer) {
633     slog.info("MaxDisconnectionTime reached, exiting");
634     desktop->terminate();
635   } else if (t == &connectTimer) {
636     slog.info("MaxConnectionTime reached, exiting");
637     desktop->terminate();
638   }
639 
640   return false;
641 }
642 
queryConnection(VNCSConnectionST * client,const char * userName)643 void VNCServerST::queryConnection(VNCSConnectionST* client,
644                                   const char* userName)
645 {
646   // - Authentication succeeded - clear from blacklist
647   CharArray name;
648   name.buf = client->getSock()->getPeerAddress();
649   blHosts->clearBlackmark(name.buf);
650 
651   // - Prepare the desktop for that the client will start requiring
652   // resources after this
653   startDesktop();
654 
655   // - Special case to provide a more useful error message
656   if (rfb::Server::neverShared &&
657       !rfb::Server::disconnectClients &&
658       authClientCount() > 0) {
659     approveConnection(client->getSock(), false,
660                       "The server is already in use");
661     return;
662   }
663 
664   // - Are we configured to do queries?
665   if (!rfb::Server::queryConnect &&
666       !client->getSock()->requiresQuery()) {
667     approveConnection(client->getSock(), true, NULL);
668     return;
669   }
670 
671   // - Does the client have the right to bypass the query?
672   if (client->accessCheck(SConnection::AccessNoQuery))
673   {
674     approveConnection(client->getSock(), true, NULL);
675     return;
676   }
677 
678   desktop->queryConnection(client->getSock(), userName);
679 }
680 
clientReady(VNCSConnectionST * client,bool shared)681 void VNCServerST::clientReady(VNCSConnectionST* client, bool shared)
682 {
683   if (!shared) {
684     if (rfb::Server::disconnectClients &&
685         client->accessCheck(SConnection::AccessNonShared)) {
686       // - Close all the other connected clients
687       slog.debug("non-shared connection - closing clients");
688       closeClients("Non-shared connection requested", client->getSock());
689     } else {
690       // - Refuse this connection if there are existing clients, in addition to
691       // this one
692       if (authClientCount() > 1) {
693         client->close("Server is already in use");
694         return;
695       }
696     }
697   }
698 }
699 
700 // -=- Internal methods
701 
startDesktop()702 void VNCServerST::startDesktop()
703 {
704   if (!desktopStarted) {
705     slog.debug("starting desktop");
706     desktop->start(this);
707     if (!pb)
708       throw Exception("SDesktop::start() did not set a valid PixelBuffer");
709     desktopStarted = true;
710     // The tracker might have accumulated changes whilst we were
711     // stopped, so flush those out
712     if (!comparer->is_empty())
713       writeUpdate();
714   }
715 }
716 
stopDesktop()717 void VNCServerST::stopDesktop()
718 {
719   if (desktopStarted) {
720     slog.debug("stopping desktop");
721     desktopStarted = false;
722     desktop->stop();
723     stopFrameClock();
724   }
725 }
726 
authClientCount()727 int VNCServerST::authClientCount() {
728   int count = 0;
729   std::list<VNCSConnectionST*>::iterator ci;
730   for (ci = clients.begin(); ci != clients.end(); ci++) {
731     if ((*ci)->authenticated())
732       count++;
733   }
734   return count;
735 }
736 
needRenderedCursor()737 inline bool VNCServerST::needRenderedCursor()
738 {
739   std::list<VNCSConnectionST*>::iterator ci;
740   for (ci = clients.begin(); ci != clients.end(); ci++)
741     if ((*ci)->needRenderedCursor()) return true;
742   return false;
743 }
744 
startFrameClock()745 void VNCServerST::startFrameClock()
746 {
747   if (frameTimer.isStarted())
748     return;
749   if (blockCounter > 0)
750     return;
751   if (!desktopStarted)
752     return;
753 
754   // The first iteration will be just half a frame as we get a very
755   // unstable update rate if we happen to be perfectly in sync with
756   // the application's update rate
757   frameTimer.start(1000/rfb::Server::frameRate/2);
758 }
759 
stopFrameClock()760 void VNCServerST::stopFrameClock()
761 {
762   frameTimer.stop();
763 }
764 
msToNextUpdate()765 int VNCServerST::msToNextUpdate()
766 {
767   // FIXME: If the application is updating slower than frameRate then
768   //        we could allow the clients more time here
769 
770   if (!frameTimer.isStarted())
771     return 1000/rfb::Server::frameRate/2;
772   else
773     return frameTimer.getRemainingMs();
774 }
775 
776 // writeUpdate() is called on a regular interval in order to see what
777 // updates are pending and propagates them to the update tracker for
778 // each client. It uses the ComparingUpdateTracker's compare() method
779 // to filter out areas of the screen which haven't actually changed. It
780 // also checks the state of the (server-side) rendered cursor, if
781 // necessary rendering it again with the correct background.
782 
writeUpdate()783 void VNCServerST::writeUpdate()
784 {
785   UpdateInfo ui;
786   Region toCheck;
787 
788   std::list<VNCSConnectionST*>::iterator ci, ci_next;
789 
790   assert(blockCounter == 0);
791   assert(desktopStarted);
792 
793   comparer->getUpdateInfo(&ui, pb->getRect());
794   toCheck = ui.changed.union_(ui.copied);
795 
796   if (needRenderedCursor()) {
797     Rect clippedCursorRect = Rect(0, 0, cursor->width(), cursor->height())
798                              .translate(cursorPos.subtract(cursor->hotspot()))
799                              .intersect(pb->getRect());
800 
801     if (!toCheck.intersect(clippedCursorRect).is_empty())
802       renderedCursorInvalid = true;
803   }
804 
805   pb->grabRegion(toCheck);
806 
807   if (getComparerState())
808     comparer->enable();
809   else
810     comparer->disable();
811 
812   if (comparer->compare())
813     comparer->getUpdateInfo(&ui, pb->getRect());
814 
815   comparer->clear();
816 
817   for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
818     ci_next = ci; ci_next++;
819     (*ci)->add_copied(ui.copied, ui.copy_delta);
820     (*ci)->add_changed(ui.changed);
821     (*ci)->writeFramebufferUpdateOrClose();
822   }
823 }
824 
825 // checkUpdate() is called by clients to see if it is safe to read from
826 // the framebuffer at this time.
827 
getPendingRegion()828 Region VNCServerST::getPendingRegion()
829 {
830   UpdateInfo ui;
831 
832   // Block clients as the frame buffer cannot be safely accessed
833   if (blockCounter > 0)
834     return pb->getRect();
835 
836   // Block client from updating if there are pending updates
837   if (comparer->is_empty())
838     return Region();
839 
840   comparer->getUpdateInfo(&ui, pb->getRect());
841 
842   return ui.changed.union_(ui.copied);
843 }
844 
getRenderedCursor()845 const RenderedCursor* VNCServerST::getRenderedCursor()
846 {
847   if (renderedCursorInvalid) {
848     renderedCursor.update(pb, cursor, cursorPos);
849     renderedCursorInvalid = false;
850   }
851 
852   return &renderedCursor;
853 }
854 
getComparerState()855 bool VNCServerST::getComparerState()
856 {
857   if (rfb::Server::compareFB == 0)
858     return false;
859   if (rfb::Server::compareFB != 2)
860     return true;
861 
862   std::list<VNCSConnectionST*>::iterator ci, ci_next;
863   for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
864     ci_next = ci; ci_next++;
865     if ((*ci)->getComparerState())
866       return true;
867   }
868   return false;
869 }
870