Merge branch 'connparams' of https://github.com/CendioOssman/tigervnc
diff --git a/.travis.yml b/.travis.yml
index 74878fc..ceafcbb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,5 +16,5 @@
  - popd
 
 script:
- - cmake . && make
+ - cmake -DCMAKE_BUILD_TYPE=Debug . && make
  - cd java && cmake . && make
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4fb3360..7a9a7ce 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,7 @@
 # Setup
 #
 
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 2.8.11)
 if(POLICY CMP0022)
   cmake_policy(SET CMP0022 OLD)
 endif()
@@ -43,9 +43,7 @@
 endif()
 
 if(NOT BUILD_TIMESTAMP)
-  set(BUILD_TIMESTAMP "")
-  execute_process(COMMAND "date" "+%Y-%m-%d %H:%M" OUTPUT_VARIABLE BUILD_TIMESTAMP)
-  string(REGEX REPLACE "\n" "" BUILD_TIMESTAMP ${BUILD_TIMESTAMP})
+  STRING(TIMESTAMP BUILD_TIMESTAMP "%Y-%m-%d %H:%M" UTC)
 endif()
 
 # Default to optimised builds instead of debug ones. Our code has no bugs ;)
@@ -76,8 +74,8 @@
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wformat=2")
 # Make sure we catch these issues whilst developing
 IF(CMAKE_BUILD_TYPE MATCHES Debug)
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Werror=vla")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Werror=vla")
 ENDIF()
 
 option(ENABLE_ASAN "Enable address sanitizer support" OFF)
diff --git a/common/network/Socket.h b/common/network/Socket.h
index bfda8a5..d38feba 100644
--- a/common/network/Socket.h
+++ b/common/network/Socket.h
@@ -144,15 +144,6 @@
     //   This is only necessary if the Socket has been put in non-blocking
     //   mode and needs this callback to flush the buffer.
     virtual void processSocketWriteEvent(network::Socket* sock) = 0;
-
-    // checkTimeouts() allows the server to check socket timeouts, etc.  The
-    //   return value is the number of milliseconds to wait before
-    //   checkTimeouts() should be called again.  If this number is zero then
-    //   there is no timeout and checkTimeouts() should be called the next time
-    //   an event occurs.
-    virtual int checkTimeouts() = 0;
-
-    virtual bool getDisable() {return false;};
   };
 
 }
diff --git a/common/network/TcpSocket.cxx b/common/network/TcpSocket.cxx
index 51d77c7..9e277cb 100644
--- a/common/network/TcpSocket.cxx
+++ b/common/network/TcpSocket.cxx
@@ -736,7 +736,7 @@
                 buffer + 1, sizeof (buffer) - 2, NULL, 0, NI_NUMERICHOST);
     strcat(buffer, "]");
     addr.buf = rfb::strDup(buffer);
-  } else if (p.address.u.sa.sa_family == AF_UNSPEC)
+  } else
     addr.buf = rfb::strDup("");
 
   char action;
diff --git a/common/rfb/CMsgReader.cxx b/common/rfb/CMsgReader.cxx
index 17152ab..3422ebf 100644
--- a/common/rfb/CMsgReader.cxx
+++ b/common/rfb/CMsgReader.cxx
@@ -208,7 +208,7 @@
   if (width > maxCursorSize || height > maxCursorSize)
     throw Exception("Too big cursor");
 
-  rdr::U8 buf[width*height*4];
+  rdr::U8Array rgba(width*height*4);
 
   if (width * height > 0) {
     rdr::U8 pr, pg, pb;
@@ -233,7 +233,7 @@
     is->readBytes(mask.buf, mask_len);
 
     int maskBytesPerRow = (width+7)/8;
-    out = buf;
+    out = rgba.buf;
     for (y = 0;y < height;y++) {
       for (x = 0;x < width;x++) {
         int byte = y * maskBytesPerRow + x / 8;
@@ -259,7 +259,7 @@
     }
   }
 
-  handler->setCursor(width, height, hotspot, buf);
+  handler->setCursor(width, height, hotspot, rgba.buf);
 }
 
 void CMsgReader::readSetCursor(int width, int height, const Point& hotspot)
@@ -273,7 +273,7 @@
   rdr::U8Array mask(mask_len);
 
   int x, y;
-  rdr::U8 buf[width*height*4];
+  rdr::U8Array rgba(width*height*4);
   rdr::U8* in;
   rdr::U8* out;
 
@@ -282,7 +282,7 @@
 
   int maskBytesPerRow = (width+7)/8;
   in = data.buf;
-  out = buf;
+  out = rgba.buf;
   for (y = 0;y < height;y++) {
     for (x = 0;x < width;x++) {
       int byte = y * maskBytesPerRow + x / 8;
@@ -300,7 +300,7 @@
     }
   }
 
-  handler->setCursor(width, height, hotspot, buf);
+  handler->setCursor(width, height, hotspot, rgba.buf);
 }
 
 void CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hotspot)
diff --git a/common/rfb/CSecurityTLS.cxx b/common/rfb/CSecurityTLS.cxx
index 72d148e..c6d1e31 100644
--- a/common/rfb/CSecurityTLS.cxx
+++ b/common/rfb/CSecurityTLS.cxx
@@ -94,9 +94,9 @@
   delete [] homeDir;
 
  if (!fileexists(caDefault.buf))
-   X509CA.setDefaultStr(strdup(caDefault.buf));
+   X509CA.setDefaultStr(caDefault.buf);
  if (!fileexists(crlDefault.buf))
-   X509CRL.setDefaultStr(strdup(crlDefault.buf));
+   X509CRL.setDefaultStr(crlDefault.buf);
 }
 
 void CSecurityTLS::shutdown(bool needbye)
diff --git a/common/rfb/ComparingUpdateTracker.h b/common/rfb/ComparingUpdateTracker.h
index e62f2b2..ca1dcc3 100644
--- a/common/rfb/ComparingUpdateTracker.h
+++ b/common/rfb/ComparingUpdateTracker.h
@@ -50,7 +50,7 @@
     bool firstCompare;
     bool enabled;
 
-    rdr::U32 totalPixels, missedPixels;
+    unsigned long long totalPixels, missedPixels;
   };
 
 }
diff --git a/common/rfb/Cursor.cxx b/common/rfb/Cursor.cxx
index 99df82d..d7b536d 100644
--- a/common/rfb/Cursor.cxx
+++ b/common/rfb/Cursor.cxx
@@ -76,7 +76,7 @@
 }
 
 // Floyd-Steinberg dithering
-static void dither(int width, int height, int* data)
+static void dither(int width, int height, rdr::S32* data)
 {
   for (int y = 0; y < height; y++) {
     for (int x_ = 0; x_ < width; x_++) {
@@ -122,31 +122,33 @@
 rdr::U8* Cursor::getBitmap() const
 {
   // First step is converting to luminance
-  int luminance[width()*height()];
-  int *lum_ptr = luminance;
+  rdr::S32Array luminance(width()*height());
+  rdr::S32 *lum_ptr = luminance.buf;
   const rdr::U8 *data_ptr = data;
   for (int y = 0; y < height(); y++) {
     for (int x = 0; x < width(); x++) {
-      // Use BT.709 coefficients for grayscale
-      *lum_ptr = 0;
-      *lum_ptr += (int)srgb_to_lin(data_ptr[0]) * 6947;  // 0.2126
-      *lum_ptr += (int)srgb_to_lin(data_ptr[1]) * 23436; // 0.7152
-      *lum_ptr += (int)srgb_to_lin(data_ptr[2]) * 2366;  // 0.0722
-      *lum_ptr /= 32768;
+      rdr::S32 lum;
 
-      lum_ptr++;
+      // Use BT.709 coefficients for grayscale
+      lum = 0;
+      lum += (rdr::U32)srgb_to_lin(data_ptr[0]) * 6947;  // 0.2126
+      lum += (rdr::U32)srgb_to_lin(data_ptr[1]) * 23436; // 0.7152
+      lum += (rdr::U32)srgb_to_lin(data_ptr[2]) * 2366;  // 0.0722
+      lum /= 32768;
+
+      *lum_ptr++ = lum;
       data_ptr += 4;
     }
   }
 
   // Then diterhing
-  dither(width(), height(), luminance);
+  dither(width(), height(), luminance.buf);
 
   // Then conversion to a bit mask
   rdr::U8Array source((width()+7)/8*height());
   memset(source.buf, 0, (width()+7)/8*height());
   int maskBytesPerRow = (width() + 7) / 8;
-  lum_ptr = luminance;
+  lum_ptr = luminance.buf;
   data_ptr = data;
   for (int y = 0; y < height(); y++) {
     for (int x = 0; x < width(); x++) {
@@ -165,25 +167,24 @@
 rdr::U8* Cursor::getMask() const
 {
   // First step is converting to integer array
-  int alpha[width()*height()];
-  int *alpha_ptr = alpha;
+  rdr::S32Array alpha(width()*height());
+  rdr::S32 *alpha_ptr = alpha.buf;
   const rdr::U8 *data_ptr = data;
   for (int y = 0; y < height(); y++) {
     for (int x = 0; x < width(); x++) {
-      *alpha_ptr = (int)data_ptr[3] * 65535 / 255;
-      alpha_ptr++;
+      *alpha_ptr++ = (rdr::U32)data_ptr[3] * 65535 / 255;
       data_ptr += 4;
     }
   }
 
   // Then diterhing
-  dither(width(), height(), alpha);
+  dither(width(), height(), alpha.buf);
 
   // Then conversion to a bit mask
   rdr::U8Array mask((width()+7)/8*height());
   memset(mask.buf, 0, (width()+7)/8*height());
   int maskBytesPerRow = (width() + 7) / 8;
-  alpha_ptr = alpha;
+  alpha_ptr = alpha.buf;
   data_ptr = data;
   for (int y = 0; y < height(); y++) {
     for (int x = 0; x < width(); x++) {
diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx
index d0490de..690653a 100644
--- a/common/rfb/SConnection.cxx
+++ b/common/rfb/SConnection.cxx
@@ -277,13 +277,14 @@
   throw ConnFailedException(str);
 }
 
-void SConnection::writeConnFailedFromScratch(const char* msg,
-                                             rdr::OutStream* os)
+void SConnection::setAccessRights(AccessRights ar)
 {
-  os->writeBytes("RFB 003.003\n", 12);
-  os->writeU32(0);
-  os->writeString(msg);
-  os->flush();
+  accessRights = ar;
+}
+
+bool SConnection::accessCheck(AccessRights ar) const
+{
+  return (accessRights & ar) == ar;
 }
 
 void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
@@ -360,6 +361,11 @@
   state_ = RFBSTATE_NORMAL;
 }
 
+void SConnection::close(const char* reason)
+{
+  state_ = RFBSTATE_CLOSING;
+}
+
 void SConnection::setPixelFormat(const PixelFormat& pf)
 {
   SMsgHandler::setPixelFormat(pf);
diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h
index 47092e3..2630240 100644
--- a/common/rfb/SConnection.h
+++ b/common/rfb/SConnection.h
@@ -69,6 +69,13 @@
     void approveConnection(bool accept, const char* reason=0);
 
 
+    // Methods to terminate the connection
+
+    // close() shuts down the connection to the client and awaits
+    // cleanup of the SConnection object by the server
+    virtual void close(const char* reason);
+
+
     // Overridden from SMsgHandler
 
     virtual void setEncodings(int nEncodings, const rdr::S32* encodings);
@@ -118,8 +125,10 @@
     virtual void enableContinuousUpdates(bool enable,
                                          int x, int y, int w, int h);
 
+    // Other methods
+
     // setAccessRights() allows a security package to limit the access rights
-    // of a VNCSConnectionST to the server.  How the access rights are treated
+    // of a SConnection to the server.  How the access rights are treated
     // is up to the derived class.
 
     typedef rdr::U16 AccessRights;
@@ -132,27 +141,14 @@
     static const AccessRights AccessDefault;        // The default rights, INCLUDING FUTURE ONES
     static const AccessRights AccessNoQuery;        // Connect without local user accepting
     static const AccessRights AccessFull;           // All of the available AND FUTURE rights
-    virtual void setAccessRights(AccessRights ar) = 0;
-
-    // Other methods
+    virtual void setAccessRights(AccessRights ar);
+    virtual bool accessCheck(AccessRights ar) const;
 
     // authenticated() returns true if the client has authenticated
     // successfully.
     bool authenticated() { return (state_ == RFBSTATE_INITIALISATION ||
                                    state_ == RFBSTATE_NORMAL); }
 
-    // throwConnFailedException() prints a message to the log, sends a conn
-    // failed message to the client (if possible) and throws a
-    // ConnFailedException.
-    void throwConnFailedException(const char* format, ...) __printf_attr(2, 3);
-
-    // writeConnFailedFromScratch() sends a conn failed message to an OutStream
-    // without the need to negotiate the protocol version first.  It actually
-    // does this by assuming that the client will understand version 3.3 of the
-    // protocol.
-    static void writeConnFailedFromScratch(const char* msg,
-                                           rdr::OutStream* os);
-
     SMsgReader* reader() { return reader_; }
     SMsgWriter* writer() { return writer_; }
 
@@ -176,6 +172,11 @@
     rdr::S32 getPreferredEncoding() { return preferredEncoding; }
 
   protected:
+    // throwConnFailedException() prints a message to the log, sends a conn
+    // failed message to the client (if possible) and throws a
+    // ConnFailedException.
+    void throwConnFailedException(const char* format, ...) __printf_attr(2, 3);
+
     void setState(stateEnum s) { state_ = s; }
 
     void setReader(SMsgReader *r) { reader_ = r; }
@@ -201,6 +202,7 @@
     SSecurity* ssecurity;
     stateEnum state_;
     rdr::S32 preferredEncoding;
+    AccessRights accessRights;
   };
 }
 #endif
diff --git a/common/rfb/SDesktop.h b/common/rfb/SDesktop.h
index 717ddbc..0060aa2 100644
--- a/common/rfb/SDesktop.h
+++ b/common/rfb/SDesktop.h
@@ -44,6 +44,8 @@
 #include <rfb/screenTypes.h>
 #include <rfb/util.h>
 
+namespace network { class Socket; }
+
 namespace rfb {
 
   class VNCServer;
@@ -56,14 +58,28 @@
     // set via the VNCServer's setPixelBuffer() method by the time this call
     // returns.
 
-    virtual void start(VNCServer* __unused_attr vs) {}
+    virtual void start(VNCServer* vs) = 0;
 
     // stop() is called by the server when there are no longer any
     // authenticated clients, and therefore the desktop can cease any
     // expensive tasks.  No further calls to the VNCServer passed to start()
     // can be made once stop has returned.
 
-    virtual void stop() {}
+    virtual void stop() = 0;
+
+    // queryConnection() is called when a connection has been
+    // successfully authenticated.  The sock and userName arguments
+    // identify the socket and the name of the authenticated user, if
+    // any. At some point later VNCServer::approveConnection() should
+    // be called to either accept or reject the client.
+    virtual void queryConnection(network::Socket* sock,
+                                 const char* userName) = 0;
+
+    // terminate() is called by the server when it wishes to terminate
+    // itself, e.g. because it was configured to terminate when no one is
+    // using it.
+
+    virtual void terminate() = 0;
 
     // setScreenLayout() requests to reconfigure the framebuffer and/or
     // the layout of screens.
@@ -112,6 +128,10 @@
       server->setPixelBuffer(0);
       server = 0;
     }
+    virtual void queryConnection(network::Socket* sock,
+                                 const char* userName) {
+      server->approveConnection(sock, true, NULL);
+    }
 
   protected:
     VNCServer* server;
diff --git a/common/rfb/SSecurityPlain.cxx b/common/rfb/SSecurityPlain.cxx
index 6d48b65..6f72432 100644
--- a/common/rfb/SSecurityPlain.cxx
+++ b/common/rfb/SSecurityPlain.cxx
@@ -41,7 +41,7 @@
 
 bool PasswordValidator::validUser(const char* username)
 {
-  CharArray users(strDup(plainUsers.getValueStr())), user;
+  CharArray users(plainUsers.getValueStr()), user;
 
   while (users.buf) {
     strSplit(users.buf, ',', &user.buf, &users.buf);
diff --git a/common/rfb/TightDecoder.cxx b/common/rfb/TightDecoder.cxx
index fad4731..5b7c553 100644
--- a/common/rfb/TightDecoder.cxx
+++ b/common/rfb/TightDecoder.cxx
@@ -266,15 +266,16 @@
       buflen -= 1;
 
       if (pf.is888()) {
-        rdr::U8 tightPalette[palSize * 3];
+        size_t len = palSize * 3;
+        rdr::U8Array tightPalette(len);
 
-        assert(buflen >= sizeof(tightPalette));
+        assert(buflen >= len);
 
-        memcpy(tightPalette, bufptr, sizeof(tightPalette));
-        bufptr += sizeof(tightPalette);
-        buflen -= sizeof(tightPalette);
+        memcpy(tightPalette.buf, bufptr, len);
+        bufptr += len;
+        buflen -= len;
 
-        pf.bufferFromRGB(palette, tightPalette, palSize);
+        pf.bufferFromRGB(palette, tightPalette.buf, palSize);
       } else {
         size_t len;
 
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index a58fd09..ea5c52a 100644
--- a/common/rfb/VNCSConnectionST.cxx
+++ b/common/rfb/VNCSConnectionST.cxx
@@ -51,28 +51,31 @@
     fenceDataLen(0), fenceData(NULL), congestionTimer(this),
     losslessTimer(this), server(server_),
     updateRenderedCursor(false), removeRenderedCursor(false),
-    continuousUpdates(false), encodeManager(this), pointerEventTime(0),
-    clientHasCursor(false),
-    accessRights(AccessDefault), startTime(time(0))
+    continuousUpdates(false), encodeManager(this), idleTimer(this),
+    pointerEventTime(0), clientHasCursor(false)
 {
   setStreams(&sock->inStream(), &sock->outStream());
   peerEndpoint.buf = sock->getPeerEndpoint();
-  VNCServerST::connectionsLog.write(1,"accepted: %s", peerEndpoint.buf);
 
   // Configure the socket
   setSocketTimeouts();
-  lastEventTime = time(0);
 
-  server->clients.push_front(this);
+  // Kick off the idle timer
+  if (rfb::Server::idleTimeout) {
+    // minimum of 15 seconds while authenticating
+    if (rfb::Server::idleTimeout < 15)
+      idleTimer.start(secsToMillis(15));
+    else
+      idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
+  }
 }
 
 
 VNCSConnectionST::~VNCSConnectionST()
 {
   // If we reach here then VNCServerST is deleting us!
-  VNCServerST::connectionsLog.write(1,"closed: %s (%s)",
-                                    peerEndpoint.buf,
-                                    (closeReason.buf) ? closeReason.buf : "");
+  if (closeReason.buf)
+    vlog.info("closing %s: %s", peerEndpoint.buf, closeReason.buf);
 
   // Release any keys the client still had pressed
   while (!pressedKeys.empty()) {
@@ -84,19 +87,42 @@
 
     vlog.debug("Releasing key 0x%x / 0x%x on client disconnect",
                keysym, keycode);
-    server->desktop->keyEvent(keysym, keycode, false);
+    server->keyEvent(keysym, keycode, false);
   }
 
-  if (server->pointerClient == this)
-    server->pointerClient = 0;
-
-  // Remove this client from the server
-  server->clients.remove(this);
-
   delete [] fenceData;
 }
 
 
+// SConnection methods
+
+bool VNCSConnectionST::accessCheck(AccessRights ar) const
+{
+  // Reverse connections are user initiated, so they are implicitly
+  // allowed to bypass the query
+  if (reverseConnection)
+    ar &= ~AccessNoQuery;
+
+  return SConnection::accessCheck(ar);
+}
+
+void VNCSConnectionST::close(const char* reason)
+{
+  // Log the reason for the close
+  if (!closeReason.buf)
+    closeReason.buf = strDup(reason);
+  else
+    vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
+
+  // Just shutdown the socket and mark our state as closing.  Eventually the
+  // calling code will call VNCServerST's removeSocket() method causing us to
+  // be deleted.
+  sock->shutdown();
+
+  SConnection::close(reason);
+}
+
+
 // Methods called from VNCServerST
 
 bool VNCSConnectionST::init()
@@ -110,25 +136,6 @@
   return true;
 }
 
-void VNCSConnectionST::close(const char* reason)
-{
-  // Log the reason for the close
-  if (!closeReason.buf)
-    closeReason.buf = strDup(reason);
-  else
-    vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
-
-  if (authenticated()) {
-      server->lastDisconnectTime = time(0);
-  }
-
-  // Just shutdown the socket and mark our state as closing.  Eventually the
-  // calling code will call VNCServerST's removeSocket() method causing us to
-  // be deleted.
-  sock->shutdown();
-  setState(RFBSTATE_CLOSING);
-}
-
 
 void VNCSConnectionST::processMessages()
 {
@@ -193,8 +200,8 @@
   try {
     if (!authenticated()) return;
     if (client.width() && client.height() &&
-        (server->pb->width() != client.width() ||
-         server->pb->height() != client.height()))
+        (server->getPixelBuffer()->width() != client.width() ||
+         server->getPixelBuffer()->height() != client.height()))
     {
       // We need to clip the next update to the new size, but also add any
       // extra bits if it's bigger.  If we wanted to do this exactly, something
@@ -211,10 +218,11 @@
       //  updates.add_changed(Rect(0, client.height(), client.width(),
       //                           server->pb->height()));
 
-      damagedCursorRegion.assign_intersect(server->pb->getRect());
+      damagedCursorRegion.assign_intersect(server->getPixelBuffer()->getRect());
 
-      client.setDimensions(server->pb->width(), server->pb->height(),
-                           server->screenLayout);
+      client.setDimensions(server->getPixelBuffer()->width(),
+                           server->getPixelBuffer()->height(),
+                           server->getScreenLayout());
       if (state() == RFBSTATE_NORMAL) {
         if (!client.supportsDesktopSize()) {
           close("Client does not support desktop resize");
@@ -224,12 +232,12 @@
       }
 
       // Drop any lossy tracking that is now outside the framebuffer
-      encodeManager.pruneLosslessRefresh(Region(server->pb->getRect()));
+      encodeManager.pruneLosslessRefresh(Region(server->getPixelBuffer()->getRect()));
     }
     // Just update the whole screen at the moment because we're too lazy to
     // work out what's actually changed.
     updates.clear();
-    updates.add_changed(server->pb->getRect());
+    updates.add_changed(server->getPixelBuffer()->getRect());
     writeFramebufferUpdate();
   } catch(rdr::Exception &e) {
     close(e.str());
@@ -267,7 +275,7 @@
 void VNCSConnectionST::serverCutTextOrClose(const char *str, int len)
 {
   try {
-    if (!(accessRights & AccessCutText)) return;
+    if (!accessCheck(AccessCutText)) return;
     if (!rfb::Server::sendCutText) return;
     if (state() == RFBSTATE_NORMAL)
       writer()->writeServerCutText(str, len);
@@ -310,36 +318,6 @@
 }
 
 
-int VNCSConnectionST::checkIdleTimeout()
-{
-  int idleTimeout = rfb::Server::idleTimeout;
-  if (idleTimeout == 0) return 0;
-  if (state() != RFBSTATE_NORMAL && idleTimeout < 15)
-    idleTimeout = 15; // minimum of 15 seconds while authenticating
-  time_t now = time(0);
-  if (now < lastEventTime) {
-    // Someone must have set the time backwards.  Set lastEventTime so that the
-    // idleTimeout will count from now.
-    vlog.info("Time has gone backwards - resetting idle timeout");
-    lastEventTime = now;
-  }
-  int timeLeft = lastEventTime + idleTimeout - now;
-  if (timeLeft < -60) {
-    // Our callback is over a minute late - someone must have set the time
-    // forwards.  Set lastEventTime so that the idleTimeout will count from
-    // now.
-    vlog.info("Time has gone forwards - resetting idle timeout");
-    lastEventTime = now;
-    return secsToMillis(idleTimeout);
-  }
-  if (timeLeft <= 0) {
-    close("Idle timeout");
-    return 0;
-  }
-  return secsToMillis(timeLeft);
-}
-
-
 bool VNCSConnectionST::getComparerState()
 {
   // We interpret a low compression level as an indication that the client
@@ -385,7 +363,7 @@
 
   if (!client.supportsLocalCursor())
     return true;
-  if (!server->cursorPos.equals(pointerEventPos) &&
+  if (!server->getCursorPos().equals(pointerEventPos) &&
       (time(0) - pointerEventTime) > 0)
     return true;
 
@@ -409,83 +387,40 @@
 
 void VNCSConnectionST::authSuccess()
 {
-  lastEventTime = time(0);
-
-  server->startDesktop();
+  if (rfb::Server::idleTimeout)
+    idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
 
   // - Set the connection parameters appropriately
-  client.setDimensions(server->pb->width(), server->pb->height(),
-                       server->screenLayout);
+  client.setDimensions(server->getPixelBuffer()->width(),
+                       server->getPixelBuffer()->height(),
+                       server->getScreenLayout());
   client.setName(server->getName());
-  client.setLEDState(server->ledState);
+  client.setLEDState(server->getLEDState());
   
   // - Set the default pixel format
-  client.setPF(server->pb->getPF());
+  client.setPF(server->getPixelBuffer()->getPF());
   char buffer[256];
   client.pf().print(buffer, 256);
   vlog.info("Server default pixel format %s", buffer);
 
   // - Mark the entire display as "dirty"
-  updates.add_changed(server->pb->getRect());
-  startTime = time(0);
+  updates.add_changed(server->getPixelBuffer()->getRect());
 }
 
 void VNCSConnectionST::queryConnection(const char* userName)
 {
-  // - Authentication succeeded - clear from blacklist
-  CharArray name; name.buf = sock->getPeerAddress();
-  server->blHosts->clearBlackmark(name.buf);
-
-  // - Special case to provide a more useful error message
-  if (rfb::Server::neverShared && !rfb::Server::disconnectClients &&
-    server->authClientCount() > 0) {
-    approveConnection(false, "The server is already in use");
-    return;
-  }
-
-  // - Does the client have the right to bypass the query?
-  if (reverseConnection ||
-      !(rfb::Server::queryConnect || sock->requiresQuery()) ||
-      (accessRights & AccessNoQuery))
-  {
-    approveConnection(true);
-    return;
-  }
-
-  // - Get the server to display an Accept/Reject dialog, if required
-  //   If a dialog is displayed, the result will be PENDING, and the
-  //   server will call approveConnection at a later time
-  CharArray reason;
-  VNCServerST::queryResult qr = server->queryConnection(sock, userName,
-                                                        &reason.buf);
-  if (qr == VNCServerST::PENDING)
-    return;
-
-  // - If server returns ACCEPT/REJECT then pass result to SConnection
-  approveConnection(qr == VNCServerST::ACCEPT, reason.buf);
+  server->queryConnection(this, userName);
 }
 
 void VNCSConnectionST::clientInit(bool shared)
 {
-  lastEventTime = time(0);
+  if (rfb::Server::idleTimeout)
+    idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
   if (rfb::Server::alwaysShared || reverseConnection) shared = true;
-  if (!(accessRights & AccessNonShared)) shared = true;
+  if (!accessCheck(AccessNonShared)) shared = true;
   if (rfb::Server::neverShared) shared = false;
-  if (!shared) {
-    if (rfb::Server::disconnectClients && (accessRights & AccessNonShared)) {
-      // - Close all the other connected clients
-      vlog.debug("non-shared connection - closing clients");
-      server->closeClients("Non-shared connection requested", getSock());
-    } else {
-      // - Refuse this connection if there are existing clients, in addition to
-      // this one
-      if (server->authClientCount() > 1) {
-        close("Server is already in use");
-        return;
-      }
-    }
-  }
   SConnection::clientInit(shared);
+  server->clientReady(this, shared);
 }
 
 void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
@@ -499,37 +434,32 @@
 
 void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask)
 {
-  pointerEventTime = lastEventTime = time(0);
-  server->lastUserInputTime = lastEventTime;
-  if (!(accessRights & AccessPtrEvents)) return;
+  if (rfb::Server::idleTimeout)
+    idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
+  pointerEventTime = time(0);
+  if (!accessCheck(AccessPtrEvents)) return;
   if (!rfb::Server::acceptPointerEvents) return;
-  if (!server->pointerClient || server->pointerClient == this) {
-    pointerEventPos = pos;
-    if (buttonMask)
-      server->pointerClient = this;
-    else
-      server->pointerClient = 0;
-    server->desktop->pointerEvent(pointerEventPos, buttonMask);
-  }
+  pointerEventPos = pos;
+  server->pointerEvent(this, pointerEventPos, buttonMask);
 }
 
 
 class VNCSConnectionSTShiftPresser {
 public:
-  VNCSConnectionSTShiftPresser(SDesktop* desktop_)
-    : desktop(desktop_), pressed(false) {}
+  VNCSConnectionSTShiftPresser(VNCServerST* server_)
+    : server(server_), pressed(false) {}
   ~VNCSConnectionSTShiftPresser() {
     if (pressed) {
       vlog.debug("Releasing fake Shift_L");
-      desktop->keyEvent(XK_Shift_L, 0, false);
+      server->keyEvent(XK_Shift_L, 0, false);
     }
   }
   void press() {
     vlog.debug("Pressing fake Shift_L");
-    desktop->keyEvent(XK_Shift_L, 0, true);
+    server->keyEvent(XK_Shift_L, 0, true);
     pressed = true;
   }
-  SDesktop* desktop;
+  VNCServerST* server;
   bool pressed;
 };
 
@@ -538,9 +468,9 @@
 void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {
   rdr::U32 lookup;
 
-  lastEventTime = time(0);
-  server->lastUserInputTime = lastEventTime;
-  if (!(accessRights & AccessKeyEvents)) return;
+  if (rfb::Server::idleTimeout)
+    idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
+  if (!accessCheck(AccessKeyEvents)) return;
   if (!rfb::Server::acceptKeyEvents) return;
 
   if (down)
@@ -548,18 +478,8 @@
   else
     vlog.debug("Key released: 0x%x / 0x%x", keysym, keycode);
 
-  // Remap the key if required
-  if (server->keyRemapper) {
-    rdr::U32 newkey;
-    newkey = server->keyRemapper->remapKey(keysym);
-    if (newkey != keysym) {
-      vlog.debug("Key remapped to 0x%x", newkey);
-      keysym = newkey;
-    }
-  }
-
   // Avoid lock keys if we don't know the server state
-  if ((server->ledState == ledUnknown) &&
+  if ((server->getLEDState() == ledUnknown) &&
       ((keysym == XK_Caps_Lock) ||
        (keysym == XK_Num_Lock) ||
        (keysym == XK_Scroll_Lock))) {
@@ -577,7 +497,7 @@
       return;
     }
 
-    if (down && (server->ledState != ledUnknown)) {
+    if (down && (server->getLEDState() != ledUnknown)) {
       // CapsLock synchronisation heuristic
       // (this assumes standard interaction between CapsLock the Shift
       // keys and normal characters)
@@ -587,12 +507,12 @@
 
         uppercase = (keysym >= XK_A) && (keysym <= XK_Z);
         shift = isShiftPressed();
-        lock = server->ledState & ledCapsLock;
+        lock = server->getLEDState() & ledCapsLock;
 
         if (lock == (uppercase == shift)) {
           vlog.debug("Inserting fake CapsLock to get in sync with client");
-          server->desktop->keyEvent(XK_Caps_Lock, 0, true);
-          server->desktop->keyEvent(XK_Caps_Lock, 0, false);
+          server->keyEvent(XK_Caps_Lock, 0, true);
+          server->keyEvent(XK_Caps_Lock, 0, false);
         }
       }
 
@@ -607,7 +527,7 @@
         number = ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) ||
                   (keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal);
         shift = isShiftPressed();
-        lock = server->ledState & ledNumLock;
+        lock = server->getLEDState() & ledNumLock;
 
         if (shift) {
           // We don't know the appropriate NumLock state for when Shift
@@ -621,15 +541,15 @@
           //
         } else if (lock == (number == shift)) {
           vlog.debug("Inserting fake NumLock to get in sync with client");
-          server->desktop->keyEvent(XK_Num_Lock, 0, true);
-          server->desktop->keyEvent(XK_Num_Lock, 0, false);
+          server->keyEvent(XK_Num_Lock, 0, true);
+          server->keyEvent(XK_Num_Lock, 0, false);
         }
       }
     }
   }
 
   // Turn ISO_Left_Tab into shifted Tab.
-  VNCSConnectionSTShiftPresser shiftPresser(server->desktop);
+  VNCSConnectionSTShiftPresser shiftPresser(server);
   if (keysym == XK_ISO_Left_Tab) {
     if (!isShiftPressed())
       shiftPresser.press();
@@ -655,21 +575,21 @@
       return;
   }
 
-  server->desktop->keyEvent(keysym, keycode, down);
+  server->keyEvent(keysym, keycode, down);
 }
 
 void VNCSConnectionST::clientCutText(const char* str, int len)
 {
-  if (!(accessRights & AccessCutText)) return;
+  if (!accessCheck(AccessCutText)) return;
   if (!rfb::Server::acceptCutText) return;
-  server->desktop->clientCutText(str, len);
+  server->clientCutText(str, len);
 }
 
 void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
 {
   Rect safeRect;
 
-  if (!(accessRights & AccessView)) return;
+  if (!accessCheck(AccessView)) return;
 
   SConnection::framebufferUpdateRequest(r, incremental);
 
@@ -710,28 +630,11 @@
 {
   unsigned int result;
 
-  if (!(accessRights & AccessSetDesktopSize)) return;
+  if (!accessCheck(AccessSetDesktopSize)) return;
   if (!rfb::Server::acceptSetDesktopSize) return;
 
-  // Don't bother the desktop with an invalid configuration
-  if (!layout.validate(fb_width, fb_height)) {
-    writer()->writeDesktopSize(reasonClient, resultInvalid);
-    return;
-  }
-
-  // FIXME: the desktop will call back to VNCServerST and an extra set
-  // of ExtendedDesktopSize messages will be sent. This is okay
-  // protocol-wise, but unnecessary.
-  result = server->desktop->setScreenLayout(fb_width, fb_height, layout);
-
+  result = server->setDesktopSize(this, fb_width, fb_height, layout);
   writer()->writeDesktopSize(reasonClient, result);
-
-  // Only notify other clients on success
-  if (result == resultSuccess) {
-    if (server->screenLayout != layout)
-        throw Exception("Desktop configured a different screen layout than requested");
-    server->notifyScreenLayoutChange(this);
-  }
 }
 
 void VNCSConnectionST::fence(rdr::U32 flags, unsigned len, const char data[])
@@ -846,6 +749,9 @@
     close(e.str());
   }
 
+  if (t == &idleTimer)
+    close("Idle timeout");
+
   return false;
 }
 
@@ -966,7 +872,7 @@
 
 void VNCSConnectionST::writeDataUpdate()
 {
-  Region req, pending;
+  Region req;
   UpdateInfo ui;
   bool needNewUpdateInfo;
   const RenderedCursor *cursor;
@@ -980,9 +886,6 @@
   if (req.is_empty())
     return;
 
-  // Get any framebuffer changes we haven't yet been informed of
-  pending = server->getPendingRegion();
-
   // Get the lists of updates. Prior to exporting the data to the `ui' object,
   // getUpdateInfo() will normalize the `updates' object such way that its
   // `changed' and `copied' regions would not intersect.
@@ -998,7 +901,7 @@
 
     bogusCopiedCursor = damagedCursorRegion;
     bogusCopiedCursor.translate(ui.copy_delta);
-    bogusCopiedCursor.assign_intersect(server->pb->getRect());
+    bogusCopiedCursor.assign_intersect(server->getPixelBuffer()->getRect());
     if (!ui.copied.intersect(bogusCopiedCursor).is_empty()) {
       updates.add_changed(bogusCopiedCursor);
       needNewUpdateInfo = true;
@@ -1031,13 +934,8 @@
 
   // If there are queued updates then we cannot safely send an update
   // without risking a partially updated screen
-
-  if (!pending.is_empty()) {
-    // However we might still be able to send a lossless refresh
-    req.assign_subtract(pending);
-    req.assign_subtract(ui.changed);
-    req.assign_subtract(ui.copied);
-
+  if (!server->getPendingRegion().is_empty()) {
+    req.clear();
     ui.changed.clear();
     ui.copied.clear();
   }
@@ -1063,48 +961,17 @@
     damagedCursorRegion.assign_union(ui.changed.intersect(renderedCursorRect));
   }
 
-  // Return if there is nothing to send the client.
+  // If we don't have a normal update, then try a lossless refresh
   if (ui.is_empty() && !writer()->needFakeUpdate()) {
-    int eta;
-
-    // Any lossless refresh that needs handling?
-    if (!encodeManager.needsLosslessRefresh(req))
-      return;
-
-    // Now? Or later?
-    eta = encodeManager.getNextLosslessRefresh(req);
-    if (eta > 0) {
-      losslessTimer.start(eta);
-      return;
-    }
+    writeLosslessRefresh();
+    return;
   }
 
+  // We have something to send, so let's get to it
+
   writeRTTPing();
 
-  if (!ui.is_empty())
-    encodeManager.writeUpdate(ui, server->getPixelBuffer(), cursor);
-  else {
-    int nextUpdate;
-
-    // FIXME: If continuous updates aren't used then the client might
-    //        be slower than frameRate in its requests and we could
-    //        afford a larger update size
-    nextUpdate = server->msToNextUpdate();
-    if (nextUpdate > 0) {
-      size_t bandwidth, maxUpdateSize;
-
-      // FIXME: Bandwidth estimation without congestion control
-      bandwidth = congestion.getBandwidth();
-
-      // FIXME: Hard coded value for maximum CPU throughput
-      if (bandwidth > 5000000)
-        bandwidth = 5000000;
-
-      maxUpdateSize = bandwidth * nextUpdate / 1000;
-      encodeManager.writeLosslessRefresh(req, server->getPixelBuffer(),
-                                         cursor, maxUpdateSize);
-    }
-  }
+  encodeManager.writeUpdate(ui, server->getPixelBuffer(), cursor);
 
   writeRTTPing();
 
@@ -1115,6 +982,80 @@
   requested.clear();
 }
 
+void VNCSConnectionST::writeLosslessRefresh()
+{
+  Region req, pending;
+  const RenderedCursor *cursor;
+
+  int nextRefresh, nextUpdate;
+  size_t bandwidth, maxUpdateSize;
+
+  if (continuousUpdates)
+    req = cuRegion.union_(requested);
+  else
+    req = requested;
+
+  // If there are queued updates then we could not safely send an
+  // update without risking a partially updated screen, however we
+  // might still be able to send a lossless refresh
+  pending = server->getPendingRegion();
+  if (!pending.is_empty()) {
+    UpdateInfo ui;
+
+    // Don't touch the updates pending in the server core
+    req.assign_subtract(pending);
+
+    // Or any updates pending just for this connection
+    updates.getUpdateInfo(&ui, req);
+    req.assign_subtract(ui.changed);
+    req.assign_subtract(ui.copied);
+  }
+
+  // Any lossy area we can refresh?
+  if (!encodeManager.needsLosslessRefresh(req))
+    return;
+
+  // Right away? Or later?
+  nextRefresh = encodeManager.getNextLosslessRefresh(req);
+  if (nextRefresh > 0) {
+    losslessTimer.start(nextRefresh);
+    return;
+  }
+
+  // Prepare the cursor in case it overlaps with a region getting
+  // refreshed
+  cursor = NULL;
+  if (needRenderedCursor())
+    cursor = server->getRenderedCursor();
+
+  // FIXME: If continuous updates aren't used then the client might
+  //        be slower than frameRate in its requests and we could
+  //        afford a larger update size
+  nextUpdate = server->msToNextUpdate();
+
+  // Don't bother if we're about to send a real update
+  if (nextUpdate == 0)
+    return;
+
+  // FIXME: Bandwidth estimation without congestion control
+  bandwidth = congestion.getBandwidth();
+
+  // FIXME: Hard coded value for maximum CPU throughput
+  if (bandwidth > 5000000)
+    bandwidth = 5000000;
+
+  maxUpdateSize = bandwidth * nextUpdate / 1000;
+
+  writeRTTPing();
+
+  encodeManager.writeLosslessRefresh(req, server->getPixelBuffer(),
+                                     cursor, maxUpdateSize);
+
+  writeRTTPing();
+
+  requested.clear();
+}
+
 
 void VNCSConnectionST::screenLayoutChange(rdr::U16 reason)
 {
@@ -1122,7 +1063,7 @@
     return;
 
   client.setDimensions(client.width(), client.height(),
-                       server->screenLayout);
+                       server->getScreenLayout());
 
   if (state() != RFBSTATE_NORMAL)
     return;
@@ -1145,7 +1086,7 @@
     client.setCursor(emptyCursor);
     clientHasCursor = false;
   } else {
-    client.setCursor(*server->cursor);
+    client.setCursor(*server->getCursor());
     clientHasCursor = true;
   }
 
@@ -1178,43 +1119,8 @@
 void VNCSConnectionST::setSocketTimeouts()
 {
   int timeoutms = rfb::Server::clientWaitTimeMillis;
-  soonestTimeout(&timeoutms, secsToMillis(rfb::Server::idleTimeout));
   if (timeoutms == 0)
     timeoutms = -1;
   sock->inStream().setTimeout(timeoutms);
   sock->outStream().setTimeout(timeoutms);
 }
-
-char* VNCSConnectionST::getStartTime()
-{
-  char* result = ctime(&startTime);
-  result[24] = '\0';
-  return result; 
-}
-
-void VNCSConnectionST::setStatus(int status)
-{
-  switch (status) {
-  case 0:
-    accessRights = accessRights | AccessPtrEvents | AccessKeyEvents | AccessView;
-    break;
-  case 1:
-    accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents)) | AccessView;
-    break;
-  case 2:
-    accessRights = accessRights & ~(AccessPtrEvents | AccessKeyEvents | AccessView);
-    break;
-  }
-  framebufferUpdateRequest(server->pb->getRect(), false);
-}
-int VNCSConnectionST::getStatus()
-{
-  if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0007)
-    return 0;
-  if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0001)
-    return 1;
-  if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0000)
-    return 2;
-  return 4;
-}
-
diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h
index dfc8bcb..662d9f3 100644
--- a/common/rfb/VNCSConnectionST.h
+++ b/common/rfb/VNCSConnectionST.h
@@ -43,21 +43,18 @@
     VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse);
     virtual ~VNCSConnectionST();
 
+    // SConnection methods
+
+    virtual bool accessCheck(AccessRights ar) const;
+    virtual void close(const char* reason);
+
     // Methods called from VNCServerST.  None of these methods ever knowingly
     // throw an exception.
 
-    // Unless otherwise stated, the SConnectionST may not be valid after any of
-    // these methods are called, since they catch exceptions and may have
-    // called close() which deletes the object.
-
     // init() must be called to initialise the protocol.  If it fails it
     // returns false, and close() will have been called.
     bool init();
 
-    // close() shuts down the socket to the client and deletes the
-    // SConnectionST object.
-    void close(const char* reason);
-
     // processMessages() processes incoming messages from the client, invoking
     // various callbacks as a result.  It continues to process messages until
     // reading might block.  shutdown() will be called on the connection's
@@ -78,14 +75,9 @@
     void serverCutTextOrClose(const char *str, int len);
     void setDesktopNameOrClose(const char *name);
     void setLEDStateOrClose(unsigned int state);
+    void approveConnectionOrClose(bool accept, const char* reason);
 
-    // checkIdleTimeout() returns the number of milliseconds left until the
-    // idle timeout expires.  If it has expired, the connection is closed and
-    // zero is returned.  Zero is also returned if there is no idle timeout.
-    int checkIdleTimeout();
-
-    // The following methods never throw exceptions nor do they ever delete the
-    // SConnectionST object.
+    // The following methods never throw exceptions
 
     // getComparerState() returns if this client would like the framebuffer
     // comparer to be enabled.
@@ -103,32 +95,18 @@
     bool needRenderedCursor();
 
     network::Socket* getSock() { return sock; }
+
+    // Change tracking
+
     void add_changed(const Region& region) { updates.add_changed(region); }
     void add_copied(const Region& dest, const Point& delta) {
       updates.add_copied(dest, delta);
     }
 
-    const char* getPeerEndpoint() const {return peerEndpoint.buf;}
-
-    // approveConnectionOrClose() is called some time after
-    // VNCServerST::queryConnection() has returned with PENDING to accept or
-    // reject the connection.  The accept argument should be true for
-    // acceptance, or false for rejection, in which case a string reason may
-    // also be given.
-
-    void approveConnectionOrClose(bool accept, const char* reason);
-
-    char* getStartTime();
-
-    void setStatus(int status);
-    int getStatus();
-
   private:
     // SConnection callbacks
 
-    // These methods are invoked as callbacks from processMsg().  Note that
-    // none of these methods should call any of the above methods which may
-    // delete the SConnectionST object.
+    // These methods are invoked as callbacks from processMsg()
 
     virtual void authSuccess();
     virtual void queryConnection(const char* userName);
@@ -148,12 +126,6 @@
     virtual void supportsContinuousUpdates();
     virtual void supportsLEDState();
 
-    // setAccessRights() allows a security package to limit the access rights
-    // of a VNCSConnectioST to the server.  These access rights are applied
-    // such that the actual rights granted are the minimum of the server's
-    // default access settings and the connection's access settings.
-    virtual void setAccessRights(AccessRights ar) {accessRights=ar;}
-
     // Timer callbacks
     virtual bool handleTimeout(Timer* t);
 
@@ -171,6 +143,7 @@
     void writeFramebufferUpdate();
     void writeNoDataUpdate();
     void writeDataUpdate();
+    void writeLosslessRefresh();
 
     void screenLayoutChange(rdr::U16 reason);
     void setCursor();
@@ -178,6 +151,7 @@
     void setLEDState(unsigned int state);
     void setSocketTimeouts();
 
+  private:
     network::Socket* sock;
     CharArray peerEndpoint;
     bool reverseConnection;
@@ -204,15 +178,13 @@
 
     std::map<rdr::U32, rdr::U32> pressedKeys;
 
-    time_t lastEventTime;
+    Timer idleTimer;
+
     time_t pointerEventTime;
     Point pointerEventPos;
     bool clientHasCursor;
 
-    AccessRights accessRights;
-
     CharArray closeReason;
-    time_t startTime;
   };
 }
 #endif
diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h
index c5335ad..298326f 100644
--- a/common/rfb/VNCServer.h
+++ b/common/rfb/VNCServer.h
@@ -22,13 +22,16 @@
 #ifndef __RFB_VNCSERVER_H__
 #define __RFB_VNCSERVER_H__
 
+#include <network/Socket.h>
+
 #include <rfb/UpdateTracker.h>
 #include <rfb/SSecurity.h>
 #include <rfb/ScreenSet.h>
 
 namespace rfb {
 
-  class VNCServer : public UpdateTracker {
+  class VNCServer : public UpdateTracker,
+                    public network::SocketServer {
   public:
     // blockUpdates()/unblockUpdates() tells the server that the pixel buffer
     // is currently in flux and may not be accessed. The attributes of the
@@ -50,7 +53,7 @@
     virtual void setScreenLayout(const ScreenSet& layout) = 0;
 
     // getPixelBuffer() returns a pointer to the PixelBuffer object.
-    virtual PixelBuffer* getPixelBuffer() const = 0;
+    virtual const PixelBuffer* getPixelBuffer() const = 0;
 
     // serverCutText() tells the server that the cut text has changed.  This
     // will normally be sent to all clients.
@@ -59,10 +62,22 @@
     // bell() tells the server that it should make all clients make a bell sound.
     virtual void bell() = 0;
 
+    // approveConnection() is called some time after
+    // SDesktop::queryConnection() has been called, to accept or reject
+    // the connection.  The accept argument should be true for
+    // acceptance, or false for rejection, in which case a string
+    // reason may also be given.
+    virtual void approveConnection(network::Socket* sock, bool accept,
+                                   const char* reason = NULL) = 0;
+
     // - Close all currently-connected clients, by calling
     //   their close() method with the supplied reason.
     virtual void closeClients(const char* reason) = 0;
 
+    // getConnection() gets the SConnection for a particular Socket.  If
+    // the Socket is not recognised then null is returned.
+    virtual SConnection* getConnection(network::Socket* sock) = 0;
+
     // setCursor() tells the server that the cursor has changed.  The
     // cursorData argument contains width*height rgba quadruplets with
     // non-premultiplied alpha.
diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx
index a9ae62e..c95c14f 100644
--- a/common/rfb/VNCServerST.cxx
+++ b/common/rfb/VNCServerST.cxx
@@ -53,7 +53,7 @@
 
 #include <rfb/ComparingUpdateTracker.h>
 #include <rfb/KeyRemapper.h>
-#include <rfb/ListConnInfo.h>
+#include <rfb/LogWriter.h>
 #include <rfb/Security.h>
 #include <rfb/ServerCore.h>
 #include <rfb/VNCServerST.h>
@@ -66,7 +66,7 @@
 using namespace rfb;
 
 static LogWriter slog("VNCServerST");
-LogWriter VNCServerST::connectionsLog("Connections");
+static LogWriter connectionsLog("Connections");
 
 //
 // -=- VNCServerST Implementation
@@ -80,12 +80,17 @@
     name(strDup(name_)), pointerClient(0), comparer(0),
     cursor(new Cursor(0, 0, Point(), NULL)),
     renderedCursorInvalid(false),
-    queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
-    lastConnectionTime(0), disableclients(false),
+    keyRemapper(&KeyRemapper::defInstance),
+    idleTimer(this), disconnectTimer(this), connectTimer(this),
     frameTimer(this)
 {
-  lastUserInputTime = lastDisconnectTime = time(0);
   slog.debug("creating single-threaded server %s", name.buf);
+
+  // FIXME: Do we really want to kick off these right away?
+  if (rfb::Server::maxIdleTime)
+    idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
+  if (rfb::Server::maxDisconnectionTime)
+    disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime));
 }
 
 VNCServerST::~VNCServerST()
@@ -99,9 +104,11 @@
   stopFrameClock();
 
   // Delete all the clients, and their sockets, and any closing sockets
-  //   NB: Deleting a client implicitly removes it from the clients list
   while (!clients.empty()) {
-    delete clients.front();
+    VNCSConnectionST* client;
+    client = clients.front();
+    clients.pop_front();
+    delete client;
   }
 
   // Stop the desktop object if active, *only* after deleting all clients!
@@ -125,8 +132,13 @@
   if (blHosts->isBlackmarked(address.buf)) {
     connectionsLog.error("blacklisted: %s", address.buf);
     try {
-      SConnection::writeConnFailedFromScratch("Too many security failures",
-                                              &sock->outStream());
+      rdr::OutStream& os = sock->outStream();
+
+      // Shortest possible way to tell a client it is not welcome
+      os.writeBytes("RFB 003.003\n", 12);
+      os.writeU32(0);
+      os.writeString("Too many security failures");
+      os.flush();
     } catch (rdr::Exception&) {
     }
     sock->shutdown();
@@ -134,11 +146,17 @@
     return;
   }
 
-  if (clients.empty()) {
-    lastConnectionTime = time(0);
-  }
+  CharArray name;
+  name.buf = sock->getPeerEndpoint();
+  connectionsLog.status("accepted: %s", name.buf);
+
+  // Adjust the exit timers
+  if (rfb::Server::maxConnectionTime && clients.empty())
+    connectTimer.start(secsToMillis(rfb::Server::maxConnectionTime));
+  disconnectTimer.stop();
 
   VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing);
+  clients.push_front(client);
   client->init();
 }
 
@@ -147,9 +165,24 @@
   std::list<VNCSConnectionST*>::iterator ci;
   for (ci = clients.begin(); ci != clients.end(); ci++) {
     if ((*ci)->getSock() == sock) {
+      clients.remove(*ci);
+
+      // - Release the cursor if this client owns it
+      if (pointerClient == *ci)
+        pointerClient = NULL;
+
+      // Adjust the exit timers
+      connectTimer.stop();
+      if (rfb::Server::maxDisconnectionTime && clients.empty())
+        disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime));
+
       // - Delete the per-Socket resources
       delete *ci;
 
+      CharArray name;
+      name.buf = sock->getPeerEndpoint();
+      connectionsLog.status("closed: %s", name.buf);
+
       // - Check that the desktop object is still required
       if (authClientCount() == 0)
         stopDesktop();
@@ -191,89 +224,6 @@
   throw rdr::Exception("invalid Socket in VNCServerST");
 }
 
-int VNCServerST::checkTimeouts()
-{
-  int timeout = 0;
-  std::list<VNCSConnectionST*>::iterator ci, ci_next;
-
-  soonestTimeout(&timeout, Timer::checkTimeouts());
-
-  for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
-    ci_next = ci; ci_next++;
-    soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
-  }
-
-  int timeLeft;
-  time_t now = time(0);
-
-  // Check MaxDisconnectionTime 
-  if (rfb::Server::maxDisconnectionTime && clients.empty()) {
-    if (now < lastDisconnectTime) {
-      // Someone must have set the time backwards. 
-      slog.info("Time has gone backwards - resetting lastDisconnectTime");
-      lastDisconnectTime = now;
-    }
-    timeLeft = lastDisconnectTime + rfb::Server::maxDisconnectionTime - now;
-    if (timeLeft < -60) {
-      // Someone must have set the time forwards.
-      slog.info("Time has gone forwards - resetting lastDisconnectTime");
-      lastDisconnectTime = now;
-      timeLeft = rfb::Server::maxDisconnectionTime;
-    }
-    if (timeLeft <= 0) { 
-      slog.info("MaxDisconnectionTime reached, exiting");
-      exit(0);
-    }
-    soonestTimeout(&timeout, timeLeft * 1000);
-  }
-
-  // Check MaxConnectionTime 
-  if (rfb::Server::maxConnectionTime && lastConnectionTime && !clients.empty()) {
-    if (now < lastConnectionTime) {
-      // Someone must have set the time backwards. 
-      slog.info("Time has gone backwards - resetting lastConnectionTime");
-      lastConnectionTime = now;
-    }
-    timeLeft = lastConnectionTime + rfb::Server::maxConnectionTime - now;
-    if (timeLeft < -60) {
-      // Someone must have set the time forwards.
-      slog.info("Time has gone forwards - resetting lastConnectionTime");
-      lastConnectionTime = now;
-      timeLeft = rfb::Server::maxConnectionTime;
-    }
-    if (timeLeft <= 0) {
-      slog.info("MaxConnectionTime reached, exiting");
-      exit(0);
-    }
-    soonestTimeout(&timeout, timeLeft * 1000);
-  }
-
-  
-  // Check MaxIdleTime 
-  if (rfb::Server::maxIdleTime) {
-    if (now < lastUserInputTime) {
-      // Someone must have set the time backwards. 
-      slog.info("Time has gone backwards - resetting lastUserInputTime");
-      lastUserInputTime = now;
-    }
-    timeLeft = lastUserInputTime + rfb::Server::maxIdleTime - now;
-    if (timeLeft < -60) {
-      // Someone must have set the time forwards.
-      slog.info("Time has gone forwards - resetting lastUserInputTime");
-      lastUserInputTime = now;
-      timeLeft = rfb::Server::maxIdleTime;
-    }
-    if (timeLeft <= 0) {
-      slog.info("MaxIdleTime reached, exiting");
-      exit(0);
-    }
-    soonestTimeout(&timeout, timeLeft * 1000);
-  }
-  
-  return timeout;
-}
-
-
 // VNCServer methods
 
 void VNCServerST::blockUpdates()
@@ -470,6 +420,83 @@
   }
 }
 
+// Event handlers
+
+void VNCServerST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
+{
+  if (rfb::Server::maxIdleTime)
+    idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
+
+  // Remap the key if required
+  if (keyRemapper) {
+    rdr::U32 newkey;
+    newkey = keyRemapper->remapKey(keysym);
+    if (newkey != keysym) {
+      slog.debug("Key remapped to 0x%x", newkey);
+      keysym = newkey;
+    }
+  }
+
+  desktop->keyEvent(keysym, keycode, down);
+}
+
+void VNCServerST::pointerEvent(VNCSConnectionST* client,
+                               const Point& pos, int buttonMask)
+{
+  if (rfb::Server::maxIdleTime)
+    idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
+
+  // Let one client own the cursor whilst buttons are pressed in order
+  // to provide a bit more sane user experience
+  if ((pointerClient != NULL) && (pointerClient != client))
+    return;
+
+  if (buttonMask)
+    pointerClient = client;
+  else
+    pointerClient = NULL;
+
+  desktop->pointerEvent(pos, buttonMask);
+}
+
+void VNCServerST::clientCutText(const char* str, int len)
+{
+  desktop->clientCutText(str, len);
+}
+
+unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester,
+                                         int fb_width, int fb_height,
+                                         const ScreenSet& layout)
+{
+  unsigned int result;
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+
+  // Don't bother the desktop with an invalid configuration
+  if (!layout.validate(fb_width, fb_height))
+    return resultInvalid;
+
+  // FIXME: the desktop will call back to VNCServerST and an extra set
+  // of ExtendedDesktopSize messages will be sent. This is okay
+  // protocol-wise, but unnecessary.
+  result = desktop->setScreenLayout(fb_width, fb_height, layout);
+  if (result != resultSuccess)
+    return result;
+
+  // Sanity check
+  if (screenLayout != layout)
+    throw Exception("Desktop configured a different screen layout than requested");
+
+  // Notify other clients
+  for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
+    ci_next = ci; ci_next++;
+    if ((*ci) == requester)
+      continue;
+    (*ci)->screenLayoutChangeOrClose(reasonOtherClient);
+  }
+
+  return resultSuccess;
+}
+
 // Other public methods
 
 void VNCServerST::approveConnection(network::Socket* sock, bool accept,
@@ -507,7 +534,7 @@
   }
 }
 
-SConnection* VNCServerST::getSConnection(network::Socket* sock) {
+SConnection* VNCServerST::getConnection(network::Socket* sock) {
   std::list<VNCSConnectionST*>::iterator ci;
   for (ci = clients.begin(); ci != clients.end(); ci++) {
     if ((*ci)->getSock() == sock)
@@ -532,11 +559,77 @@
     }
 
     return true;
+  } else if (t == &idleTimer) {
+    slog.info("MaxIdleTime reached, exiting");
+    desktop->terminate();
+  } else if (t == &disconnectTimer) {
+    slog.info("MaxDisconnectionTime reached, exiting");
+    desktop->terminate();
+  } else if (t == &connectTimer) {
+    slog.info("MaxConnectionTime reached, exiting");
+    desktop->terminate();
   }
 
   return false;
 }
 
+void VNCServerST::queryConnection(VNCSConnectionST* client,
+                                  const char* userName)
+{
+  // - Authentication succeeded - clear from blacklist
+  CharArray name;
+  name.buf = client->getSock()->getPeerAddress();
+  blHosts->clearBlackmark(name.buf);
+
+  // - Prepare the desktop for that the client will start requiring
+  // resources after this
+  startDesktop();
+
+  // - Special case to provide a more useful error message
+  if (rfb::Server::neverShared &&
+      !rfb::Server::disconnectClients &&
+      authClientCount() > 0) {
+    approveConnection(client->getSock(), false,
+                      "The server is already in use");
+    return;
+  }
+
+  // - Are we configured to do queries?
+  if (!rfb::Server::queryConnect &&
+      !client->getSock()->requiresQuery()) {
+    approveConnection(client->getSock(), true, NULL);
+    return;
+  }
+
+  // - Does the client have the right to bypass the query?
+  if (client->accessCheck(SConnection::AccessNoQuery))
+  {
+    approveConnection(client->getSock(), true, NULL);
+    return;
+  }
+
+  desktop->queryConnection(client->getSock(), userName);
+}
+
+void VNCServerST::clientReady(VNCSConnectionST* client, bool shared)
+{
+  if (!shared) {
+    if (rfb::Server::disconnectClients &&
+        client->accessCheck(SConnection::AccessNonShared)) {
+      // - Close all the other connected clients
+      slog.debug("non-shared connection - closing clients");
+      closeClients("Non-shared connection requested", client->getSock());
+    } else {
+      // - Refuse this connection if there are existing clients, in addition to
+      // this one
+      if (authClientCount() > 1) {
+        client->close("Server is already in use");
+        return;
+      }
+    }
+  }
+}
+
 // -=- Internal methods
 
 void VNCServerST::startDesktop()
@@ -692,50 +785,6 @@
   return &renderedCursor;
 }
 
-void VNCServerST::getConnInfo(ListConnInfo * listConn)
-{
-  listConn->Clear();
-  listConn->setDisable(getDisable());
-  if (clients.empty())
-    return;
-  std::list<VNCSConnectionST*>::iterator i;
-  for (i = clients.begin(); i != clients.end(); i++)
-    listConn->addInfo((void*)(*i), (*i)->getSock()->getPeerAddress(),
-                      (*i)->getStartTime(), (*i)->getStatus());
-}
-
-void VNCServerST::setConnStatus(ListConnInfo* listConn)
-{
-  setDisable(listConn->getDisable());
-  if (listConn->Empty() || clients.empty()) return;
-  for (listConn->iBegin(); !listConn->iEnd(); listConn->iNext()) {
-    VNCSConnectionST* conn = (VNCSConnectionST*)listConn->iGetConn();
-    std::list<VNCSConnectionST*>::iterator i;
-    for (i = clients.begin(); i != clients.end(); i++) {
-      if ((*i) == conn) {
-        int status = listConn->iGetStatus();
-        if (status == 3) {
-          (*i)->close(0);
-        } else {
-          (*i)->setStatus(status);
-        }
-        break;
-      }
-    }
-  }
-}
-
-void VNCServerST::notifyScreenLayoutChange(VNCSConnectionST* requester)
-{
-  std::list<VNCSConnectionST*>::iterator ci, ci_next;
-  for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
-    ci_next = ci; ci_next++;
-    if ((*ci) == requester)
-      continue;
-    (*ci)->screenLayoutChangeOrClose(reasonOtherClient);
-  }
-}
-
 bool VNCServerST::getComparerState()
 {
   if (rfb::Server::compareFB == 0)
diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h
index b7845dd..43a3bb9 100644
--- a/common/rfb/VNCServerST.h
+++ b/common/rfb/VNCServerST.h
@@ -28,11 +28,9 @@
 
 #include <rfb/SDesktop.h>
 #include <rfb/VNCServer.h>
-#include <rfb/LogWriter.h>
 #include <rfb/Blacklist.h>
 #include <rfb/Cursor.h>
 #include <rfb/Timer.h>
-#include <network/Socket.h>
 #include <rfb/ScreenSet.h>
 
 namespace rfb {
@@ -44,8 +42,7 @@
   class KeyRemapper;
 
   class VNCServerST : public VNCServer,
-                      public Timer::Callback,
-                      public network::SocketServer {
+                      public Timer::Callback {
   public:
     // -=- Constructors
 
@@ -79,12 +76,6 @@
     //   Flush pending data from the Socket on to the network.
     virtual void processSocketWriteEvent(network::Socket* sock);
 
-    // checkTimeouts
-    //   Returns the number of milliseconds left until the next idle timeout
-    //   expires.  If any have already expired, the corresponding connections
-    //   are closed.  Zero is returned if there is no idle timeout.
-    virtual int checkTimeouts();
-
 
     // Methods overridden from VNCServer
 
@@ -93,102 +84,70 @@
     virtual void setPixelBuffer(PixelBuffer* pb, const ScreenSet& layout);
     virtual void setPixelBuffer(PixelBuffer* pb);
     virtual void setScreenLayout(const ScreenSet& layout);
-    virtual PixelBuffer* getPixelBuffer() const { return pb; }
+    virtual const PixelBuffer* getPixelBuffer() const { return pb; }
     virtual void serverCutText(const char* str, int len);
+
+    virtual void approveConnection(network::Socket* sock, bool accept,
+                                   const char* reason);
+    virtual void closeClients(const char* reason) {closeClients(reason, 0);}
+    virtual SConnection* getConnection(network::Socket* sock);
+
     virtual void add_changed(const Region &region);
     virtual void add_copied(const Region &dest, const Point &delta);
     virtual void setCursor(int width, int height, const Point& hotspot,
                            const rdr::U8* data);
     virtual void setCursorPos(const Point& p);
+    virtual void setName(const char* name_);
     virtual void setLEDState(unsigned state);
 
     virtual void bell();
 
-    // - Close all currently-connected clients, by calling
-    //   their close() method with the supplied reason.
-    virtual void closeClients(const char* reason) {closeClients(reason, 0);}
-
     // VNCServerST-only methods
 
+    // Methods to get the currently set server state
+
+    const ScreenSet& getScreenLayout() const { return screenLayout; }
+    const Cursor* getCursor() const { return cursor; }
+    const Point& getCursorPos() const { return cursorPos; }
+    const char* getName() const { return name.buf; }
+    unsigned getLEDState() const { return ledState; }
+
+    // Event handlers
+    void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
+    void pointerEvent(VNCSConnectionST* client, const Point& pos, int buttonMask);
+    void clientCutText(const char* str, int len);
+
+    unsigned int setDesktopSize(VNCSConnectionST* requester,
+                                int fb_width, int fb_height,
+                                const ScreenSet& layout);
+
     // closeClients() closes all RFB sessions, except the specified one (if
     // any), and logs the specified reason for closure.
     void closeClients(const char* reason, network::Socket* sock);
 
-    // getSConnection() gets the SConnection for a particular Socket.  If
-    // the Socket is not recognised then null is returned.
+    // queryConnection() does some basic checks and then passes on the
+    // request to the desktop.
+    void queryConnection(VNCSConnectionST* client, const char* userName);
 
-    SConnection* getSConnection(network::Socket* sock);
+    // clientReady() is called by a VNCSConnectionST instance when the
+    // client has completed the handshake and is ready for normal
+    // communication.
+    void clientReady(VNCSConnectionST* client, bool shared);
 
-    // getName() returns the name of this VNC Server.  NB: The value returned
-    // is the server's internal buffer which may change after any other methods
-    // are called - take a copy if necessary.
-    const char* getName() const {return name.buf;}
+    // Estimated time until the next time new updates will be pushed
+    // to clients
+    int msToNextUpdate();
 
-    // setName() specifies the desktop name that the server should provide to
-    // clients
-    virtual void setName(const char* name_);
+    // Part of the framebuffer that has been modified but is not yet
+    // ready to be sent to clients
+    Region getPendingRegion();
 
-    // A QueryConnectionHandler, if supplied, is passed details of incoming
-    // connections to approve, reject, or query the user about.
-    //
-    // queryConnection() is called when a connection has been
-    // successfully authenticated.  The sock and userName arguments identify
-    // the socket and the name of the authenticated user, if any.  It should
-    // return ACCEPT if the connection should be accepted, REJECT if it should
-    // be rejected, or PENDING if a decision cannot yet be reached.  If REJECT
-    // is returned, *reason can be set to a string describing the reason - this
-    // will be delete[]ed when it is finished with.  If PENDING is returned,
-    // approveConnection() must be called some time later to accept or reject
-    // the connection.
-    enum queryResult { ACCEPT, REJECT, PENDING };
-    struct QueryConnectionHandler {
-      virtual ~QueryConnectionHandler() {}
-      virtual queryResult queryConnection(network::Socket* sock,
-                                          const char* userName,
-                                          char** reason) = 0;
-    };
-    void setQueryConnectionHandler(QueryConnectionHandler* qch) {
-      queryConnectionHandler = qch;
-    }
-
-    // queryConnection is called as described above, and either passes the
-    // request on to the registered handler, or accepts the connection if
-    // no handler has been specified.
-    virtual queryResult queryConnection(network::Socket* sock,
-                                        const char* userName,
-                                        char** reason) {
-      return queryConnectionHandler
-        ? queryConnectionHandler->queryConnection(sock, userName, reason)
-        : ACCEPT;
-    }
-
-    // approveConnection() is called by the active QueryConnectionHandler,
-    // some time after queryConnection() has returned with PENDING, to accept
-    // or reject the connection.  The accept argument should be true for
-    // acceptance, or false for rejection, in which case a string reason may
-    // also be given.
-    void approveConnection(network::Socket* sock, bool accept,
-                           const char* reason);
-
-    // setBlacklist() is called to replace the VNCServerST's internal
-    // Blacklist instance with another instance.  This allows a single
-    // Blacklist to be shared by multiple VNCServerST instances.
-    void setBlacklist(Blacklist* bl) {blHosts = bl ? bl : &blacklist;}
-
-    // setKeyRemapper() replaces the VNCServerST's default key remapper.
-    // NB: A null pointer is valid here.
-    void setKeyRemapper(KeyRemapper* kr) { keyRemapper = kr; }
-
-    void getConnInfo(ListConnInfo * listConn);
-    void setConnStatus(ListConnInfo* listConn);
-
-    bool getDisable() { return disableclients;};
-    void setDisable(bool disable) { disableclients = disable;};
+    // getRenderedCursor() returns an up to date version of the server
+    // side rendered cursor buffer
+    const RenderedCursor* getRenderedCursor();
 
   protected:
 
-    friend class VNCSConnectionST;
-
     // Timer callbacks
     virtual bool handleTimeout(Timer* t);
 
@@ -197,7 +156,17 @@
     void startDesktop();
     void stopDesktop();
 
-    static LogWriter connectionsLog;
+    // - Check how many of the clients are authenticated.
+    int authClientCount();
+
+    bool needRenderedCursor();
+    void startFrameClock();
+    void stopFrameClock();
+    void writeUpdate();
+
+    bool getComparerState();
+
+  protected:
     Blacklist blacklist;
     Blacklist* blHosts;
 
@@ -221,29 +190,11 @@
     RenderedCursor renderedCursor;
     bool renderedCursorInvalid;
 
-    // - Check how many of the clients are authenticated.
-    int authClientCount();
-
-    bool needRenderedCursor();
-    void startFrameClock();
-    void stopFrameClock();
-    int msToNextUpdate();
-    void writeUpdate();
-    Region getPendingRegion();
-    const RenderedCursor* getRenderedCursor();
-
-    void notifyScreenLayoutChange(VNCSConnectionST *requester);
-
-    bool getComparerState();
-
-    QueryConnectionHandler* queryConnectionHandler;
     KeyRemapper* keyRemapper;
 
-    time_t lastUserInputTime;
-    time_t lastDisconnectTime;
-    time_t lastConnectionTime;
-
-    bool disableclients;
+    Timer idleTimer;
+    Timer disconnectTimer;
+    Timer connectTimer;
 
     Timer frameTimer;
   };
diff --git a/contrib/packages/deb/ubuntu-trusty/debian/rules b/contrib/packages/deb/ubuntu-trusty/debian/rules
index 81f4f89..ea9f143 100755
--- a/contrib/packages/deb/ubuntu-trusty/debian/rules
+++ b/contrib/packages/deb/ubuntu-trusty/debian/rules
@@ -151,7 +151,7 @@
 	#  anything for this package.
 	#/usr/bin/docbook-to-man debian/vnc.sgml > vnc.1
 	(cd media;make)
-	(cd java;cmake -G"Unix Makefiles";make)
+	#(cd java;cmake -G"Unix Makefiles";make)
 
 	touch build-indep-stamp
 
@@ -213,8 +213,8 @@
 	mv $(CURDIR)/debian/xtigervncviewer/usr/share/man/man1/vncviewer.1 \
 		$(CURDIR)/debian/xtigervncviewer/usr/share/man/man1/xtigervncviewer.1
 	# tigervnc-java
-	mkdir -p $(CURDIR)/debian/tigervnc-java/usr/share
-	(cd java; make install DESTDIR=$(CURDIR)/debian/tigervnc-java/usr/share)
+	#mkdir -p $(CURDIR)/debian/tigervnc-java/usr/share
+	#(cd java; make install DESTDIR=$(CURDIR)/debian/tigervnc-java/usr/share)
 	#dh_movefiles
 
 # Build architecture-independent files here.
diff --git a/contrib/packages/deb/ubuntu-xenial/debian/rules b/contrib/packages/deb/ubuntu-xenial/debian/rules
index 531a1c4..7509e7b 100644
--- a/contrib/packages/deb/ubuntu-xenial/debian/rules
+++ b/contrib/packages/deb/ubuntu-xenial/debian/rules
@@ -138,7 +138,7 @@
 	#  anything for this package.
 	#/usr/bin/docbook-to-man debian/vnc.sgml > vnc.1
 	(cd media;make)
-	(cd java;cmake -G"Unix Makefiles";make)
+	#(cd java;cmake -G"Unix Makefiles";make)
 
 	touch build-indep-stamp
 
@@ -204,15 +204,15 @@
 	mv $(CURDIR)/debian/tigervncserver/usr/share/icons \
 		$(CURDIR)/debian/xtigervncviewer/usr/share/
 	# tigervnc-java
-	mkdir -p $(CURDIR)/debian/tigervnc-java/usr/share
-	(cd java; make install DESTDIR=$(CURDIR)/debian/tigervnc-java/usr/share)
+	#mkdir -p $(CURDIR)/debian/tigervnc-java/usr/share
+	#(cd java; make install DESTDIR=$(CURDIR)/debian/tigervnc-java/usr/share)
 	# install additional license files
 	mkdir -p $(CURDIR)/debian/xtigervncviewer/usr/share/doc/xtigervncviewer
 	cp $(CURDIR)/debian/tigervncserver/usr/share/doc/tigervnc-*/* \
 		$(CURDIR)/debian/xtigervncviewer/usr/share/doc/xtigervncviewer/
-	mkdir -p $(CURDIR)/debian/tigervnc-java/usr/share/doc/tigervnc-java
-	cp $(CURDIR)/debian/tigervncserver/usr/share/doc/tigervnc-*/* \
-		$(CURDIR)/debian/tigervnc-java/usr/share/doc/tigervnc-java/
+	#mkdir -p $(CURDIR)/debian/tigervnc-java/usr/share/doc/tigervnc-java
+	#cp $(CURDIR)/debian/tigervncserver/usr/share/doc/tigervnc-*/* \
+		#$(CURDIR)/debian/tigervnc-java/usr/share/doc/tigervnc-java/
 	mkdir -p $(CURDIR)/debian/tigervncserver/usr/share/doc/tigervncserver
 	mv $(CURDIR)/debian/tigervncserver/usr/share/doc/tigervnc-*/* \
 		$(CURDIR)/debian/tigervncserver/usr/share/doc/tigervncserver/
diff --git a/contrib/packages/rpm/el7/SPECS/tigervnc.spec b/contrib/packages/rpm/el7/SPECS/tigervnc.spec
index 1b8630a..77db826 100644
--- a/contrib/packages/rpm/el7/SPECS/tigervnc.spec
+++ b/contrib/packages/rpm/el7/SPECS/tigervnc.spec
@@ -9,8 +9,8 @@
 %endif
 
 Name:           tigervnc
-Version:        1.6.80
-Release:        1%{?snap:.%{snap}}%{?dist}
+Version:        1.9.80
+Release:        2%{?snap:.%{snap}}%{?dist}
 Summary:        A TigerVNC remote display system
 
 Group:          User Interface/Desktops
@@ -151,7 +151,7 @@
 for all in `find . -type f -perm -001`; do
         chmod -x "$all"
 done
-patch -p1 -b --suffix .vnc < ../xserver119.patch
+patch -p1 -b --suffix .vnc < ../xserver120.patch
 popd
 
 # Don't use shebang in vncserver script.
@@ -219,7 +219,7 @@
         --with-fontdir=%{_datadir}/X11/fonts \
         --with-xkb-output=%{_localstatedir}/lib/xkb \
         --enable-install-libxf86config \
-        --enable-glx --disable-dri --enable-dri2 \
+        --enable-glx --disable-dri --enable-dri2 --disable-dri3 \
         --disable-wayland \
         --disable-present \
         --disable-config-dbus \
@@ -354,6 +354,9 @@
 %endif
 
 %changelog
+* Sun Nov 26 2018 Brian P. Hinz <bphinz@users.sourceforge.net> 1.9.80-2
+- Bumped Xorg version to 1.2.0
+
 * Sun Jul 22 2018 Brian P. Hinz <bphinz@users.sourceforge.net> 1.9.80-1
 - Updated fltk to latest version
 
diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt
index 23137f7..da24dcf 100644
--- a/java/CMakeLists.txt
+++ b/java/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 2.8.11)
 
 project(tigervnc-java Java)
 if(NOT VERSION)
@@ -25,13 +25,10 @@
 set(JAVA_TSA_URL NOTFOUND CACHE STRING "URL of Time Stamping Authority (TSA)")
 
 if(NOT BUILD)
-	execute_process(COMMAND "date" "+%Y%m%d" OUTPUT_VARIABLE BUILD)
+	STRING(TIMESTAMP BUILD "%Y%m%d" UTC)
 endif()
-execute_process(COMMAND "date" "+%b %d %Y" OUTPUT_VARIABLE JAVA_DATE)
-execute_process(COMMAND "date" "+%H:%M:%S" OUTPUT_VARIABLE JAVA_TIME)
-string(REGEX REPLACE "\n" "" JAVA_DATE ${JAVA_DATE})
-string(REGEX REPLACE "\n" "" JAVA_TIME ${JAVA_TIME})
-string(REGEX REPLACE "\n" "" BUILD ${BUILD})
+STRING(TIMESTAMP JAVA_DATE "%Y-%m-%d" UTC)
+STRING(TIMESTAMP JAVA_TIME "%H:%M:%S" UTC)
 
 set(JAVA_SOURCES "")
 set(JAVA_CLASSES "")
diff --git a/tests/encperf.cxx b/tests/encperf.cxx
index 6f9283b..6523eb7 100644
--- a/tests/encperf.cxx
+++ b/tests/encperf.cxx
@@ -421,8 +421,9 @@
   }
 
   int runCount = count;
-  struct stats runs[runCount];
-  double values[runCount], dev[runCount];
+  struct stats *runs = new struct stats[runCount];
+  double *values = new double[runCount];
+  double *dev = new double[runCount];
   double median, meddev;
 
   if (fn == NULL) {
diff --git a/unix/tx/TXWindow.cxx b/unix/tx/TXWindow.cxx
index a681917..6129840 100644
--- a/unix/tx/TXWindow.cxx
+++ b/unix/tx/TXWindow.cxx
@@ -24,6 +24,7 @@
 #include <list>
 #include <stdio.h>
 #include <stdlib.h>
+#include <vector>
 #include <rfb/util.h>
 
 std::list<TXWindow*> windows;
@@ -132,20 +133,20 @@
 
 void TXWindow::getColours(Display* dpy, XColor* cols, int nCols)
 {
-  bool* got = new bool[nCols];
+  std::vector<bool> got;
+
   bool failed = false;
   int i;
   for (i = 0; i < nCols; i++) {
     if (XAllocColor(dpy, cmap, &cols[i])) {
-      got[i] = true;
+      got.push_back(true);
     } else {
-      got[i] = false;
+      got.push_back(false);
       failed = true;
     }
   }
 
   if (!failed) {
-    delete [] got;
     return;
   }
 
@@ -168,12 +169,13 @@
   int cmapSize = DisplayCells(dpy,DefaultScreen(dpy));
 
   XColor* cm = new XColor[cmapSize];
-  bool* shared = new bool[cmapSize];
-  bool* usedAsNearest = new bool[cmapSize];
+  std::vector<bool> shared;
+  std::vector<bool> usedAsNearest;
 
   for (i = 0; i < cmapSize; i++) {
     cm[i].pixel = i;
-    shared[i] = usedAsNearest[i] = false;
+    shared.push_back(false);
+    usedAsNearest.push_back(false);
   }
 
   XQueryColors(dpy, cmap, cm, cmapSize);
diff --git a/unix/vncpasswd/vncpasswd.cxx b/unix/vncpasswd/vncpasswd.cxx
index 8bd4e48..3055223 100644
--- a/unix/vncpasswd/vncpasswd.cxx
+++ b/unix/vncpasswd/vncpasswd.cxx
@@ -134,7 +134,7 @@
     } else if (argv[i][0] == '-') {
       usage();
     } else if (!fname) {
-      fname = argv[i];
+      fname = strDup(argv[i]);
     } else {
       usage();
     }
@@ -165,24 +165,37 @@
     FILE* fp = fopen(fname,"w");
     if (!fp) {
       fprintf(stderr,"Couldn't open %s for writing\n",fname);
+      delete [] fname;
+      delete obfuscated;
+      delete obfuscatedReadOnly;
       exit(1);
     }
     chmod(fname, S_IRUSR|S_IWUSR);
 
     if (fwrite(obfuscated->buf, obfuscated->length, 1, fp) != 1) {
       fprintf(stderr,"Writing to %s failed\n",fname);
+      delete [] fname;
+      delete obfuscated;
+      delete obfuscatedReadOnly;
       exit(1);
     }
 
+    delete obfuscated;
+
     if (obfuscatedReadOnly) {
       if (fwrite(obfuscatedReadOnly->buf, obfuscatedReadOnly->length, 1, fp) != 1) {
         fprintf(stderr,"Writing to %s failed\n",fname);
+        delete [] fname;
+        delete obfuscatedReadOnly;
         exit(1);
       }
     }
 
     fclose(fp);
 
+    delete [] fname;
+    delete obfuscatedReadOnly;
+
     return 0;
   }
 }
diff --git a/unix/x0vncserver/XDesktop.cxx b/unix/x0vncserver/XDesktop.cxx
index e77e765..564b2d5 100644
--- a/unix/x0vncserver/XDesktop.cxx
+++ b/unix/x0vncserver/XDesktop.cxx
@@ -18,6 +18,12 @@
  * USA.
  */
 
+#include <assert.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <rfb/LogWriter.h>
+
 #include <x0vncserver/XDesktop.h>
 
 #include <X11/XKBlib.h>
@@ -53,6 +59,10 @@
                           "Send keyboard events straight through and "
                           "avoid mapping them to the current keyboard "
                           "layout", false);
+IntParameter queryConnectTimeout("QueryConnectTimeout",
+                                 "Number of seconds to show the Accept Connection dialog before "
+                                 "rejecting the connection",
+                                 10);
 
 static rfb::LogWriter vlog("XDesktop");
 
@@ -63,6 +73,7 @@
 
 XDesktop::XDesktop(Display* dpy_, Geometry *geometry_)
   : dpy(dpy_), geometry(geometry_), pb(0), server(0),
+    queryConnectDialog(0), queryConnectSock(0),
     oldButtonMask(0), haveXtest(false), haveDamage(false),
     maxButtons(0), running(false), ledMasks(), ledState(0),
     codeMap(0), codeMapLen(0)
@@ -227,7 +238,7 @@
   pb = new XPixelBuffer(dpy, factory, geometry->getRect());
   vlog.info("Allocated %s", pb->getImage()->classDesc());
 
-  server = (VNCServerST *)vs;
+  server = vs;
   server->setPixelBuffer(pb, computeScreenLayout());
 
 #ifdef HAVE_XDAMAGE
@@ -254,6 +265,9 @@
     XDamageDestroy(dpy, damage);
 #endif
 
+  delete queryConnectDialog;
+  queryConnectDialog = 0;
+
   server->setPixelBuffer(0);
   server = 0;
 
@@ -261,10 +275,38 @@
   pb = 0;
 }
 
+void XDesktop::terminate() {
+  kill(getpid(), SIGTERM);
+}
+
 bool XDesktop::isRunning() {
   return running;
 }
 
+void XDesktop::queryConnection(network::Socket* sock,
+                               const char* userName)
+{
+  assert(isRunning());
+
+  if (queryConnectSock) {
+    server->approveConnection(sock, false, "Another connection is currently being queried.");
+    return;
+  }
+
+  if (!userName)
+    userName = "(anonymous)";
+
+  queryConnectSock = sock;
+
+  CharArray address(sock->getPeerAddress());
+  delete queryConnectDialog;
+  queryConnectDialog = new QueryConnectDialog(dpy, address.buf,
+                                              userName,
+                                              queryConnectTimeout,
+                                              this);
+  queryConnectDialog->map();
+}
+
 void XDesktop::pointerEvent(const Point& pos, int buttonMask) {
 #ifdef HAVE_XTEST
   if (!haveXtest) return;
@@ -630,6 +672,8 @@
 
     dev = (XDamageNotifyEvent*)ev;
     rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height);
+    rect = rect.translate(Point(-geometry->offsetLeft(),
+                                -geometry->offsetTop()));
     server->add_changed(rect);
 
     return true;
@@ -703,6 +747,21 @@
   return false;
 }
 
+void XDesktop::queryApproved()
+{
+  assert(isRunning());
+  server->approveConnection(queryConnectSock, true, 0);
+  queryConnectSock = 0;
+}
+
+void XDesktop::queryRejected()
+{
+  assert(isRunning());
+  server->approveConnection(queryConnectSock, false,
+                            "Connection rejected by local user");
+  queryConnectSock = 0;
+}
+
 bool XDesktop::setCursor()
 {
   XFixesCursorImage *cim;
diff --git a/unix/x0vncserver/XDesktop.h b/unix/x0vncserver/XDesktop.h
index ff52c01..3e85aac 100644
--- a/unix/x0vncserver/XDesktop.h
+++ b/unix/x0vncserver/XDesktop.h
@@ -21,7 +21,7 @@
 #ifndef __XDESKTOP_H__
 #define __XDESKTOP_H__
 
-#include <rfb/VNCServerST.h>
+#include <rfb/SDesktop.h>
 #include <tx/TXWindow.h>
 #include <unixcommon.h>
 
@@ -30,13 +30,17 @@
 #include <X11/extensions/Xdamage.h>
 #endif
 
+#include <vncconfig/QueryConnectDialog.h>
+
 class Geometry;
 class XPixelBuffer;
 
 // number of XKb indicator leds to handle
 #define XDESKTOP_N_LEDS 3
 
-class XDesktop : public rfb::SDesktop, public TXGlobalEventHandler
+class XDesktop : public rfb::SDesktop,
+                 public TXGlobalEventHandler,
+                 public QueryResultCallback
 {
 public:
   XDesktop(Display* dpy_, Geometry *geometry);
@@ -45,7 +49,10 @@
   // -=- SDesktop interface
   virtual void start(rfb::VNCServer* vs);
   virtual void stop();
+  virtual void terminate();
   bool isRunning();
+  virtual void queryConnection(network::Socket* sock,
+                               const char* userName);
   virtual void pointerEvent(const rfb::Point& pos, int buttonMask);
   KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym);
   virtual void keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down);
@@ -56,11 +63,17 @@
   // -=- TXGlobalEventHandler interface
   virtual bool handleGlobalEvent(XEvent* ev);
 
+  // -=- QueryResultCallback interface
+  virtual void queryApproved();
+  virtual void queryRejected();
+
 protected:
   Display* dpy;
   Geometry* geometry;
   XPixelBuffer* pb;
-  rfb::VNCServerST* server;
+  rfb::VNCServer* server;
+  QueryConnectDialog* queryConnectDialog;
+  network::Socket* queryConnectSock;
   int oldButtonMask;
   bool haveXtest;
   bool haveDamage;
diff --git a/unix/x0vncserver/x0vncserver.cxx b/unix/x0vncserver/x0vncserver.cxx
index d0d4be8..eac9edf 100644
--- a/unix/x0vncserver/x0vncserver.cxx
+++ b/unix/x0vncserver/x0vncserver.cxx
@@ -33,8 +33,6 @@
 #include <network/TcpSocket.h>
 #include <network/UnixSocket.h>
 
-#include <vncconfig/QueryConnectDialog.h>
-
 #include <signal.h>
 #include <X11/X.h>
 #include <X11/Xlib.h>
@@ -61,10 +59,6 @@
 IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
 StringParameter rfbunixpath("rfbunixpath", "Unix socket to listen for RFB protocol", "");
 IntParameter rfbunixmode("rfbunixmode", "Unix socket access mode", 0600);
-IntParameter queryConnectTimeout("QueryConnectTimeout",
-                                 "Number of seconds to show the Accept Connection dialog before "
-                                 "rejecting the connection",
-                                 10);
 StringParameter hostsFile("HostsFile", "File with IP access control rules", "");
 
 //
@@ -79,50 +73,6 @@
 }
 
 
-class QueryConnHandler : public VNCServerST::QueryConnectionHandler,
-                         public QueryResultCallback {
-public:
-  QueryConnHandler(Display* dpy, VNCServerST* vs)
-    : display(dpy), server(vs), queryConnectDialog(0), queryConnectSock(0) {}
-  ~QueryConnHandler() { delete queryConnectDialog; }
-
-  // -=- VNCServerST::QueryConnectionHandler interface
-  virtual VNCServerST::queryResult queryConnection(network::Socket* sock,
-                                                   const char* userName,
-                                                   char** reason) {
-    if (queryConnectSock) {
-      *reason = strDup("Another connection is currently being queried.");
-      return VNCServerST::REJECT;
-    }
-    if (!userName) userName = "(anonymous)";
-    queryConnectSock = sock;
-    CharArray address(sock->getPeerAddress());
-    delete queryConnectDialog;
-    queryConnectDialog = new QueryConnectDialog(display, address.buf,
-                                                userName, queryConnectTimeout,
-                                                this);
-    queryConnectDialog->map();
-    return VNCServerST::PENDING;
-  }
-
-  // -=- QueryResultCallback interface
-  virtual void queryApproved() {
-    server->approveConnection(queryConnectSock, true, 0);
-    queryConnectSock = 0;
-  }
-  virtual void queryRejected() {
-    server->approveConnection(queryConnectSock, false,
-                              "Connection rejected by local user");
-    queryConnectSock = 0;
-  }
-private:
-  Display* display;
-  VNCServerST* server;
-  QueryConnectDialog* queryConnectDialog;
-  network::Socket* queryConnectSock;
-};
-
-
 class FileTcpFilter : public TcpFilter
 {
 
@@ -256,9 +206,6 @@
 
   Configuration::enableServerParams();
 
-  // Disable configuration parameters which we do not support
-  Configuration::removeParam("AcceptSetDesktopSize");
-
   for (int i = 1; i < argc; i++) {
     if (Configuration::setParam(argv[i]))
       continue;
@@ -307,8 +254,6 @@
     XDesktop desktop(dpy, &geo);
 
     VNCServerST server("x0vncserver", &desktop);
-    QueryConnHandler qcHandler(dpy, &server);
-    server.setQueryConnectionHandler(&qcHandler);
 
     if (rfbunixpath.getValueStr()[0] != '\0') {
       listeners.push_back(new network::UnixListener(rfbunixpath, rfbunixmode));
@@ -374,7 +319,7 @@
         }
       }
 
-      soonestTimeout(&wait_ms, server.checkTimeouts());
+      soonestTimeout(&wait_ms, Timer::checkTimeouts());
 
       tv.tv_sec = wait_ms / 1000;
       tv.tv_usec = (wait_ms % 1000) * 1000;
@@ -409,7 +354,7 @@
         }
       }
 
-      server.checkTimeouts();
+      Timer::checkTimeouts();
 
       // Client list could have been changed.
       server.getSockets(&sockets);
diff --git a/unix/x0vncserver/x0vncserver.man b/unix/x0vncserver/x0vncserver.man
index 5f1508c..5aaf694 100644
--- a/unix/x0vncserver/x0vncserver.man
+++ b/unix/x0vncserver/x0vncserver.man
@@ -187,6 +187,10 @@
 Accept pointer press and release events from clients. Default is on.
 .
 .TP
+.B \-AcceptSetDesktopSize
+Accept requests to resize the size of the desktop. Default is on.
+.
+.TP
 .B \-RemapKeys \fImapping
 Sets up a keyboard mapping.
 .I mapping
diff --git a/unix/xserver/hw/vnc/InputXKB.c b/unix/xserver/hw/vnc/InputXKB.c
index 50a2130..f84a6e4 100644
--- a/unix/xserver/hw/vnc/InputXKB.c
+++ b/unix/xserver/hw/vnc/InputXKB.c
@@ -1,6 +1,6 @@
 /* Copyright (C) 2009 TightVNC Team
  * Copyright (C) 2009 Red Hat, Inc.
- * Copyright 2013-2015 Pierre Ossman for Cendio AB
+ * Copyright 2013-2018 Pierre Ossman for Cendio AB
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -52,6 +52,14 @@
 
 extern DeviceIntPtr vncKeyboardDev;
 
+static const KeyCode fakeKeys[] = {
+#ifdef __linux__
+    92, 203, 204, 205, 206, 207
+#else
+    8, 124, 125, 156, 127, 128
+#endif
+    };
+
 static void vncXkbProcessDeviceEvent(int screenNum,
                                      InternalEvent *event,
                                      DeviceIntPtr dev);
@@ -430,17 +438,20 @@
 KeyCode vncKeysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state)
 {
 	XkbDescPtr xkb;
-	unsigned int key;
+	unsigned int key; // KeyCode has insufficient range for the loop
+	KeyCode fallback;
 	KeySym ks;
 	unsigned level_three_mask;
 
 	if (new_state != NULL)
 		*new_state = state;
 
+	fallback = 0;
 	xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
 	for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
 		unsigned int state_out;
 		KeySym dummy;
+		size_t fakeIdx;
 
 		XkbTranslateKeyCode(xkb, key, state, &state_out, &ks);
 		if (ks == NoSymbol)
@@ -456,10 +467,35 @@
 		if (state_out & LockMask)
 			XkbConvertCase(ks, &dummy, &ks);
 
-		if (ks == keysym)
-			return key;
+		if (ks != keysym)
+			continue;
+
+		/*
+		 * Some keys are never sent by a real keyboard and are
+		 * used in the default layouts as a fallback for
+		 * modifiers. Make sure we use them last as some
+		 * applications can be confused by these normally
+		 * unused keys.
+		 */
+		for (fakeIdx = 0;
+		     fakeIdx < sizeof(fakeKeys)/sizeof(fakeKeys[0]);
+		     fakeIdx++) {
+			if (key == fakeKeys[fakeIdx]) {
+				if (fallback == 0)
+					fallback = key;
+				break;
+			}
+		}
+		if (fakeIdx < sizeof(fakeKeys)/sizeof(fakeKeys[0]))
+			continue;
+
+		return key;
 	}
 
+	/* Use the fallback key, if one was found */
+	if (fallback != 0)
+		return fallback;
+
 	if (new_state == NULL)
 		return 0;
 
diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc
index 04107dc..d8b3a4d 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.cc
+++ b/unix/xserver/hw/vnc/XserverDesktop.cc
@@ -22,6 +22,7 @@
 //
 
 #include <assert.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <strings.h>
@@ -81,7 +82,6 @@
 
   server = new VNCServerST(name, this);
   setFramebuffer(width, height, fbptr, stride);
-  server->setQueryConnectionHandler(this);
 
   for (std::list<SocketListener*>::iterator i = listeners.begin();
        i != listeners.end();
@@ -145,22 +145,31 @@
   server->setScreenLayout(::computeScreenLayout(&outputIdMap));
 }
 
-rfb::VNCServerST::queryResult
-XserverDesktop::queryConnection(network::Socket* sock,
-                                const char* userName,
-                                char** reason)
+void XserverDesktop::start(rfb::VNCServer* vs)
+{
+  // We already own the server object, and we always keep it in a
+  // ready state
+  assert(vs == server);
+}
+
+void XserverDesktop::stop()
+{
+}
+
+void XserverDesktop::queryConnection(network::Socket* sock,
+                                     const char* userName)
 {
   int count;
 
   if (queryConnectTimer.isStarted()) {
-    *reason = strDup("Another connection is currently being queried.");
-    return rfb::VNCServerST::REJECT;
+    server->approveConnection(sock, false, "Another connection is currently being queried.");
+    return;
   }
 
   count = vncNotifyQueryConnect();
   if (count == 0) {
-    *reason = strDup("Unable to query the local user to accept the connection.");
-    return rfb::VNCServerST::REJECT;
+    server->approveConnection(sock, false, "Unable to query the local user to accept the connection.");
+    return;
   }
 
   queryConnectAddress.replaceBuf(sock->getPeerAddress());
@@ -171,8 +180,6 @@
   queryConnectSocket = sock;
 
   queryConnectTimer.start(queryConnectTimeout * 1000);
-
-  return rfb::VNCServerST::PENDING;
 }
 
 void XserverDesktop::bell()
@@ -361,7 +368,7 @@
     }
 
     // Trigger timers and check when the next will expire
-    int nextTimeout = server->checkTimeouts();
+    int nextTimeout = Timer::checkTimeouts();
     if (nextTimeout > 0 && (*timeout == -1 || nextTimeout < *timeout))
       *timeout = nextTimeout;
   } catch (rdr::Exception& e) {
@@ -417,6 +424,11 @@
 // SDesktop callbacks
 
 
+void XserverDesktop::terminate()
+{
+  kill(getpid(), SIGTERM);
+}
+
 void XserverDesktop::pointerEvent(const Point& pos, int buttonMask)
 {
   vncPointerMove(pos.x + vncGetScreenX(screenIndex),
diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h
index 014fcb5..1253935 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.h
+++ b/unix/xserver/hw/vnc/XserverDesktop.h
@@ -34,7 +34,7 @@
 #include <rfb/SDesktop.h>
 #include <rfb/PixelBuffer.h>
 #include <rfb/Configuration.h>
-#include <rfb/VNCServerST.h>
+#include <rfb/Timer.h>
 #include <unixcommon.h>
 #include "Input.h"
 
@@ -45,7 +45,6 @@
 namespace network { class SocketListener; class Socket; class SocketServer; }
 
 class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer,
-                       public rfb::VNCServerST::QueryConnectionHandler,
                        public rfb::Timer::Callback {
 public:
 
@@ -86,6 +85,11 @@
                          const char* rejectMsg=0);
 
   // rfb::SDesktop callbacks
+  virtual void start(rfb::VNCServer* vs);
+  virtual void stop();
+  virtual void terminate();
+  virtual void queryConnection(network::Socket* sock,
+                               const char* userName);
   virtual void pointerEvent(const rfb::Point& pos, int buttonMask);
   virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
   virtual void clientCutText(const char* str, int len);
@@ -95,11 +99,6 @@
   // rfb::PixelBuffer callbacks
   virtual void grabRegion(const rfb::Region& r);
 
-  // rfb::VNCServerST::QueryConnectionHandler callback
-  virtual rfb::VNCServerST::queryResult queryConnection(network::Socket* sock,
-                                                        const char* userName,
-                                                        char** reason);
-
 protected:
   bool handleListenerEvent(int fd,
                            std::list<network::SocketListener*>* sockets,
@@ -113,7 +112,7 @@
 private:
 
   int screenIndex;
-  rfb::VNCServerST* server;
+  rfb::VNCServer* server;
   std::list<network::SocketListener*> listeners;
   bool directFbptr;
 
diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc
index 286a04f..20072f4 100644
--- a/unix/xserver/hw/vnc/vncExtInit.cc
+++ b/unix/xserver/hw/vnc/vncExtInit.cc
@@ -179,7 +179,7 @@
             listeners.push_back(new network::TcpListener(vncInetdSock));
             vlog.info("inetd wait");
           }
-        } else if (rfbunixpath.getValueStr()[0] != '\0') {
+        } else if (((const char*)rfbunixpath)[0] != '\0') {
           char path[PATH_MAX];
           int mode = (int)rfbunixmode;
 
@@ -187,7 +187,7 @@
             strncpy(path, rfbunixpath, sizeof(path));
           else
             snprintf(path, sizeof(path), "%s.%d",
-                     rfbunixpath.getValueStr(), scr);
+                     (const char*)rfbunixpath, scr);
           path[sizeof(path)-1] = '\0';
 
           listeners.push_back(new network::UnixListener(path, mode));
diff --git a/unix/xserver/hw/vnc/vncSelection.c b/unix/xserver/hw/vnc/vncSelection.c
index 51dfd9c..4f3538d 100644
--- a/unix/xserver/hw/vnc/vncSelection.c
+++ b/unix/xserver/hw/vnc/vncSelection.c
@@ -105,7 +105,7 @@
       LOG_ERROR("Could not set PRIMARY selection");
   }
 
-  vncOwnSelection(xaCLIPBOARD);
+  rc = vncOwnSelection(xaCLIPBOARD);
   if (rc != Success)
     LOG_ERROR("Could not set CLIPBOARD selection");
 }
diff --git a/unix/xserver/hw/vnc/xvnc.c b/unix/xserver/hw/vnc/xvnc.c
index 79b0e3d..98c4a15 100644
--- a/unix/xserver/hw/vnc/xvnc.c
+++ b/unix/xserver/hw/vnc/xvnc.c
@@ -766,10 +766,13 @@
 	    curpmap = (ColormapPtr) LookupIDByType(pmap->pScreen->defColormap,
 						   RT_COLORMAP);
 #else
-	    dixLookupResourceByType((void * *) &curpmap, pmap->pScreen->defColormap,
-				    RT_COLORMAP, serverClient, DixUnknownAccess);
+	    int rc =  dixLookupResourceByType((void * *) &curpmap, pmap->pScreen->defColormap,
+					      RT_COLORMAP, serverClient, DixUnknownAccess);
+	    if (rc != Success)
+		ErrorF("Failed to uninstall color map\n");
+	    else
 #endif
-	    (*pmap->pScreen->InstallColormap)(curpmap);
+		(*pmap->pScreen->InstallColormap)(curpmap);
 	}
     }
 }
diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx
index 2890989..150c39b 100644
--- a/vncviewer/DesktopWindow.cxx
+++ b/vncviewer/DesktopWindow.cxx
@@ -103,12 +103,12 @@
   int geom_x = 0, geom_y = 0;
   if (strcmp(geometry, "") != 0) {
     int matched;
-    matched = sscanf(geometry.getValueStr(), "+%d+%d", &geom_x, &geom_y);
+    matched = sscanf((const char*)geometry, "+%d+%d", &geom_x, &geom_y);
     if (matched == 2) {
       force_position(1);
     } else {
       int geom_w, geom_h;
-      matched = sscanf(geometry.getValueStr(), "%dx%d+%d+%d", &geom_w, &geom_h, &geom_x, &geom_y);
+      matched = sscanf((const char*)geometry, "%dx%d+%d+%d", &geom_w, &geom_h, &geom_x, &geom_y);
       switch (matched) {
       case 4:
         force_position(1);
diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx
index b018c95..62b5d9c 100644
--- a/vncviewer/OptionsDialog.cxx
+++ b/vncviewer/OptionsDialog.cxx
@@ -282,7 +282,7 @@
   /* Screen */
   int width, height;
 
-  if (sscanf(desktopSize.getValueStr(), "%dx%d", &width, &height) != 2) {
+  if (sscanf((const char*)desktopSize, "%dx%d", &width, &height) != 2) {
     desktopSizeCheckbox->value(false);
     desktopWidthInput->value("1024");
     desktopHeightInput->value("768");
diff --git a/vncviewer/PlatformPixelBuffer.cxx b/vncviewer/PlatformPixelBuffer.cxx
index e6a054a..1e9803e 100644
--- a/vncviewer/PlatformPixelBuffer.cxx
+++ b/vncviewer/PlatformPixelBuffer.cxx
@@ -58,6 +58,9 @@
 
   data = (rdr::U8*)xim->data;
   stride = xim->bytes_per_line / (getPF().bpp/8);
+
+  // On X11, the Pixmap backing this Surface is uninitialized.
+  clear(0, 0, 0);
 #else
   FullFramePixelBuffer::data = (rdr::U8*)Surface::data;
   stride = width;
@@ -101,6 +104,9 @@
   mutex.unlock();
 
 #if !defined(WIN32) && !defined(__APPLE__)
+  if (r.width() == 0 || r.height() == 0)
+    return r;
+
   GC gc;
 
   gc = XCreateGC(fl_display, pixmap, 0, NULL);
diff --git a/vncviewer/ServerDialog.cxx b/vncviewer/ServerDialog.cxx
index de67f87..fec1789 100644
--- a/vncviewer/ServerDialog.cxx
+++ b/vncviewer/ServerDialog.cxx
@@ -150,7 +150,7 @@
     return;
   }
   
-  const char* filename = strdup(file_chooser->value());
+  const char* filename = file_chooser->value();
 
   try {
     dialog->serverName->value(loadViewerParameters(filename));
@@ -165,8 +165,8 @@
 void ServerDialog::handleSaveAs(Fl_Widget *widget, void *data)
 { 
   ServerDialog *dialog = (ServerDialog*)data;
-  const char* servername = strdup(dialog->serverName->value());
-  char* filename;
+  const char* servername = dialog->serverName->value();
+  const char* filename;
 
   Fl_File_Chooser* file_chooser = new Fl_File_Chooser("", _("TigerVNC configuration (*.tigervnc)"), 
 						      2, _("Save the TigerVNC configuration to file"));
@@ -187,7 +187,7 @@
       return;
     }
     
-    filename = strdup(file_chooser->value());
+    filename = file_chooser->value();
     
     FILE* f = fopen(filename, "r");
     if (f) {
@@ -235,7 +235,7 @@
 void ServerDialog::handleConnect(Fl_Widget *widget, void *data)
 {
   ServerDialog *dialog = (ServerDialog*)data;
-  const char* servername = strdup(dialog->serverName->value());
+  const char* servername = dialog->serverName->value();
 
   dialog->hide();
   
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index ec78314..425cb9f 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -936,6 +936,13 @@
 
     keyCode = ((msg->lParam >> 16) & 0xff);
 
+    // Windows' touch keyboard doesn't set a scan code for the Alt
+    // portion of the AltGr sequence, so we need to help it out
+    if (!isExtended && (keyCode == 0x00) && (vKey == VK_MENU)) {
+      isExtended = true;
+      keyCode = 0x38;
+    }
+
     // Windows doesn't have a proper AltGr, but handles it using fake
     // Ctrl+Alt. However the remote end might not be Windows, so we need
     // to merge those in to a single AltGr event. We detect this case
@@ -1040,6 +1047,12 @@
 
     keyCode = ((msg->lParam >> 16) & 0xff);
 
+    // Touch keyboard AltGr (see above)
+    if (!isExtended && (keyCode == 0x00) && (vKey == VK_MENU)) {
+      isExtended = true;
+      keyCode = 0x38;
+    }
+
     // We can't get a release in the middle of an AltGr sequence, so
     // abort that detection
     if (self->altGrArmed) {
diff --git a/vncviewer/gettext.h b/vncviewer/gettext.h
index ac4d7d5..768a699 100644
--- a/vncviewer/gettext.h
+++ b/vncviewer/gettext.h
@@ -1,24 +1,26 @@
 /* Convenience header for conditional use of GNU <libintl.h>.
-   Copyright (C) 1995-1998, 2000-2002, 2004-2006, 2009-2011 Free Software Foundation, Inc.
+   Copyright (C) 1995-1998, 2000-2002, 2004-2006, 2009-2018 Free Software
+   Foundation, Inc.
 
-   This program is free software: you can redistribute it and/or modify
+   This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 3 of the License, or
-   (at your option) any later version.
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, see <https://www.gnu.org/licenses/>.  */
 
 #ifndef _LIBGETTEXT_H
 #define _LIBGETTEXT_H 1
 
-/* NLS can be disabled through the configure --disable-nls option.  */
-#if ENABLE_NLS
+/* NLS can be disabled through the configure --disable-nls option
+   or through "#define ENABLE NLS 0" before including this file.  */
+#if defined ENABLE_NLS && ENABLE_NLS
 
 /* Get declarations of GNU message catalog functions.  */
 # include <libintl.h>
@@ -182,8 +184,9 @@
 
 #include <string.h>
 
-#if (((__GNUC__ >= 3 || __GNUG__ >= 2) && !defined __STRICT_ANSI__) \
-     /* || __STDC_VERSION__ >= 199901L */ )
+#if (((__GNUC__ >= 3 || __GNUG__ >= 2) && !defined __STRICT_ANSI__ && !defined __cplusplus) \
+     /* || __STDC_VERSION__ == 199901L
+        || (__STDC_VERSION__ >= 201112L && !defined __STDC_NO_VLA__) */ )
 # define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS 1
 #else
 # define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS 0
@@ -224,15 +227,17 @@
   if (msg_ctxt_id != NULL)
 #endif
     {
+      int found_translation;
       memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1);
       msg_ctxt_id[msgctxt_len - 1] = '\004';
       memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len);
       translation = dcgettext (domain, msg_ctxt_id, category);
+      found_translation = (translation != msg_ctxt_id);
 #if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
       if (msg_ctxt_id != buf)
         free (msg_ctxt_id);
 #endif
-      if (translation != msg_ctxt_id)
+      if (found_translation)
         return translation;
     }
   return msgid;
@@ -270,15 +275,17 @@
   if (msg_ctxt_id != NULL)
 #endif
     {
+      int found_translation;
       memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1);
       msg_ctxt_id[msgctxt_len - 1] = '\004';
       memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len);
       translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category);
+      found_translation = !(translation == msg_ctxt_id || translation == msgid_plural);
 #if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
       if (msg_ctxt_id != buf)
         free (msg_ctxt_id);
 #endif
-      if (!(translation == msg_ctxt_id || translation == msgid_plural))
+      if (found_translation)
         return translation;
     }
   return (n == 1 ? msgid : msgid_plural);
diff --git a/vncviewer/parameters.cxx b/vncviewer/parameters.cxx
index 51cce3d..8427818 100644
--- a/vncviewer/parameters.cxx
+++ b/vncviewer/parameters.cxx
@@ -325,9 +325,10 @@
 
 static bool getKeyString(const char* _name, char* dest, size_t destSize, HKEY* hKey) {
   
-  DWORD buffersize = 256;
-  WCHAR value[destSize];
+  const DWORD buffersize = 256;
   wchar_t name[buffersize];
+  WCHAR* value;
+  DWORD valuesize;
 
   unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
   if (size >= buffersize) {
@@ -335,8 +336,11 @@
     return false;
   }
 
-  LONG res = RegQueryValueExW(*hKey, name, 0, NULL, (LPBYTE)value, &buffersize);
+  value = new WCHAR[destSize];
+  valuesize = destSize;
+  LONG res = RegQueryValueExW(*hKey, name, 0, NULL, (LPBYTE)value, &valuesize);
   if (res != ERROR_SUCCESS){
+    delete [] value;
     if (res == ERROR_FILE_NOT_FOUND) {
       // The value does not exist, defaults will be used.
     } else {
@@ -346,18 +350,19 @@
     return false;
   }
   
-  char utf8val[destSize];
-  size = fl_utf8fromwc(utf8val, sizeof(utf8val), value, wcslen(value)+1);
-  if (size >= sizeof(utf8val)) {
+  char* utf8val = new char[destSize];
+  size = fl_utf8fromwc(utf8val, destSize, value, wcslen(value)+1);
+  delete [] value;
+  if (size >= destSize) {
+    delete [] utf8val;
     vlog.error(_("The parameter %s was too large to read from the registry"), _name);
     return false;
   }
-  const char *ret = utf8val;
   
-  if(decodeValue(ret, dest, destSize))
-    return true;
-  else 
-    return false;
+  bool ret = decodeValue(utf8val, dest, destSize);
+  delete [] utf8val;
+
+  return ret;
 }
 
 
@@ -499,6 +504,7 @@
     }
 
     snprintf(filepath, sizeof(filepath), "%sdefault.tigervnc", homeDir);
+    free(homeDir);
   } else {
     snprintf(filepath, sizeof(filepath), "%s", filename);
   }
@@ -555,6 +561,7 @@
                         "can't obtain home directory path."));
 
     snprintf(filepath, sizeof(filepath), "%sdefault.tigervnc", homeDir);
+    free(homeDir);
   } else {
     snprintf(filepath, sizeof(filepath), "%s", filename);
   }
diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx
index ac4afca..1c6f524 100644
--- a/vncviewer/vncviewer.cxx
+++ b/vncviewer/vncviewer.cxx
@@ -479,9 +479,9 @@
   int localPort = findFreeTcpPort();
   int remotePort;
 
-  gatewayHost = strDup(via.getValueStr());
   if (interpretViaParam(remoteHost, &remotePort, localPort) != 0)
     return 1;
+  gatewayHost = (const char*)via;
   createTunnel(gatewayHost, remoteHost, remotePort, localPort);
 
   return 0;
diff --git a/win/rfb_win32/SDisplay.cxx b/win/rfb_win32/SDisplay.cxx
index 9b2cbb0..2cedc4a 100644
--- a/win/rfb_win32/SDisplay.cxx
+++ b/win/rfb_win32/SDisplay.cxx
@@ -20,6 +20,8 @@
 //
 // The SDisplay class encapsulates a particular system display.
 
+#include <assert.h>
+
 #include <rfb_win32/SDisplay.h>
 #include <rfb_win32/Service.h>
 #include <rfb_win32/TsSessions.h>
@@ -66,9 +68,10 @@
   : server(0), pb(0), device(0),
     core(0), ptr(0), kbd(0), clipboard(0),
     inputs(0), monitor(0), cleanDesktop(0), cursor(0),
-    statusLocation(0), ledState(0)
+    statusLocation(0), queryConnectionHandler(0), ledState(0)
 {
   updateEvent.h = CreateEvent(0, TRUE, FALSE, 0);
+  terminateEvent.h = CreateEvent(0, TRUE, FALSE, 0);
 }
 
 SDisplay::~SDisplay()
@@ -138,6 +141,25 @@
   if (statusLocation) *statusLocation = false;
 }
 
+void SDisplay::terminate()
+{
+  SetEvent(terminateEvent);
+}
+
+
+void SDisplay::queryConnection(network::Socket* sock,
+                               const char* userName)
+{
+  assert(server != NULL);
+
+  if (queryConnectionHandler) {
+    queryConnectionHandler->queryConnection(sock, userName);
+    return;
+  }
+
+  server->approveConnection(sock, true);
+}
+
 
 void SDisplay::startCore() {
 
diff --git a/win/rfb_win32/SDisplay.h b/win/rfb_win32/SDisplay.h
index c1d5c1e..6dbfabb 100644
--- a/win/rfb_win32/SDisplay.h
+++ b/win/rfb_win32/SDisplay.h
@@ -52,6 +52,13 @@
       virtual const char* methodName() const = 0;
     };
 
+    class QueryConnectionHandler {
+    public:
+      virtual ~QueryConnectionHandler() {}
+      virtual void queryConnection(network::Socket* sock,
+                                   const char* userName) = 0;
+    };
+
     class SDisplay : public SDesktop,
       WMMonitor::Notifier,
       Clipboard::Notifier,
@@ -65,6 +72,9 @@
 
       virtual void start(VNCServer* vs);
       virtual void stop();
+      virtual void terminate();
+      virtual void queryConnection(network::Socket* sock,
+                                   const char* userName);
       virtual void pointerEvent(const Point& pos, int buttonmask);
       virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
       virtual void clientCutText(const char* str, int len);
@@ -80,12 +90,19 @@
       // -=- EventHandler interface
 
       HANDLE getUpdateEvent() {return updateEvent;}
+      HANDLE getTerminateEvent() {return terminateEvent;}
       virtual void processEvent(HANDLE event);
 
       // -=- Notification of whether or not SDisplay is started
 
       void setStatusLocation(bool* status) {statusLocation = status;}
 
+      // -=- Set handler for incoming connections
+
+      void setQueryConnectionHandler(QueryConnectionHandler* qch) {
+        queryConnectionHandler = qch;
+      }
+
       static IntParameter updateMethod;
       static BoolParameter disableLocalInputs;
       static StringParameter disconnectAction;
@@ -146,10 +163,15 @@
 
       // -=- Event signalled to trigger an update to be flushed
       Handle updateEvent;
+      // -=- Event signalled to terminate the server
+      Handle terminateEvent;
 
       // -=- Where to write the active/inactive indicator to
       bool* statusLocation;
 
+      // -=- Whom to query incoming connections
+      QueryConnectionHandler* queryConnectionHandler;
+
       unsigned ledState;
     };
 
diff --git a/win/rfb_win32/SocketManager.cxx b/win/rfb_win32/SocketManager.cxx
index 5b211a0..0092d94 100644
--- a/win/rfb_win32/SocketManager.cxx
+++ b/win/rfb_win32/SocketManager.cxx
@@ -21,6 +21,7 @@
 #include <winsock2.h>
 #include <list>
 #include <rfb/LogWriter.h>
+#include <rfb/Timer.h>
 #include <rfb_win32/SocketManager.h>
 
 using namespace rfb;
@@ -78,6 +79,7 @@
   li.sock = sock_;
   li.server = srvr;
   li.notifier = acn;
+  li.disable = false;
   listeners[event] = li;
 }
 
@@ -128,13 +130,39 @@
   throw rdr::Exception("Socket not registered");
 }
 
+bool SocketManager::getDisable(network::SocketServer* srvr)
+{
+  std::map<HANDLE,ListenInfo>::iterator i;
+  for (i=listeners.begin(); i!=listeners.end(); i++) {
+    if (i->second.server == srvr) {
+      return i->second.disable;
+    }
+  }
+  throw rdr::Exception("Listener not registered");
+}
+
+void SocketManager::setDisable(network::SocketServer* srvr, bool disable)
+{
+  bool found = false;
+  std::map<HANDLE,ListenInfo>::iterator i;
+  for (i=listeners.begin(); i!=listeners.end(); i++) {
+    if (i->second.server == srvr) {
+      i->second.disable = disable;
+      // There might be multiple sockets for the same server, so
+      // continue iterating
+      found = true;
+    }
+  }
+  if (!found)
+    throw rdr::Exception("Listener not registered");
+}
 
 int SocketManager::checkTimeouts() {
   int timeout = EventManager::checkTimeouts();
 
   std::map<HANDLE,ListenInfo>::iterator i;
   for (i=listeners.begin(); i!=listeners.end(); i++)
-    soonestTimeout(&timeout, i->second.server->checkTimeouts());
+    soonestTimeout(&timeout, Timer::checkTimeouts());
 
   std::list<network::Socket*> shutdownSocks;
   std::map<HANDLE,ConnInfo>::iterator j, j_next;
@@ -164,7 +192,7 @@
     WSAEnumNetworkEvents(li.sock->getFd(), event, &network_events);
     if (network_events.lNetworkEvents & FD_ACCEPT) {
       network::Socket* new_sock = li.sock->accept();
-      if (new_sock && li.server->getDisable()) {
+      if (new_sock && li.disable) {
         delete new_sock;
         new_sock = 0;
       }
diff --git a/win/rfb_win32/SocketManager.h b/win/rfb_win32/SocketManager.h
index c3c8faf..e5ca02e 100644
--- a/win/rfb_win32/SocketManager.h
+++ b/win/rfb_win32/SocketManager.h
@@ -65,6 +65,9 @@
       // the SocketServer.
       void addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing=true);
 
+      bool getDisable(network::SocketServer* srvr);
+      void setDisable(network::SocketServer* srvr, bool disable);
+
     protected:
       virtual int checkTimeouts();
       virtual void processEvent(HANDLE event);
@@ -78,6 +81,7 @@
         network::SocketListener* sock;
         network::SocketServer* server;
         AddressChangeNotifier* notifier;
+        bool disable;
       };
       std::map<HANDLE, ListenInfo> listeners;
       std::map<HANDLE, ConnInfo> connections;
diff --git a/win/winvnc/ControlPanel.cxx b/win/winvnc/ControlPanel.cxx
index ba6cab2..72831e5 100644
--- a/win/winvnc/ControlPanel.cxx
+++ b/win/winvnc/ControlPanel.cxx
@@ -19,10 +19,9 @@
 {
   TCHAR *ColumnsStrings[] = {
     (TCHAR *) "IP address",
-    (TCHAR *) "Time connected",
     (TCHAR *) "Status"
   };
-  InitLVColumns(IDC_LIST_CONNECTIONS, handle, 120, 3, ColumnsStrings,
+  InitLVColumns(IDC_LIST_CONNECTIONS, handle, 120, 2, ColumnsStrings,
                 LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM,
                 LVS_EX_FULLROWSELECT, LVCFMT_LEFT);
   SendCommand(4, -1);
@@ -74,7 +73,7 @@
   
 }
 
-void ControlPanel::UpdateListView(rfb::ListConnInfo* LCInfo)
+void ControlPanel::UpdateListView(ListConnInfo* LCInfo)
 {
   getSelConnInfo();
   DeleteAllLVItem(IDC_LIST_CONNECTIONS, handle);
@@ -85,12 +84,12 @@
 
   ListConn.Copy(LCInfo);
 
-  char* ItemString[3];
+  char* ItemString[2];
   int i = 0;
 
   for (ListConn.iBegin(); !ListConn.iEnd(); ListConn.iNext()) {
     ListConn.iGetCharInfo(ItemString);
-    InsertLVItem(IDC_LIST_CONNECTIONS, handle, i, (TCHAR **) ItemString, 3);
+    InsertLVItem(IDC_LIST_CONNECTIONS, handle, i, (TCHAR **) ItemString, 2);
     for (ListSelConn.iBegin(); !ListSelConn.iEnd(); ListSelConn.iNext()) {
       if (ListSelConn.iGetConn() == ListConn.iGetConn())
         SelectLVItem(IDC_LIST_CONNECTIONS, handle, i);
@@ -141,6 +140,8 @@
 {
   COPYDATASTRUCT copyData;
   copyData.dwData = command;
+  copyData.cbData = 0;
+  copyData.lpData = 0;
   getSelConnInfo();
   if (data != -1) {
     ListConnStatus.Copy(&ListSelConn);
@@ -149,8 +150,6 @@
   } else {
     ListConnStatus.Clear();
   }
-  copyData.cbData = 0;
-  copyData.lpData = &ListConnStatus;
   SendMessage(m_hSTIcon, WM_COPYDATA, 0, (LPARAM)&copyData);
 }
 
diff --git a/win/winvnc/ControlPanel.h b/win/winvnc/ControlPanel.h
index 73b859f..f64a608 100644
--- a/win/winvnc/ControlPanel.h
+++ b/win/winvnc/ControlPanel.h
@@ -11,10 +11,10 @@
 
 #include <list>
 #include <winvnc/resource.h>
+#include <winvnc/ListConnInfo.h>
 #include <rfb_win32/Dialog.h>
 #include <rfb_win32/ListViewControl.h>
 #include <rfb_win32/Win32Util.h>
-#include <rfb/ListConnInfo.h>
 
 namespace winvnc {
   
@@ -27,19 +27,19 @@
     virtual bool showDialog();
     virtual void initDialog();
     virtual bool onCommand(int cmd);
-    void UpdateListView(rfb::ListConnInfo* LCInfo);
+    void UpdateListView(ListConnInfo* LCInfo);
     HWND GetHandle() {return handle;};
     void SendCommand(DWORD command, int data);
     ~ControlPanel();
-    rfb::ListConnInfo ListConnStatus;
+    ListConnInfo ListConnStatus;
   protected: 
     virtual BOOL dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
     void getSelConnInfo();
     HWND m_hSTIcon;
-    rfb::ListConnInfo ListConn;
-    rfb::ListConnInfo ListSelConn;
+    ListConnInfo ListConn;
+    ListConnInfo ListSelConn;
     bool stop_updating;
   };
 };
 
-#endif  
\ No newline at end of file
+#endif  
diff --git a/common/rfb/ListConnInfo.h b/win/winvnc/ListConnInfo.h
similarity index 80%
rename from common/rfb/ListConnInfo.h
rename to win/winvnc/ListConnInfo.h
index c49947d..6ca5b7c 100644
--- a/common/rfb/ListConnInfo.h
+++ b/win/winvnc/ListConnInfo.h
@@ -24,7 +24,7 @@
 
 #include <rfb/util.h>
 
-namespace rfb {
+namespace winvnc {
 
   struct ListConnInfo  {
     ListConnInfo() : disableClients(false) {}
@@ -32,7 +32,6 @@
     void Clear() {
       conn.clear();
       IP_address.clear();
-      time_conn.clear();
       status.clear();
     }
 
@@ -41,7 +40,6 @@
     void iBegin() {
       ci = conn.begin();
       Ii = IP_address.begin();
-      ti = time_conn.begin();
       si = status.begin();
     }
 
@@ -50,32 +48,29 @@
     void iNext() {
       ci++;
       Ii++;
-      ti++;
       si++;
     }
 
-    void addInfo(void* Conn, char* IP, char* Time, int Status) {
+    void addInfo(void* Conn, char* IP, int Status) {
       conn.push_back(Conn);
-      IP_address.push_back(strDup(IP));
-      time_conn.push_back(strDup(Time));
+      IP_address.push_back(rfb::strDup(IP));
       status.push_back(Status);
     }
 
-    void iGetCharInfo(char* buf[3]) {
+    void iGetCharInfo(char* buf[2]) {
       buf[0] = *Ii;
-      buf[1] = *ti;
       switch (*si) {
       case 0:
-        buf[2] = strDup("Full control");
+        buf[1] = rfb::strDup("Full control");
         break;
       case 1:
-        buf[2] = strDup("View only");
+        buf[1] = rfb::strDup("View only");
         break;
       case 2:
-        buf[2] = strDup("Stop updating");
+        buf[1] = rfb::strDup("Stop updating");
         break;
       default:
-        buf[2] = strDup("Unknown");
+        buf[1] = rfb::strDup("Unknown");
       }
     }
 
@@ -95,9 +90,9 @@
     }
 
     void iAdd (ListConnInfo* InputList) {
-      char* buf[3];
+      char* buf[2];
       InputList->iGetCharInfo(buf);
-      addInfo(InputList->iGetConn(), buf[0], buf[1], InputList->iGetStatus());
+      addInfo(InputList->iGetConn(), buf[0], InputList->iGetStatus());
     }
 
     void setDisable(bool disable) {disableClients = disable;}
@@ -113,11 +108,9 @@
   private:
     std::list<void*> conn;
     std::list<char*> IP_address;
-    std::list<char*> time_conn;
     std::list<int> status;
     std::list<void*>::iterator ci;
     std::list<char*>::iterator Ii;
-    std::list<char*>::iterator ti;
     std::list<int>::iterator si;
     bool disableClients;
   };
diff --git a/win/winvnc/STrayIcon.cxx b/win/winvnc/STrayIcon.cxx
index 05a38d6..a90819d 100644
--- a/win/winvnc/STrayIcon.cxx
+++ b/win/winvnc/STrayIcon.cxx
@@ -184,7 +184,7 @@
         case 2:
           return thread.server.disconnectClients("IPC disconnect") ? 1 : 0;
         case 3:
-          thread.server.setClientsStatus((rfb::ListConnInfo *)command->lpData);
+          thread.server.setClientsStatus(&CPanel->ListConnStatus);
         case 4:
           thread.server.getClientsInfo(&LCInfo);
           CPanel->UpdateListView(&LCInfo);
@@ -230,7 +230,7 @@
   LaunchProcess vncConnect;
   STrayIconThread& thread;
   ControlPanel * CPanel;
-  rfb::ListConnInfo LCInfo;
+  ListConnInfo LCInfo;
 };
 
 
diff --git a/win/winvnc/VNCServerService.cxx b/win/winvnc/VNCServerService.cxx
index 5656de0..f5a4dba 100644
--- a/win/winvnc/VNCServerService.cxx
+++ b/win/winvnc/VNCServerService.cxx
@@ -19,6 +19,7 @@
 // -=- WinVNC Version 4.0 Service-Mode implementation
 
 #include <winvnc/VNCServerService.h>
+#include <rfb/LogWriter.h>
 #include <rfb_win32/TsSessions.h>
 #include <rfb_win32/ModuleFileName.h>
 #include <windows.h>
diff --git a/win/winvnc/VNCServerWin32.cxx b/win/winvnc/VNCServerWin32.cxx
index 9f6a954..e001449 100644
--- a/win/winvnc/VNCServerWin32.cxx
+++ b/win/winvnc/VNCServerWin32.cxx
@@ -20,6 +20,7 @@
 
 #include <winvnc/VNCServerWin32.h>
 #include <winvnc/resource.h>
+#include <winvnc/ListConnInfo.h>
 #include <winvnc/STrayIcon.h>
 
 #include <os/Mutex.h>
@@ -71,12 +72,11 @@
 
   // Initialise the desktop
   desktop.setStatusLocation(&isDesktopStarted);
-
-  // Initialise the VNC server
-  vncServer.setQueryConnectionHandler(this);
+  desktop.setQueryConnectionHandler(this);
 
   // Register the desktop's event to be handled
   sockMgr.addEvent(desktop.getUpdateEvent(), &desktop);
+  sockMgr.addEvent(desktop.getTerminateEvent(), this);
 
   // Register the queued command event to be handled
   sockMgr.addEvent(commandEvent, this);
@@ -241,27 +241,27 @@
   return false;
 }
 
-bool VNCServerWin32::getClientsInfo(rfb::ListConnInfo* LCInfo) {
+bool VNCServerWin32::getClientsInfo(ListConnInfo* LCInfo) {
   return queueCommand(GetClientsInfo, LCInfo, 0);
 }
 
-bool VNCServerWin32::setClientsStatus(rfb::ListConnInfo* LCInfo) {
+bool VNCServerWin32::setClientsStatus(ListConnInfo* LCInfo) {
   return queueCommand(SetClientsStatus, LCInfo, 0);
 }
 
-VNCServerST::queryResult VNCServerWin32::queryConnection(network::Socket* sock,
-                                            const char* userName,
-                                            char** reason)
+void VNCServerWin32::queryConnection(network::Socket* sock,
+                                     const char* userName)
 {
-  if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn())
-    return VNCServerST::ACCEPT;
+  if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn()) {
+    vncServer.approveConnection(sock, true, NULL);
+    return;
+  }
   if (queryConnectDialog) {
-    *reason = rfb::strDup("Another connection is currently being queried.");
-    return VNCServerST::REJECT;
+    vncServer.approveConnection(sock, false, "Another connection is currently being queried.");
+    return;
   }
   queryConnectDialog = new QueryConnectDialog(sock, userName, this);
   queryConnectDialog->startDialog();
-  return VNCServerST::PENDING;
 }
 
 void VNCServerWin32::queryConnectionComplete() {
@@ -309,10 +309,10 @@
       sockMgr.addSocket((network::Socket*)commandData, &vncServer);
       break;
   case GetClientsInfo:
-    vncServer.getConnInfo((ListConnInfo*)commandData); 
+    getConnInfo((ListConnInfo*)commandData);
     break;
   case SetClientsStatus:
-    vncServer.setConnStatus((ListConnInfo*)commandData); 
+    setConnStatus((ListConnInfo*)commandData);
     break;
 
     case QueryConnectionComplete:
@@ -336,8 +336,88 @@
       command = NoCommand;
       commandSig->signal();
     }
-  } else if (event_ == sessionEvent.h) {
+  } else if ((event_ == sessionEvent.h) ||
+             (event_ == desktop.getTerminateEvent())) {
     stop();
   }
 }
 
+void VNCServerWin32::getConnInfo(ListConnInfo * listConn)
+{
+  std::list<network::Socket*> sockets;
+  std::list<network::Socket*>::iterator i;
+
+  listConn->Clear();
+  listConn->setDisable(sockMgr.getDisable(&vncServer));
+
+  vncServer.getSockets(&sockets);
+
+  for (i = sockets.begin(); i != sockets.end(); i++) {
+    rfb::SConnection* conn;
+    int status;
+
+    conn = vncServer.getConnection(*i);
+    if (!conn)
+      continue;
+
+    if (conn->accessCheck(rfb::SConnection::AccessPtrEvents |
+                          rfb::SConnection::AccessKeyEvents |
+                          rfb::SConnection::AccessView))
+      status = 0;
+    else if (conn->accessCheck(rfb::SConnection::AccessView))
+      status = 1;
+    else
+      status = 2;
+
+    listConn->addInfo((void*)(*i), (*i)->getPeerAddress(), status);
+  }
+}
+
+void VNCServerWin32::setConnStatus(ListConnInfo* listConn)
+{
+  sockMgr.setDisable(&vncServer, listConn->getDisable());
+
+  if (listConn->Empty())
+    return;
+
+  for (listConn->iBegin(); !listConn->iEnd(); listConn->iNext()) {
+    network::Socket* sock;
+    rfb::SConnection* conn;
+    int status;
+
+    sock = (network::Socket*)listConn->iGetConn();
+
+    conn = vncServer.getConnection(sock);
+    if (!conn)
+      continue;
+
+    status = listConn->iGetStatus();
+    if (status == 3) {
+      conn->close(0);
+    } else {
+      rfb::SConnection::AccessRights ar;
+
+      ar = rfb::SConnection::AccessDefault;
+
+      switch (status) {
+      case 0:
+        ar |= rfb::SConnection::AccessPtrEvents |
+              rfb::SConnection::AccessKeyEvents |
+              rfb::SConnection::AccessView;
+        break;
+      case 1:
+        ar |= rfb::SConnection::AccessView;
+        ar &= ~(rfb::SConnection::AccessPtrEvents |
+                rfb::SConnection::AccessKeyEvents);
+        break;
+      case 2:
+        ar &= ~(rfb::SConnection::AccessPtrEvents |
+                rfb::SConnection::AccessKeyEvents |
+                rfb::SConnection::AccessView);
+        break;
+      }
+      conn->setAccessRights(ar);
+      conn->framebufferUpdateRequest(vncServer.getPixelBuffer()->getRect(), false);
+    }
+  }
+}
diff --git a/win/winvnc/VNCServerWin32.h b/win/winvnc/VNCServerWin32.h
index 271cb76..1a73782 100644
--- a/win/winvnc/VNCServerWin32.h
+++ b/win/winvnc/VNCServerWin32.h
@@ -37,9 +37,10 @@
 
 namespace winvnc {
 
+  class ListConnInfo;
   class STrayIconThread;
 
-  class VNCServerWin32 : rfb::VNCServerST::QueryConnectionHandler,
+  class VNCServerWin32 : rfb::win32::QueryConnectionHandler,
                          rfb::win32::SocketManager::AddressChangeNotifier,
                          rfb::win32::RegConfig::Callback,
                          rfb::win32::EventHandler {
@@ -73,17 +74,16 @@
     // Where to read the configuration settings from
     static const TCHAR* RegConfigPath;
 
-    bool getClientsInfo(rfb::ListConnInfo* LCInfo);
+    bool getClientsInfo(ListConnInfo* LCInfo);
 
-    bool setClientsStatus(rfb::ListConnInfo* LCInfo);
+    bool setClientsStatus(ListConnInfo* LCInfo);
 
   protected:
-    // VNCServerST::QueryConnectionHandler interface
+    // QueryConnectionHandler interface
     // Callback used to prompt user to accept or reject a connection.
     // CALLBACK IN VNCServerST "HOST" THREAD
-    virtual rfb::VNCServerST::queryResult queryConnection(network::Socket* sock,
-                                                          const char* userName,
-                                                          char** reason);
+    virtual void queryConnection(network::Socket* sock,
+                                 const char* userName);
 
     // SocketManager::AddressChangeNotifier interface
     // Used to keep tray icon up to date
@@ -97,6 +97,9 @@
     // Used to perform queued commands
     virtual void processEvent(HANDLE event);
 
+    void getConnInfo(ListConnInfo * listConn);
+    void setConnStatus(ListConnInfo* listConn);
+
   protected:
     // Perform a particular internal function in the server thread
     typedef enum {NoCommand, DisconnectClients, AddClient, QueryConnectionComplete, SetClientsStatus, GetClientsInfo} Command;