Merge branch 'covscan' of https://github.com/grulja/tigervnc
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/rdr/SubstitutingInStream.h b/common/rdr/SubstitutingInStream.h
deleted file mode 100644
index 325b01c..0000000
--- a/common/rdr/SubstitutingInStream.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * 
- * This 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 2 of the License, or
- * (at your option) any later version.
- * 
- * This software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
- * USA.
- */
-
-#ifndef __RDR_SUBSTITUTINGINSTREAM_H__
-#define __RDR_SUBSTITUTINGINSTREAM_H__
-
-#include <rdr/InStream.h>
-#include <rdr/Exception.h>
-
-namespace rdr {
-
-  class Substitutor {
-  public:
-    virtual char* substitute(const char* varName) = 0;
-  };
-
-  class SubstitutingInStream : public InStream {
-  public:
-    SubstitutingInStream(InStream* underlying_, Substitutor* s,
-                         int maxVarNameLen_)
-      : underlying(underlying_), dollar(0), substitutor(s), subst(0),
-        maxVarNameLen(maxVarNameLen_)
-    {
-      ptr = end = underlying->getptr();
-      varName = new char[maxVarNameLen+1];
-    }
-    ~SubstitutingInStream() {
-      delete underlying;
-      delete [] varName;
-      delete [] subst;
-    }
-
-    int pos() { return underlying->pos(); }
-
-    virtual int overrun(int itemSize, int nItems, bool wait=true) {
-      if (itemSize != 1)
-        throw new rdr::Exception("SubstitutingInStream: itemSize must be 1");
-
-      if (subst) {
-        delete [] subst;
-        subst = 0;
-      } else {
-        underlying->setptr(ptr);
-      }
-
-      underlying->check(1);
-      ptr = underlying->getptr();
-      end = underlying->getend();
-      dollar = (const U8*)memchr(ptr, '$', end-ptr);
-      if (dollar) {
-        if (dollar == ptr) {
-          try {
-            int i = 0;
-            while (i < maxVarNameLen) {
-              varName[i++] = underlying->readS8();
-              varName[i] = 0;
-              subst = substitutor->substitute(varName);
-              if (subst) {
-                ptr = (U8*)subst;
-                end = (U8*)subst + strlen(subst);
-                break;
-              }
-            }
-          } catch (EndOfStream&) {
-          }
-
-          if (!subst)
-            dollar = (const U8*)memchr(ptr+1, '$', end-ptr-1);
-        }
-        if (!subst && dollar) end = dollar;
-      }
-
-      if (itemSize * nItems > end - ptr)
-        nItems = (end - ptr) / itemSize;
-
-      return nItems;
-    }
-
-    InStream* underlying;
-    const U8* dollar;
-    Substitutor* substitutor;
-    char* varName;
-    char* subst;
-    int maxVarNameLen;
-  };
-}
-#endif
diff --git a/common/rfb/CMakeLists.txt b/common/rfb/CMakeLists.txt
index 62ef401..b8d0813 100644
--- a/common/rfb/CMakeLists.txt
+++ b/common/rfb/CMakeLists.txt
@@ -21,7 +21,6 @@
   d3des.c
   EncodeManager.cxx
   Encoder.cxx
-  HTTPServer.cxx
   HextileDecoder.cxx
   HextileEncoder.cxx
   JpegCompressor.cxx
diff --git a/common/rfb/CSecurityTLS.cxx b/common/rfb/CSecurityTLS.cxx
index d268202..d6a8d7f 100644
--- a/common/rfb/CSecurityTLS.cxx
+++ b/common/rfb/CSecurityTLS.cxx
@@ -196,6 +196,9 @@
     throw AuthFailureException("TLS Handshake failed");
   }
 
+  vlog.debug("TLS handshake completed with %s",
+             gnutls_session_get_desc(session));
+
   checkSession();
 
   cc->setStreams(tlsis, tlsos);
diff --git a/common/rfb/HTTPServer.cxx b/common/rfb/HTTPServer.cxx
deleted file mode 100644
index 2895a69..0000000
--- a/common/rfb/HTTPServer.cxx
+++ /dev/null
@@ -1,424 +0,0 @@
-/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * 
- * This 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 2 of the License, or
- * (at your option) any later version.
- * 
- * This software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
- * USA.
- */
-
-#include <rfb/HTTPServer.h>
-#include <rfb/LogWriter.h>
-#include <rfb/util.h>
-#include <rdr/MemOutStream.h>
-
-
-using namespace rfb;
-using namespace rdr;
-
-static LogWriter vlog("HTTPServer");
-
-const int clientWaitTimeMillis = 20000;
-const int idleTimeoutSecs = 5 * 60;
-
-
-//
-// -=- LineReader
-//     Helper class which is repeatedly called until a line has been read
-//     (lines end in \n or \r\n).
-//     Returns true when line complete, and resets internal state so that
-//     next read() call will start reading a new line.
-//     Only one buffer is kept - process line before reading next line!
-//
-
-class LineReader : public CharArray {
-public:
-  LineReader(InStream& is_, int l)
-    : CharArray(l), is(is_), pos(0), len(l), bufferOverrun(false) {}
-
-  // Returns true if line complete, false otherwise
-  bool read() {
-    while (is.checkNoWait(1)) {
-      char c = is.readU8();
-
-      if (c == '\n') {
-        if (pos && (buf[pos-1] == '\r'))
-          pos--;
-        bufferOverrun = false;
-        buf[pos++] = 0;
-        pos = 0;
-        return true;
-      }
-
-      if (pos == (len-1)) {
-        bufferOverrun = true;
-        buf[pos] = 0;
-        return true;
-      }
-
-      buf[pos++] = c;
-    }
-
-    return false;
-  }
-  bool didBufferOverrun() const {return bufferOverrun;}
-protected:
-  InStream& is;
-  int pos, len;
-  bool bufferOverrun;
-};
-
-
-//
-// -=- HTTPServer::Session
-//     Manages the internal state for an HTTP session.
-//     processHTTP returns true when request has completed,
-//     indicating that socket & session data can be deleted.
-//
-
-class rfb::HTTPServer::Session {
-public:
-  Session(network::Socket& s, rfb::HTTPServer& srv)
-    : contentType(0), contentLength(-1), lastModified(-1),
-      line(s.inStream(), 256), sock(s),
-      server(srv), state(ReadRequestLine), lastActive(time(0)) {
-  }
-  ~Session() {
-  }
-
-  void writeResponse(int result, const char* text);
-  bool writeResponse(int code);
-
-  bool processHTTP();
-
-  network::Socket* getSock() const {return &sock;}
-
-  int checkIdleTimeout();
-protected:
-  CharArray uri;
-  const char* contentType;
-  int contentLength;
-  time_t lastModified;
-  LineReader line;
-  network::Socket& sock;
-  rfb::HTTPServer& server;
-  enum {ReadRequestLine, ReadHeaders, WriteResponse} state;
-  enum {GetRequest, HeadRequest} request;
-  time_t lastActive;
-};
-
-
-// - Internal helper routines
-
-void
-copyStream(InStream& is, OutStream& os) {
-  try {
-    while (1) {
-      os.writeU8(is.readU8());
-    }
-  } catch (rdr::EndOfStream&) {
-  }
-}
-
-void writeLine(OutStream& os, const char* text) {
-  os.writeBytes(text, strlen(text));
-  os.writeBytes("\r\n", 2);
-}
-
-
-// - Write an HTTP-compliant response to the client
-
-
-void
-HTTPServer::Session::writeResponse(int result, const char* text) {
-  char buffer[1024];
-  if (strlen(text) > 512)
-    throw new rdr::Exception("Internal error - HTTP response text too big");
-  sprintf(buffer, "%s %d %s", "HTTP/1.1", result, text);
-  OutStream& os=sock.outStream();
-  writeLine(os, buffer);
-  writeLine(os, "Server: TigerVNC/4.0");
-  time_t now = time(0);
-  struct tm* tm = gmtime(&now);
-  strftime(buffer, 1024, "Date: %a, %d %b %Y %H:%M:%S GMT", tm);
-  writeLine(os, buffer);
-  if (lastModified == (time_t)-1 || lastModified == 0)
-    lastModified = now;
-  tm = gmtime(&lastModified);
-  strftime(buffer, 1024, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT", tm);
-  writeLine(os, buffer);
-  if (contentLength != -1) {
-    sprintf(buffer,"Content-Length: %d",contentLength);
-    writeLine(os, buffer);
-  }
-  writeLine(os, "Connection: close");
-  os.writeBytes("Content-Type: ", 14);
-  if (result == 200) {
-    if (!contentType)
-      contentType = guessContentType(uri.buf, "text/html");
-    os.writeBytes(contentType, strlen(contentType));
-  } else {
-    os.writeBytes("text/html", 9);
-  }
-  os.writeBytes("\r\n", 2);
-  writeLine(os, "");
-  if (result != 200) {
-    writeLine(os, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">");
-    writeLine(os, "<HTML><HEAD>");
-    sprintf(buffer, "<TITLE>%d %s</TITLE>", result, text);
-    writeLine(os, buffer);
-    writeLine(os, "</HEAD><BODY><H1>");
-    writeLine(os, text);
-    writeLine(os, "</H1></BODY></HTML>");
-    sock.outStream().flush();
-  }
-}
-
-bool
-HTTPServer::Session::writeResponse(int code) {
-  switch (code) {
-  case 200: writeResponse(code, "OK"); break;
-  case 400: writeResponse(code, "Bad Request"); break;
-  case 404: writeResponse(code, "Not Found"); break;
-  case 501: writeResponse(code, "Not Implemented"); break;
-  default: writeResponse(500, "Unknown Error"); break;
-  };
-
-  // This return code is passed straight out of processHTTP().
-  // true indicates that the request has been completely processed.
-  return true;
-}
-
-// - Main HTTP request processing routine
-
-bool
-HTTPServer::Session::processHTTP() {
-  lastActive = time(0);
-
-  while (sock.inStream().checkNoWait(1)) {
-
-    switch (state) {
-
-      // Reading the Request-Line
-    case ReadRequestLine:
-
-      // Either read a line, or run out of incoming data
-      if (!line.read())
-        return false;
-
-      // We have read a line!  Skip it if it's blank
-      if (strlen(line.buf) == 0)
-        continue;
-
-      // The line contains a request to process.
-      {
-        char method[16], path[128], version[16];
-        int matched = sscanf(line.buf, "%15s%127s%15s",
-          method, path, version);
-        if (matched != 3)
-          return writeResponse(400);
-
-        // Store the required "method"
-        if (strcmp(method, "GET") == 0)
-          request = GetRequest;
-        else if (strcmp(method, "HEAD") == 0)
-          request = HeadRequest;
-        else
-          return writeResponse(501);
-
-        // Store the URI to the "document"
-        uri.buf = strDup(path);
-      }
-
-      // Move on to reading the request headers
-      state = ReadHeaders;
-      break;
-
-      // Reading the request headers
-    case ReadHeaders:
-
-      // Try to read a line
-      if (!line.read())
-        return false;
-
-      // Skip headers until we hit a blank line
-      if (strlen(line.buf) != 0)
-        continue;
-
-      // Headers ended - write the response!
-      {
-        CharArray address(sock.getPeerAddress());
-        vlog.info("getting %s for %s", uri.buf, address.buf);
-        contentLength = -1;
-        lastModified = -1;
-        InStream* data = server.getFile(uri.buf, &contentType, &contentLength,
-                                        &lastModified);
-        if (!data)
-          return writeResponse(404);
-
-        try {
-          writeResponse(200);
-          if (request == GetRequest)
-            copyStream(*data, sock.outStream());
-          sock.outStream().flush();
-        } catch (rdr::Exception& e) {
-          vlog.error("error writing HTTP document:%s", e.str());
-        }
-        delete data;
-      }
-
-      // The operation is complete!
-      return true;
-
-    default:
-      throw rdr::Exception("invalid HTTPSession state!");
-    };
-
-  }
-
-  // Indicate that we're still processing the HTTP request.
-  return false;
-}
-
-int HTTPServer::Session::checkIdleTimeout() {
-  time_t now = time(0);
-  int timeout = (lastActive + idleTimeoutSecs) - now;
-  if (timeout > 0)
-    return secsToMillis(timeout);
-  sock.shutdown();
-  return 0;
-}
-
-// -=- Constructor / destructor
-
-HTTPServer::HTTPServer() {
-}
-
-HTTPServer::~HTTPServer() {
-  std::list<Session*>::iterator i;
-  for (i=sessions.begin(); i!=sessions.end(); i++)
-    delete *i;
-}
-
-
-// -=- SocketServer interface implementation
-
-void
-HTTPServer::addSocket(network::Socket* sock, bool) {
-  Session* s = new Session(*sock, *this);
-  if (!s) {
-    sock->shutdown();
-  } else {
-    sock->inStream().setTimeout(clientWaitTimeMillis);
-    sock->outStream().setTimeout(clientWaitTimeMillis);
-    sessions.push_front(s);
-  }
-}
-
-void
-HTTPServer::removeSocket(network::Socket* sock) {
-  std::list<Session*>::iterator i;
-  for (i=sessions.begin(); i!=sessions.end(); i++) {
-    if ((*i)->getSock() == sock) {
-      delete *i;
-      sessions.erase(i);
-      return;
-    }
-  }
-}
-
-void
-HTTPServer::processSocketReadEvent(network::Socket* sock) {
-  std::list<Session*>::iterator i;
-  for (i=sessions.begin(); i!=sessions.end(); i++) {
-    if ((*i)->getSock() == sock) {
-      try {
-        if ((*i)->processHTTP()) {
-          vlog.info("completed HTTP request");
-          sock->shutdown();
-        }
-      } catch (rdr::Exception& e) {
-        vlog.error("untrapped: %s", e.str());
-        sock->shutdown();
-      }
-      return;
-    }
-  }
-  throw rdr::Exception("invalid Socket in HTTPServer");
-}
-
-void
-HTTPServer::processSocketWriteEvent(network::Socket* sock) {
-  std::list<Session*>::iterator i;
-  for (i=sessions.begin(); i!=sessions.end(); i++) {
-    if ((*i)->getSock() == sock) {
-      try {
-        sock->outStream().flush();
-      } catch (rdr::Exception& e) {
-        vlog.error("untrapped: %s", e.str());
-        sock->shutdown();
-      }
-      return;
-    }
-  }
-  throw rdr::Exception("invalid Socket in HTTPServer");
-}
-
-void HTTPServer::getSockets(std::list<network::Socket*>* sockets)
-{
-  sockets->clear();
-  std::list<Session*>::iterator ci;
-  for (ci = sessions.begin(); ci != sessions.end(); ci++) {
-    sockets->push_back((*ci)->getSock());
-  }
-}
-
-int HTTPServer::checkTimeouts() {
-  std::list<Session*>::iterator ci;
-  int timeout = 0;
-  for (ci = sessions.begin(); ci != sessions.end(); ci++) {
-    soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
-  }
-  return timeout;
-}
-
-
-// -=- Default getFile implementation
-
-InStream*
-HTTPServer::getFile(const char* name, const char** contentType,
-                    int* contentLength, time_t* lastModified)
-{
-  return 0;
-}
-
-const char*
-HTTPServer::guessContentType(const char* name, const char* defType) {
-  CharArray file, ext;
-  if (!strSplit(name, '.', &file.buf, &ext.buf))
-    return defType;
-  if (strcasecmp(ext.buf, "html") == 0 ||
-    strcasecmp(ext.buf, "htm") == 0) {
-    return "text/html";
-  } else if (strcasecmp(ext.buf, "txt") == 0) {
-    return "text/plain";
-  } else if (strcasecmp(ext.buf, "gif") == 0) {
-    return "image/gif";
-  } else if (strcasecmp(ext.buf, "jpg") == 0) {
-    return "image/jpeg";
-  } else if (strcasecmp(ext.buf, "jar") == 0) {
-    return "application/java-archive";
-  } else if (strcasecmp(ext.buf, "exe") == 0) {
-    return "application/octet-stream";
-  }
-  return defType;
-}
diff --git a/common/rfb/HTTPServer.h b/common/rfb/HTTPServer.h
deleted file mode 100644
index 04ef499..0000000
--- a/common/rfb/HTTPServer.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * 
- * This 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 2 of the License, or
- * (at your option) any later version.
- * 
- * This software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
- * USA.
- */
-
-// -=- HTTPServer.h
-
-// Single-threaded HTTP server implementation.
-// All I/O is handled by the processSocketEvent routine,
-// which is called by the main-loop of the VNC server whenever
-// there is an event on an HTTP socket.
-
-#ifndef __RFB_HTTP_SERVER_H__
-#define __RFB_HTTP_SERVER_H__
-
-#include <rdr/MemInStream.h>
-#include <rfb/UpdateTracker.h>
-#include <rfb/Configuration.h>
-#include <network/Socket.h>
-#include <time.h>
-
-namespace rfb {
-
-  class HTTPServer : public network::SocketServer {
-  public:
-    // -=- Constructors
-
-    // - HTTPServer(files)
-    //   Create an HTTP server which will use the getFile method
-    //   to satisfy HTTP GET requests.
-    HTTPServer();
-
-    virtual ~HTTPServer();
-
-    // SocketServer interface
-
-    // addSocket()
-    //   This causes the server to perform HTTP protocol on the
-    //   supplied socket.
-    virtual void addSocket(network::Socket* sock, bool outgoing=false);
-
-    // removeSocket()
-    //   Could clean up socket-specific resources here.
-    virtual void removeSocket(network::Socket* sock);
-
-    // getSockets() gets a list of sockets.  This can be used to generate an
-    // fd_set for calling select().
-    virtual void getSockets(std::list<network::Socket*>* sockets);
-
-    // processSocketReadEvent()
-    //   The platform-specific side of the server implementation calls
-    //   this method whenever data arrives on one of the active
-    //   network sockets.
-    virtual void processSocketReadEvent(network::Socket* sock);
-
-    // processSocketWriteEvent()
-    //   Similar to processSocketReadEvent(), but called when it is
-    //   possible to write more data to a socket.
-    virtual void processSocketWriteEvent(network::Socket* sock);
-
-    // Check for socket timeouts
-    virtual int checkTimeouts();
-
-
-    // -=- File interface
-
-    // - getFile is passed the path portion of a URL and returns an
-    //   InStream containing the data to return.  If the requested
-    //   file is available then the contentType should be set to the
-    //   type of the file, or left untouched if the file type is to
-    //   be determined automatically by HTTPServer.
-    //   If the file is not available then null is returned.
-    //   Overridden getFile functions should call the default version
-    //   if they do not recognise a path name.
-    //   NB: The caller assumes ownership of the returned InStream.
-    //   NB: The contentType is statically allocated by the getFile impl.
-    //   NB: contentType is *guaranteed* to be valid when getFile is called.
-
-    virtual rdr::InStream* getFile(const char* name, const char** contentType,
-                                   int* contentLength, time_t* lastModified);
-
-    // - guessContentType is passed the name of a file and returns the
-    //   name of an HTTP content type, based on the file's extension.  If
-    //   the extension isn't recognised then defType is returned.  This can
-    //   be used from getFile to easily default to the supplied contentType,
-    //   or by passing zero in to determine whether a type is recognised or
-    //   not.
-
-    static const char* guessContentType(const char* name, const char* defType);
-
-  protected:
-    class Session;
-    std::list<Session*> sessions;
-  };
-}
-
-#endif
-
diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx
index efc26ac..a79abee 100644
--- a/common/rfb/SConnection.cxx
+++ b/common/rfb/SConnection.cxx
@@ -260,13 +260,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)
@@ -342,6 +343,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/SSecurityTLS.cxx b/common/rfb/SSecurityTLS.cxx
index 49532f5..d5ef47e 100644
--- a/common/rfb/SSecurityTLS.cxx
+++ b/common/rfb/SSecurityTLS.cxx
@@ -163,7 +163,8 @@
     throw AuthFailureException("TLS Handshake failed");
   }
 
-  vlog.debug("Handshake completed");
+  vlog.debug("TLS handshake completed with %s",
+             gnutls_session_get_desc(session));
 
   sc->setStreams(tlsis, tlsos);
 
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index f1591f4..8758362 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_), updates(false),
     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()
 {
@@ -192,8 +199,9 @@
 {
   try {
     if (!authenticated()) return;
-    if (cp.width && cp.height && (server->pb->width() != cp.width ||
-                                  server->pb->height() != cp.height))
+    if (cp.width && cp.height &&
+        (server->getPixelBuffer()->width() != cp.width ||
+         server->getPixelBuffer()->height() != cp.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
@@ -210,11 +218,11 @@
       //  updates.add_changed(Rect(0, cp.height, cp.width,
       //                           server->pb->height()));
 
-      damagedCursorRegion.assign_intersect(server->pb->getRect());
+      damagedCursorRegion.assign_intersect(server->getPixelBuffer()->getRect());
 
-      cp.width = server->pb->width();
-      cp.height = server->pb->height();
-      cp.screenLayout = server->screenLayout;
+      cp.width = server->getPixelBuffer()->width();
+      cp.height = server->getPixelBuffer()->height();
+      cp.screenLayout = server->getScreenLayout();
       if (state() == RFBSTATE_NORMAL) {
         // We should only send EDS to client asking for both
         if (!writer()->writeExtendedDesktopSize()) {
@@ -226,12 +234,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());
@@ -269,7 +277,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);
@@ -312,36 +320,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
@@ -388,7 +366,7 @@
   if (!cp.supportsLocalCursorWithAlpha &&
       !cp.supportsLocalCursor && !cp.supportsLocalXCursor)
     return true;
-  if (!server->cursorPos.equals(pointerEventPos) &&
+  if (!server->getCursorPos().equals(pointerEventPos) &&
       (time(0) - pointerEventTime) > 0)
     return true;
 
@@ -412,84 +390,40 @@
 
 void VNCSConnectionST::authSuccess()
 {
-  lastEventTime = time(0);
-
-  server->startDesktop();
+  if (rfb::Server::idleTimeout)
+    idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
 
   // - Set the connection parameters appropriately
-  cp.width = server->pb->width();
-  cp.height = server->pb->height();
-  cp.screenLayout = server->screenLayout;
+  cp.width = server->getPixelBuffer()->width();
+  cp.height = server->getPixelBuffer()->height();
+  cp.screenLayout = server->getScreenLayout();
   cp.setName(server->getName());
-  cp.setLEDState(server->ledState);
+  cp.setLEDState(server->getLEDState());
   
   // - Set the default pixel format
-  cp.setPF(server->pb->getPF());
+  cp.setPF(server->getPixelBuffer()->getPF());
   char buffer[256];
   cp.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)
@@ -503,37 +437,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;
 };
 
@@ -542,9 +471,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)
@@ -552,18 +481,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))) {
@@ -581,7 +500,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)
@@ -591,12 +510,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);
         }
       }
 
@@ -611,7 +530,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
@@ -625,15 +544,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();
@@ -659,21 +578,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);
 
@@ -712,30 +631,12 @@
 {
   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()->writeExtendedDesktopSize(reasonClient, resultInvalid,
-                                       fb_width, fb_height, layout);
-    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()->writeExtendedDesktopSize(reasonClient, result,
                                      fb_width, fb_height, layout);
-
-  // 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[])
@@ -847,6 +748,9 @@
     close(e.str());
   }
 
+  if (t == &idleTimer)
+    close("Idle timeout");
+
   return false;
 }
 
@@ -1001,7 +905,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;
@@ -1124,7 +1028,7 @@
   if (!authenticated())
     return;
 
-  cp.screenLayout = server->screenLayout;
+  cp.screenLayout = server->getScreenLayout();
 
   if (state() != RFBSTATE_NORMAL)
     return;
@@ -1148,7 +1052,7 @@
     cp.setCursor(emptyCursor);
     clientHasCursor = false;
   } else {
-    cp.setCursor(*server->cursor);
+    cp.setCursor(*server->getCursor());
     clientHasCursor = true;
   }
 
@@ -1188,43 +1092,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..c992d14 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);
 
@@ -178,6 +150,7 @@
     void setLEDState(unsigned int state);
     void setSocketTimeouts();
 
+  private:
     network::Socket* sock;
     CharArray peerEndpoint;
     bool reverseConnection;
@@ -204,15 +177,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 8cc04f7..40580b1 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()
@@ -467,6 +417,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,
@@ -504,7 +531,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)
@@ -529,11 +556,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()
@@ -689,50 +782,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/common/rfb/util.h b/common/rfb/util.h
index b678b89..9e59bd3 100644
--- a/common/rfb/util.h
+++ b/common/rfb/util.h
@@ -50,7 +50,7 @@
     CharArray() : buf(0) {}
     CharArray(char* str) : buf(str) {} // note: assumes ownership
     CharArray(int len) {
-      buf = new char[len];
+      buf = new char[len]();
     }
     ~CharArray() {
       delete [] buf;
diff --git a/contrib/packages/deb/ubuntu-precise/debian/copyright b/contrib/packages/deb/ubuntu-precise/debian/copyright
index 003dcc4..290fbdf 100644
--- a/contrib/packages/deb/ubuntu-precise/debian/copyright
+++ b/contrib/packages/deb/ubuntu-precise/debian/copyright
@@ -2,7 +2,7 @@
 on Tue, 02 Jul 2013 21:33:24 +0500 using the tightvnc package as a base.
 
 It was downloaded from:
-	http://www.tigervnc.org/
+	https://www.tigervnc.org/
 
 COPYRIGHT:
 ==========
diff --git a/contrib/packages/deb/ubuntu-trusty/debian/copyright b/contrib/packages/deb/ubuntu-trusty/debian/copyright
index 003dcc4..290fbdf 100644
--- a/contrib/packages/deb/ubuntu-trusty/debian/copyright
+++ b/contrib/packages/deb/ubuntu-trusty/debian/copyright
@@ -2,7 +2,7 @@
 on Tue, 02 Jul 2013 21:33:24 +0500 using the tightvnc package as a base.
 
 It was downloaded from:
-	http://www.tigervnc.org/
+	https://www.tigervnc.org/
 
 COPYRIGHT:
 ==========
diff --git a/contrib/packages/deb/ubuntu-xenial/debian/copyright b/contrib/packages/deb/ubuntu-xenial/debian/copyright
index 003dcc4..290fbdf 100644
--- a/contrib/packages/deb/ubuntu-xenial/debian/copyright
+++ b/contrib/packages/deb/ubuntu-xenial/debian/copyright
@@ -2,7 +2,7 @@
 on Tue, 02 Jul 2013 21:33:24 +0500 using the tightvnc package as a base.
 
 It was downloaded from:
-	http://www.tigervnc.org/
+	https://www.tigervnc.org/
 
 COPYRIGHT:
 ==========
diff --git a/contrib/packages/rpm/el5/SPECS/tigervnc.spec b/contrib/packages/rpm/el5/SPECS/tigervnc.spec
index 51388af..f83c15a 100644
--- a/contrib/packages/rpm/el5/SPECS/tigervnc.spec
+++ b/contrib/packages/rpm/el5/SPECS/tigervnc.spec
@@ -115,7 +115,6 @@
 
 # xorg requires newer versions of automake, & autoconf than are available with el5. Use el6 versions.
 BuildRequires: automake >= 1.11, autoconf >= 2.60, libtool >= 1.4, gettext >= 0.14.4, gettext-devel >= 0.14.4, bison-devel, python26
-BuildRequires: java-devel, jpackage-utils
 BuildRequires: pam-devel
 BuildRequires: cmake28
 BuildRequires: pkgconfig >= 0.20
@@ -247,18 +246,6 @@
 of TigerVNC server, allowing others to access the desktop on your
 machine.
 
-%package server-applet
-Summary: Java TigerVNC viewer applet for TigerVNC server
-Group: User Interface/X
-Requires: tigervnc-server, java, jpackage-utils
-%if 0%{?fedora} >= 10 || 0%{?rhel} >= 6 || 0%{?centos} >= 6
-BuildArch: noarch
-%endif
-
-%description server-applet
-The Java TigerVNC viewer applet for web browsers. Install this package to allow
-clients to use web browser when connect to the TigerVNC server.
-
 %package license
 Summary: License of TigerVNC suite
 Group: User Interface/X
@@ -885,7 +872,7 @@
   --prefix=%{_prefix} --libdir=%{_libdir} --mandir=%{_datadir}/man \
   --sysconfdir=%{_sysconfdir} --localstatedir=%{_localstatedir} \
   --with-vendor-name="The TigerVNC Project" --with-vendor-name-short="TigerVNC" \
-  --with-vendor-web="http://www.tigervnc.org" \
+  --with-vendor-web="https://www.tigervnc.org" \
   --disable-xorg --disable-xnest --disable-xvfb --disable-dmx \
   --disable-xwin --disable-xephyr --disable-kdrive --disable-wayland \
   --with-pic --enable-static --disable-shared --enable-xinerama \
@@ -915,21 +902,6 @@
 make
 popd
 
-# Build Java applet
-pushd java
-%{cmake28} \
-%if !%{_self_signed}
-  -DJAVA_KEYSTORE=%{_keystore} \
-  -DJAVA_KEYSTORE_TYPE=%{_keystore_type} \
-  -DJAVA_KEY_ALIAS=%{_key_alias} \
-  -DJAVA_STOREPASS=":env STOREPASS" \
-  -DJAVA_KEYPASS=":env KEYPASS" \
-  -DJAVA_TSA_URL=https://timestamp.geotrust.com/tsa .
-%endif
-
-JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF8" make
-popd
-
 %install
 make install DESTDIR=$RPM_BUILD_ROOT
 
@@ -948,13 +920,6 @@
 install -m644 %{SOURCE1} $RPM_BUILD_ROOT%{_sysconfdir}/init.d/vncserver
 install -m644 %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/vncservers
 
-# Install Java applet
-pushd java
-mkdir -p $RPM_BUILD_ROOT%{_datadir}/vnc/classes
-install -m755 VncViewer.jar $RPM_BUILD_ROOT%{_datadir}/vnc/classes
-install -m644 com/tigervnc/vncviewer/index.vnc $RPM_BUILD_ROOT%{_datadir}/vnc/classes
-popd
-
 %find_lang %{name} %{name}.lang
 
 %if %{_bootstrap}
@@ -1024,11 +989,6 @@
 %{_mandir}/man1/vncconfig.1*
 %{_libdir}/*
 
-%files server-applet
-%defattr(-,root,root,-)
-%doc java/com/tigervnc/vncviewer/README
-%{_datadir}/vnc/classes/*
-
 %files license
 %defattr(-,root,root,-)
 %doc LICENCE.TXT
diff --git a/contrib/packages/rpm/el6/SPECS/tigervnc.spec b/contrib/packages/rpm/el6/SPECS/tigervnc.spec
index 09843b2..5019399 100644
--- a/contrib/packages/rpm/el6/SPECS/tigervnc.spec
+++ b/contrib/packages/rpm/el6/SPECS/tigervnc.spec
@@ -37,7 +37,6 @@
 BuildRequires: libxkbfile-devel, openssl-devel, libpciaccess-devel
 BuildRequires: mesa-libGL-devel, libXinerama-devel, ImageMagick
 BuildRequires: freetype-devel, libXdmcp-devel
-BuildRequires: java-devel, jpackage-utils
 BuildRequires: libjpeg-turbo-devel, pam-devel
 BuildRequires: cmake >= 2.8
 %if !%{_bootstrap}
@@ -120,16 +119,6 @@
 to access the desktop on your machine.
 %endif
 
-%package server-applet
-Summary: Java TigerVNC viewer applet for TigerVNC server
-Group: User Interface/X
-Requires: tigervnc-server, java, jpackage-utils
-BuildArch: noarch
-
-%description server-applet
-The Java TigerVNC viewer applet for web browsers. Install this package to allow
-clients to use web browser when connect to the TigerVNC server.
-
 %package license
 Summary: License of TigerVNC suite
 Group: User Interface/X
@@ -326,21 +315,6 @@
 make
 popd
 
-# Build Java applet
-pushd java
-%{cmake} \
-%if !%{_self_signed}
-	-DJAVA_KEYSTORE=%{_keystore} \
-	-DJAVA_KEYSTORE_TYPE=%{_keystore_type} \
-	-DJAVA_KEY_ALIAS=%{_key_alias} \
-	-DJAVA_STOREPASS=":env STOREPASS" \
-	-DJAVA_KEYPASS=":env KEYPASS" \
-	-DJAVA_TSA_URL=http://timestamp.geotrust.com/tsa .
-%endif
-
-make
-popd
-
 %install
 %if %{_bootstrap}
 for l in gmp libtasn1 nettle gnutls libpng fltk; do
@@ -364,13 +338,6 @@
 install -m644 %{SOURCE1} $RPM_BUILD_ROOT%{_sysconfdir}/init.d/vncserver
 install -m644 %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/vncservers
 
-# Install Java applet
-pushd java
-mkdir -p $RPM_BUILD_ROOT%{_datadir}/vnc/classes
-install -m755 VncViewer.jar $RPM_BUILD_ROOT%{_datadir}/vnc/classes
-install -m644 com/tigervnc/vncviewer/index.vnc $RPM_BUILD_ROOT%{_datadir}/vnc/classes
-popd
-
 %find_lang %{name} %{name}.lang
 
 # remove unwanted files
@@ -433,11 +400,6 @@
 %{_libdir}/xorg/modules/extensions/libvnc.so
 %endif
 
-%files server-applet
-%defattr(-,root,root,-)
-%doc java/com/tigervnc/vncviewer/README
-%{_datadir}/vnc/classes/*
-
 %files license
 %defattr(-,root,root,-)
 %doc LICENCE.TXT
diff --git a/contrib/packages/rpm/el7/SPECS/tigervnc.spec b/contrib/packages/rpm/el7/SPECS/tigervnc.spec
index ec36ccc..1b8630a 100644
--- a/contrib/packages/rpm/el7/SPECS/tigervnc.spec
+++ b/contrib/packages/rpm/el7/SPECS/tigervnc.spec
@@ -32,7 +32,6 @@
 BuildRequires:  libxkbfile-devel, openssl-devel, libpciaccess-devel
 BuildRequires:  mesa-libGL-devel, libXinerama-devel, ImageMagick
 BuildRequires:  freetype-devel, libXdmcp-devel, libXfont2-devel
-BuildRequires:  java-devel, jpackage-utils
 BuildRequires:  libjpeg-turbo-devel, gnutls-devel, pam-devel
 BuildRequires:  systemd, cmake
 
@@ -114,16 +113,6 @@
 to access the desktop on your machine.
 %endif
 
-%package server-applet
-Summary:        Java TigerVNC viewer applet for TigerVNC server
-Group:          User Interface/X
-Requires:       tigervnc-server, java, jpackage-utils
-BuildArch:      noarch
-
-%description server-applet
-The Java TigerVNC viewer applet for web browsers. Install this package to allow
-clients to use web browser when connect to the TigerVNC server.
-
 %package license
 Summary:        License of TigerVNC suite
 Group:          User Interface/X
@@ -250,21 +239,6 @@
 make
 popd
 
-# Build Java applet
-pushd java
-%{cmake} \
-%if !%{_self_signed}
-	-DJAVA_KEYSTORE=%{_keystore} \
-	-DJAVA_KEYSTORE_TYPE=%{_keystore_type} \
-	-DJAVA_KEY_ALIAS=%{_key_alias} \
-	-DJAVA_STOREPASS=":env STOREPASS" \
-	-DJAVA_KEYPASS=":env KEYPASS" \
-	-DJAVA_TSA_URL=http://timestamp.geotrust.com/tsa .
-%endif
-
-make
-popd
-
 %install
 %if %{_bootstrap}
 for l in fltk; do
@@ -291,13 +265,6 @@
 mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig
 install -m644 %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/vncservers
 
-# Install Java applet
-pushd java
-mkdir -p $RPM_BUILD_ROOT%{_datadir}/vnc/classes
-install -m755 VncViewer.jar $RPM_BUILD_ROOT%{_datadir}/vnc/classes
-install -m644 com/tigervnc/vncviewer/index.vnc $RPM_BUILD_ROOT%{_datadir}/vnc/classes
-popd
-
 %find_lang %{name} %{name}.lang
 
 # remove unwanted files
@@ -370,11 +337,6 @@
 %config %{_sysconfdir}/X11/xorg.conf.d/10-libvnc.conf
 %endif
 
-%files server-applet
-%defattr(-,root,root,-)
-%doc java/com/tigervnc/vncviewer/README
-%{_datadir}/vnc/classes/*
-
 %files license
 %doc %{_docdir}/%{name}-%{version}/LICENCE.TXT
 
diff --git a/doc/keyboard-test.txt b/doc/keyboard-test.txt
index e839177..e5fcbd7 100644
--- a/doc/keyboard-test.txt
+++ b/doc/keyboard-test.txt
@@ -4,9 +4,11 @@
 Platform specific tests/issues are marked with [].
 
 These tests are primarily about what is sent over the protocol and some
-may be difficult or impossible to test using normal applications. In
-these cases you can turn on debug logging in either the client or the
-server by specifying "-Log *:stderr:100".
+may be difficult or impossible to test using normal applications. One
+such case is that xev will see fake KeyRelease events on key repeat,
+even when no such events were sent by the client. In these cases you
+can turn on debug logging in either the client or the server by
+specifying "-Log *:stderr:100".
 
 We currently have a limitation in Xvnc where it can run out of symbols,
 resulting in nothing being sent to the applications. Just run setxkbmap
@@ -103,7 +105,7 @@
 
   - F1-F24 (FIXME: F14-F15 broken on OS X)
   - Tab, Space, Backspace, Return, Esc
-  - LeftTab sends Tab [X11?]
+  - LeftTab sends Tab [X11]
   - PrntScrn, ScrollLock, Pause [X11, Win]
   - Help [X11?, OS X]
   - Insert [X11, Win]
@@ -157,6 +159,45 @@
 
   - CapsLock
   - NumLock (hidden state on macOS)
-  - ScollLock [X11, Win]
+  - ScollLock* [X11, Win]
+
+  * Usually needs to be enabled with: xmodmap -e "add mod3 = Scroll_Lock"
 
 - Virtual keyboard (as much of the above as is possible)
+
+Server
+------
+
+- Fake key events
+
+  An extra press or release should be added before the real key press
+  in order to get the correct state to get the desired symbol. A second
+  fake event should be added after the real key press to restore state.
+  No extra events should be added for a key release.
+
+  Possible fake keys:
+
+  - Shift
+  - AltGr
+
+  - Shift+Tab bypasses this and never fake releases Shift
+
+- Alternative keys
+
+  A semantically equivalent key is sent when the desired key cannot be
+  reached in the current keyboard state:
+
+  - Left modifier <=> Right modifier (e.g. Alt_R for Alt_L)
+  - Keypad key <=> Standard key
+  - ISO_Level3_Shift <=> Mode_Switch
+
+- Meta for Shift+Alt (if server keymap agrees)
+
+- Lock key heuristics (for clients without lock key extension)
+
+  - CapsLock is corrected when A-Z or a-z are pressed
+  - CapsLock is corrected when Shift and A-Z or a-z are pressed*
+  - NumLock is corrected when 0-9 on the keypad are pressed
+
+  * Gives incorrect behaviour if the client doesn't have "Shift cancels
+    CapsLock behaviour", e.g. macOS
diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt
index 340dec1..23137f7 100644
--- a/java/CMakeLists.txt
+++ b/java/CMakeLists.txt
@@ -177,12 +177,3 @@
     -P ${SRCDIR}/cmake/SignJar.cmake)
 
 add_custom_target(java ALL DEPENDS VncViewer.jar)
-
-if(NOT WIN32)
-  install(FILES
-    ${BINDIR}/VncViewer.jar
-    ${SRCDIR}/${CLASSPATH}/README
-    ${SRCDIR}/${CLASSPATH}/index.vnc
-    ${SRCDIR}/${CLASSPATH}/favicon.ico
-    DESTINATION ${DATA_DIR}/vnc/classes)
-endif()
diff --git a/java/com/tigervnc/vncviewer/README b/java/com/tigervnc/vncviewer/README
index d0baa93..3019f8d 100644
--- a/java/com/tigervnc/vncviewer/README
+++ b/java/com/tigervnc/vncviewer/README
@@ -27,86 +27,24 @@
 Installation
 ============
 
-There are three basic ways to use the TigerVNC Java Viewer:
+The TigerVNC Java Viewer requires installation of either a JRE (Java
+Runtime Environment) or a JDK (Java Development Kit).  If VncViewer.jar is
+in the current directory, then the TigerVNC Java Viewer can be launched
+with the following command line:
 
-  1. Running the applet as part of a TigerVNC Server installation.
+   java -jar VncViewer.jar [parameters]
 
-     Both the Unix and Windows versions of the TigerVNC Server include a small
-     built-in HTTP server that can serve the TigerVNC Java Viewer to web
-     clients.  This enables easy access to the shared desktop without the need
-     to install any software on the client machine.
-
-     The Unix TigerVNC Server (Xvnc) is able to serve up any set of files that
-     are present in a particular directory, which is specified in the -httpd
-     argument to Xvnc.  The default version of the vncserver script will look
-     for a directory called vnc/classes, one level up from the directory
-     containing the vncserver script, then it will look for a directory called
-     /usr/share/vnc/classes, then /usr/local/vnc/classes.  It will set the
-     -httpd argument to Xvnc to the first one of these VNC classes directories
-     it finds.  Thus, one can easily deploy a modified version of the TigerVNC
-     Java Viewer by simply copying a new version of VncViewer.jar and/or
-     index.vnc into the VNC classes directory.
-
-     On Windows and Linux, the embedded applet can be drag-undocked from the 
-     browser window and converted to a standalone application. The drag
-     gesture ALT+drag on Windows, and SHIFT+drag on Linux.
-
-     In the case of the Windows TigerVNC Server, VncViewer.jar and index.vnc
-     are embedded as resources in the WinVNC executable, so deploying a
-     modified version of the TigerVNC Java Viewer on a Windows server requires
-     rebuilding WinVNC.
-
-  2. Running the applet from a standalone web server.
-
-     Another possibility for using the TigerVNC Java Viewer is to install it
-     under a fully-functional HTTP server, such as Apache or IIS.  Due to Java
-     security restrictions, the applet must be signed in order for it to
-     connect to a VNC server running on a different machine from the HTTP
-     server.
-
-     One can install the TigerVNC Java Viewer by simply copying the .class and
-     .jar files into a directory that is under the control of the HTTP server.
-     Also, an HTML page should be created to act as a the base document for the
-     TigerVNC Java Viewer applet (an example named index.html is provided in
-     this directory.  Modify this file to suit your specific needs.)
-
-  3. Running the viewer as a standalone application.
-
-     Finally, the TigerVNC Java Viewer can be executed locally on the client
-     machine, but this method requires installation of either a JRE (Java
-     Runtime Environment) or a JDK (Java Development Kit).  If VncViewer.jar is
-     in the current directory, then the TigerVNC Java Viewer can be launched
-     with the following command line:
-
-         java -jar VncViewer.jar [parameters]
-
-     Add an argument of -? to the above command line to print a list of
-     optional parameters supported by VncViewer.
+Add an argument of -? to the above command line to print a list of
+optional parameters supported by VncViewer.
 
 
 Parameters
 ==========
 
 The TigerVNC Java Viewer accepts a number of optional parameters, allowing you
-to customize its behavior.
+to customize its behavior. Example:
 
-Parameters can be specified in one of the two ways, depending on how the
-TigerVNC Java Viewer is used:
-
-  1. When the TigerVNC Java Viewer is run as an applet (embedded within an HTML
-     document), parameters should be specified using the <PARAM> HTML tags
-     within the appropriate <APPLET> section.  Example:
-
-    <APPLET CODE=com.tigervnc.vncviewer.VncViewer ARCHIVE=VncViewer.jar
-      WIDTH=400 HEIGHT=300>
-      <PARAM NAME="PORT" VALUE=5901>
-      <PARAM NAME="ScalingFactor" VALUE=50>
-    </APPLET>
-
-  2. When run as a standalone application, the TigerVNC Java Viewer reads
-     parameters from the command line.  Example:
-
-     java -jar VncViewer.jar Port=5901 ScalingFactor=50
+  java -jar VncViewer.jar Port=5901 ScalingFactor=50
 
 Both parameter names and their values are case-insensitive.
 
diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java
index f96e2ec..2ef6bcf 100644
--- a/java/com/tigervnc/vncviewer/VncViewer.java
+++ b/java/com/tigervnc/vncviewer/VncViewer.java
@@ -66,7 +66,7 @@
     new String("TigerVNC Java Viewer v%s (%s)%n"+
                "Built on %s at %s%n"+
                "Copyright (C) 1999-2018 TigerVNC Team and many others (see README.rst)%n"+
-               "See http://www.tigervnc.org for information on TigerVNC.");
+               "See https://www.tigervnc.org for information on TigerVNC.");
 
   public static String version = null;
   public static String build = null;
diff --git a/java/com/tigervnc/vncviewer/favicon.ico b/java/com/tigervnc/vncviewer/favicon.ico
deleted file mode 100644
index edbd467..0000000
--- a/java/com/tigervnc/vncviewer/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/java/com/tigervnc/vncviewer/index.html b/java/com/tigervnc/vncviewer/index.html
deleted file mode 100644
index ba00e26..0000000
--- a/java/com/tigervnc/vncviewer/index.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!-- 
-     index.html - an example HTML page for the TigerVNC Java Viewer applet, to
-     be used with a standalone web server.  Before using this example, please
-     MAKE SURE to check the following:
-
-     * the CODE and ARCHIVE attributes of the <APPLET> tag should point to
-       the correct directory (this example assumes that this page is in the
-       same directory as VncViewer.jar);
--->
-
-<HTML>
-<TITLE>
-TigerVNC desktop
-</TITLE>
-<APPLET CODE="com.tigervnc.vncviewer.VncViewer" ARCHIVE="VncViewer.jar"
-        WIDTH=500>
-</APPLET>
-<BR>
-<A href="http://www.tigervnc.org/">TigerVNC site</A>
-</HTML>
diff --git a/java/com/tigervnc/vncviewer/index.vnc b/java/com/tigervnc/vncviewer/index.vnc
deleted file mode 100644
index 27bdebc..0000000
--- a/java/com/tigervnc/vncviewer/index.vnc
+++ /dev/null
@@ -1,22 +0,0 @@
-<!-- 
-     index.vnc - default HTML page for TigerVNC Java viewer applet, to be
-     used with Xvnc. On any file ending in .vnc, the HTTP server embedded in
-     Xvnc will substitute the following variables when preceded by a dollar:
-     USER, DESKTOP, DISPLAY, APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT,
-     Use two dollar signs ($$) to get a dollar sign in the generated
-     HTML page.
--->
-
-<HTML>
-<TITLE>
-$USER's $DESKTOP desktop ($DISPLAY)
-</TITLE>
-<APPLET CODE=com.tigervnc.vncviewer.VncViewer ARCHIVE=VncViewer.jar
-        WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
-<param name=PORT value=$PORT>
-<param name="Embed" value="true">
-<param name="draggable" value="true">
-</APPLET>
-<BR>
-<A href="http://www.tigervnc.org/">TigerVNC site</A>
-</HTML>
diff --git a/po/da.po b/po/da.po
index 1859f44..1577051 100644
--- a/po/da.po
+++ b/po/da.po
@@ -1,15 +1,15 @@
 # Danish translation of tigervnc.
-# Copyright (C) 2017 the TigerVNC Team (msgids)
+# Copyright (C) 2018 the TigerVNC Team (msgids)
 # This file is distributed under the same license as the tigervnc package.
-# Joe Hansen <joedalton2@yahoo.dk>, 2015, 2016, 2017.
+# Joe Hansen <joedalton2@yahoo.dk>, 2015, 2016, 2017, 2018.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: tigervnc 1.7.90\n"
+"Project-Id-Version: tigervnc 1.8.90\n"
 "Report-Msgid-Bugs-To: tigervnc-devel@googlegroups.com\n"
-"POT-Creation-Date: 2017-04-19 13:05+0000\n"
-"PO-Revision-Date: 2017-05-22 15:00+0000\n"
-"Last-Translator: Joe Hansen <joedalton2@yahoo.dk>\n"
+"POT-Creation-Date: 2018-06-13 14:22+0000\n"
+"PO-Revision-Date: 2018-08-12 13:22+0200\n"
+"Last-Translator: joe Hansen <joedalton2@yahoo.dk>\n"
 "Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
 "Language: da\n"
 "MIME-Version: 1.0\n"
@@ -17,133 +17,135 @@
 "Content-Transfer-Encoding: 8bit\n"
 "X-Bugs: Report translation errors to the Language-Team address.\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 1.8.11\n"
 
-#: vncviewer/CConn.cxx:110
+#: vncviewer/CConn.cxx:116
+#, c-format
+msgid "connected to socket %s"
+msgstr "forbundet til soklen %s"
+
+#: vncviewer/CConn.cxx:123
 #, c-format
 msgid "connected to host %s port %d"
 msgstr "forbundet til værten %s på port %d"
 
-#: vncviewer/CConn.cxx:169
+#: vncviewer/CConn.cxx:184
 #, c-format
 msgid "Desktop name: %.80s"
 msgstr "Skrivebordsnavn: %.80s"
 
-#: vncviewer/CConn.cxx:174
+#: vncviewer/CConn.cxx:189
 #, c-format
 msgid "Host: %.80s port: %d"
 msgstr "Vært: %.80s port: %d"
 
-#: vncviewer/CConn.cxx:179
+#: vncviewer/CConn.cxx:194
 #, c-format
 msgid "Size: %d x %d"
 msgstr "Størrelse: %d x %d"
 
-#: vncviewer/CConn.cxx:187
+#: vncviewer/CConn.cxx:202
 #, c-format
 msgid "Pixel format: %s"
 msgstr "Billedformat: %s"
 
-#: vncviewer/CConn.cxx:194
+#: vncviewer/CConn.cxx:209
 #, c-format
 msgid "(server default %s)"
 msgstr "(serverstandard %s)"
 
-#: vncviewer/CConn.cxx:199
+#: vncviewer/CConn.cxx:214
 #, c-format
 msgid "Requested encoding: %s"
 msgstr "Anmodt kodning: %s"
 
-#: vncviewer/CConn.cxx:204
+#: vncviewer/CConn.cxx:219
 #, c-format
 msgid "Last used encoding: %s"
 msgstr "Sidst anvendt kodning: %s"
 
-#: vncviewer/CConn.cxx:209
+#: vncviewer/CConn.cxx:224
 #, c-format
 msgid "Line speed estimate: %d kbit/s"
 msgstr "Linjehastighedsestimat: %d kbit/s"
 
-#: vncviewer/CConn.cxx:214
+#: vncviewer/CConn.cxx:229
 #, c-format
 msgid "Protocol version: %d.%d"
 msgstr "Protokolversion: %d.%d"
 
-#: vncviewer/CConn.cxx:219
+#: vncviewer/CConn.cxx:234
 #, c-format
 msgid "Security method: %s"
 msgstr "Sikkerhedsmetode: %s"
 
-#: vncviewer/CConn.cxx:343
+#: vncviewer/CConn.cxx:358
 #, c-format
 msgid "SetDesktopSize failed: %d"
 msgstr "SetDesktopSize fejlede: %d"
 
-#: vncviewer/CConn.cxx:413
+#: vncviewer/CConn.cxx:428
 msgid "Invalid SetColourMapEntries from server!"
 msgstr "Ugyldig SetColourMapEntries fra server!"
 
-#: vncviewer/CConn.cxx:489
+#: vncviewer/CConn.cxx:479
 msgid "Enabling continuous updates"
 msgstr "Aktiverer fortsættende opdateringer"
 
-#: vncviewer/CConn.cxx:559
+#: vncviewer/CConn.cxx:556
 #, c-format
 msgid "Throughput %d kbit/s - changing to quality %d"
 msgstr "Gennemløb %d kbit/s - ændrer til kvalitet %d"
 
-#: vncviewer/CConn.cxx:581
+#: vncviewer/CConn.cxx:578
 #, c-format
 msgid "Throughput %d kbit/s - full color is now %s"
 msgstr "Gennemløb %d kbit/s - fuld farve er nu %s"
 
-#: vncviewer/CConn.cxx:583
+#: vncviewer/CConn.cxx:580
 msgid "disabled"
 msgstr "deaktiveret"
 
-#: vncviewer/CConn.cxx:583
+#: vncviewer/CConn.cxx:580
 msgid "enabled"
 msgstr "aktiveret"
 
-#: vncviewer/CConn.cxx:593
+#: vncviewer/CConn.cxx:590
 #, c-format
 msgid "Using %s encoding"
 msgstr "Bruger %s-kodning"
 
-#: vncviewer/CConn.cxx:640
+#: vncviewer/CConn.cxx:637
 #, c-format
 msgid "Using pixel format %s"
 msgstr "Bruger billedpunktsformat %s"
 
-#: vncviewer/DesktopWindow.cxx:121
+#: vncviewer/DesktopWindow.cxx:122
 msgid "Invalid geometry specified!"
 msgstr "Ugyldig geometri angivet!"
 
-#: vncviewer/DesktopWindow.cxx:434
+#: vncviewer/DesktopWindow.cxx:451
 msgid "Adjusting window size to avoid accidental full screen request"
 msgstr "Justerer vinduesstørrelse for at undgå utilsigtet anmodning om fuld skærm"
 
-#: vncviewer/DesktopWindow.cxx:478
+#: vncviewer/DesktopWindow.cxx:495
 #, c-format
 msgid "Press %s to open the context menu"
 msgstr "Tryk %s for at åbne kontekstmenuen"
 
-#: vncviewer/DesktopWindow.cxx:741 vncviewer/DesktopWindow.cxx:747
-#: vncviewer/DesktopWindow.cxx:760
+#: vncviewer/DesktopWindow.cxx:794 vncviewer/DesktopWindow.cxx:802
+#: vncviewer/DesktopWindow.cxx:822
 msgid "Failure grabbing keyboard"
 msgstr "Kunne ikke fange tastatur"
 
-#: vncviewer/DesktopWindow.cxx:772
+#: vncviewer/DesktopWindow.cxx:896
 msgid "Failure grabbing mouse"
 msgstr "Kunne ikke fange mus"
 
-#: vncviewer/DesktopWindow.cxx:1002
+#: vncviewer/DesktopWindow.cxx:1120
 msgid "Invalid screen layout computed for resize request!"
 msgstr "Ugyldig skærmlayout beregnet for anmodning om ny størrelse!"
 
-#: vncviewer/FLTKPixelBuffer.cxx:33
-msgid "Not enough memory for framebuffer"
-msgstr "Ikke nok hukommelse for framebuffer"
-
 #: vncviewer/OptionsDialog.cxx:57
 msgid "VNC Viewer: Connection Options"
 msgstr "VNC-fremviser: Forbindelsesindstillinger"
@@ -338,220 +340,282 @@
 msgid "Connect"
 msgstr "Forbind"
 
-#: vncviewer/UserDialog.cxx:74
+#: vncviewer/ServerDialog.cxx:137 vncviewer/ServerDialog.cxx:171
+msgid "TigerVNC configuration (*.tigervnc)"
+msgstr "TigerVNC-konfiguration (*.tigervnc)"
+
+#: vncviewer/ServerDialog.cxx:138
+msgid "Select a TigerVNC configuration file"
+msgstr "Vælg en TigerVNC-konfigurationsfil"
+
+#: vncviewer/ServerDialog.cxx:172
+msgid "Save the TigerVNC configuration to file"
+msgstr "Gem TigerVNC-konfigurationen til fil"
+
+#: vncviewer/ServerDialog.cxx:197
+#, c-format
+msgid "%s already exists. Do you want to overwrite?"
+msgstr "%s findes allerede. Ønsker du at overskrive?"
+
+#: vncviewer/ServerDialog.cxx:198 vncviewer/vncviewer.cxx:279
+msgid "No"
+msgstr "Nej"
+
+#: vncviewer/ServerDialog.cxx:198
+msgid "Overwrite"
+msgstr "Overskriv"
+
+#: vncviewer/UserDialog.cxx:85
 msgid "Opening password file failed"
 msgstr "Åbning af adgangskodefil mislykkedes"
 
-#: vncviewer/UserDialog.cxx:86 vncviewer/UserDialog.cxx:96
+#: vncviewer/UserDialog.cxx:105
 msgid "VNC authentication"
 msgstr "VNC-godkendelse"
 
-#: vncviewer/UserDialog.cxx:87 vncviewer/UserDialog.cxx:102
-msgid "Password:"
-msgstr "Adgangskode:"
+#: vncviewer/UserDialog.cxx:112
+msgid "This connection is secure"
+msgstr "Denne forbindelse er sikker"
 
-#: vncviewer/UserDialog.cxx:89
-msgid "Authentication cancelled"
-msgstr "Godkendelse afbrudt"
+#: vncviewer/UserDialog.cxx:116
+msgid "This connection is not secure"
+msgstr "Denne forbindelse er ikke sikker"
 
-#: vncviewer/UserDialog.cxx:99
+#: vncviewer/UserDialog.cxx:133
 msgid "Username:"
 msgstr "Brugernavn:"
 
-#: vncviewer/Viewport.cxx:586
+#: vncviewer/UserDialog.cxx:140
+msgid "Password:"
+msgstr "Adgangskode:"
+
+#: vncviewer/UserDialog.cxx:179
+msgid "Authentication cancelled"
+msgstr "Godkendelse afbrudt"
+
+#: vncviewer/Viewport.cxx:377
+#, c-format
+msgid "Failed to update keyboard LED state: %lu"
+msgstr "Kunne ikke opdatere tastatur-LED-tilstand: %lu"
+
+#: vncviewer/Viewport.cxx:383 vncviewer/Viewport.cxx:389
+#, c-format
+msgid "Failed to update keyboard LED state: %d"
+msgstr "Kunne ikke opdatere tastatur-LED-tilstand: %d"
+
+#: vncviewer/Viewport.cxx:419
+msgid "Failed to update keyboard LED state"
+msgstr "Kunne ikke opdatere tastatur-LED-tilstand"
+
+#: vncviewer/Viewport.cxx:446 vncviewer/Viewport.cxx:454
+#: vncviewer/Viewport.cxx:471
+#, c-format
+msgid "Failed to get keyboard LED state: %d"
+msgstr "Kunne ikke indhente tastatur-LED-tilstand: %d"
+
+#: vncviewer/Viewport.cxx:817
+msgid "No key code specified on key press"
+msgstr "Ingen nøglekode angivet ved tastaturtryk"
+
+#: vncviewer/Viewport.cxx:959
 #, c-format
 msgid "No scan code for extended virtual key 0x%02x"
 msgstr "Ingen skanningskode for udvidet virtuel nøgle 0x%02x"
 
-#: vncviewer/Viewport.cxx:588
+#: vncviewer/Viewport.cxx:961
 #, c-format
 msgid "No scan code for virtual key 0x%02x"
 msgstr "Ingen skanningskode for virtuel nøgle 0x%02x"
 
-#: vncviewer/Viewport.cxx:605
+#: vncviewer/Viewport.cxx:967
+#, c-format
+msgid "Invalid scan code 0x%02x"
+msgstr "Ugyldig skanningskode 0x%02x"
+
+#: vncviewer/Viewport.cxx:997
 #, c-format
 msgid "No symbol for extended virtual key 0x%02x"
 msgstr "Intet symbol for udvidet virtuel nøgle 0x%02x"
 
-#: vncviewer/Viewport.cxx:607
+#: vncviewer/Viewport.cxx:999
 #, c-format
 msgid "No symbol for virtual key 0x%02x"
 msgstr "Intet symbol for virtuel nøgle 0x%02x"
 
-#: vncviewer/Viewport.cxx:645
+#: vncviewer/Viewport.cxx:1086
 #, c-format
 msgid "No symbol for key code 0x%02x (in the current state)"
 msgstr "Intet symbol for nøglekode 0x%02x (i den nuværende tilstand)"
 
-#: vncviewer/Viewport.cxx:671
+#: vncviewer/Viewport.cxx:1119
 #, c-format
 msgid "No symbol for key code %d (in the current state)"
 msgstr "Intet symbol for nøglekode %d (i den nuværende tilstand)"
 
-#: vncviewer/Viewport.cxx:708
+#: vncviewer/Viewport.cxx:1170
 msgctxt "ContextMenu|"
 msgid "E&xit viewer"
 msgstr "&Afslut fremviser"
 
-#: vncviewer/Viewport.cxx:711
+#: vncviewer/Viewport.cxx:1173
 msgctxt "ContextMenu|"
 msgid "&Full screen"
 msgstr "&Fuld skærm"
 
-#: vncviewer/Viewport.cxx:714
+#: vncviewer/Viewport.cxx:1176
 msgctxt "ContextMenu|"
 msgid "Minimi&ze"
 msgstr "&Minimer"
 
-#: vncviewer/Viewport.cxx:716
+#: vncviewer/Viewport.cxx:1178
 msgctxt "ContextMenu|"
 msgid "Resize &window to session"
 msgstr "Ændr størrelse for &vindue til session"
 
-#: vncviewer/Viewport.cxx:721
+#: vncviewer/Viewport.cxx:1183
 msgctxt "ContextMenu|"
 msgid "&Ctrl"
 msgstr "&Ctrl"
 
-#: vncviewer/Viewport.cxx:724
+#: vncviewer/Viewport.cxx:1186
 msgctxt "ContextMenu|"
 msgid "&Alt"
 msgstr "&Alt"
 
-#: vncviewer/Viewport.cxx:730
+#: vncviewer/Viewport.cxx:1192
 #, c-format
 msgctxt "ContextMenu|"
 msgid "Send %s"
 msgstr "Send %s"
 
-#: vncviewer/Viewport.cxx:736
+#: vncviewer/Viewport.cxx:1198
 msgctxt "ContextMenu|"
 msgid "Send Ctrl-Alt-&Del"
 msgstr "Send Ctrl-Alt-&Slet"
 
-#: vncviewer/Viewport.cxx:739
+#: vncviewer/Viewport.cxx:1201
 msgctxt "ContextMenu|"
 msgid "&Refresh screen"
 msgstr "&Opdater skærm"
 
-#: vncviewer/Viewport.cxx:742
+#: vncviewer/Viewport.cxx:1204
 msgctxt "ContextMenu|"
 msgid "&Options..."
 msgstr "&Indstillinger ..."
 
-#: vncviewer/Viewport.cxx:744
+#: vncviewer/Viewport.cxx:1206
 msgctxt "ContextMenu|"
 msgid "Connection &info..."
 msgstr "Forbindelses&info ..."
 
-#: vncviewer/Viewport.cxx:746
+#: vncviewer/Viewport.cxx:1208
 msgctxt "ContextMenu|"
 msgid "About &TigerVNC viewer..."
 msgstr "Om &TigerVNC-fremviseren ..."
 
-#: vncviewer/Viewport.cxx:749
+#: vncviewer/Viewport.cxx:1211
 msgctxt "ContextMenu|"
 msgid "Dismiss &menu"
 msgstr "Fjern %menu"
 
-#: vncviewer/Viewport.cxx:833
+#: vncviewer/Viewport.cxx:1300
 msgid "VNC connection info"
 msgstr "VNC-forbindelsesinfo"
 
-#: vncviewer/parameters.cxx:286 vncviewer/parameters.cxx:320
+#: vncviewer/parameters.cxx:279 vncviewer/parameters.cxx:313
 #, c-format
 msgid "The name of the parameter %s was too large to write to the registry"
 msgstr "Navnet på parameteren %s var for lang til at kunne skrives til registret"
 
-#: vncviewer/parameters.cxx:292 vncviewer/parameters.cxx:299
+#: vncviewer/parameters.cxx:285 vncviewer/parameters.cxx:292
 #, c-format
 msgid "The parameter %s was too large to write to the registry"
 msgstr "Parameteren %s var for lang til at kunne skrives til registret"
 
-#: vncviewer/parameters.cxx:305 vncviewer/parameters.cxx:326
+#: vncviewer/parameters.cxx:298 vncviewer/parameters.cxx:319
 #, c-format
 msgid "Failed to write parameter %s of type %s to the registry: %ld"
 msgstr "Kunne ikke skrive parameteren %s af typen %s til registret: %ld"
 
-#: vncviewer/parameters.cxx:341 vncviewer/parameters.cxx:380
+#: vncviewer/parameters.cxx:334 vncviewer/parameters.cxx:373
 #, c-format
 msgid "The name of the parameter %s was too large to read from the registry"
 msgstr "Navnet for parameteren %s var for lang til at kunne læses fra registret"
 
-#: vncviewer/parameters.cxx:350 vncviewer/parameters.cxx:389
+#: vncviewer/parameters.cxx:343 vncviewer/parameters.cxx:382
 #, c-format
 msgid "Failed to read parameter %s from the registry: %ld"
 msgstr "Kunne ikke læse parameteren %s fra registret: %ld"
 
-#: vncviewer/parameters.cxx:359
+#: vncviewer/parameters.cxx:352
 #, c-format
 msgid "The parameter %s was too large to read from the registry"
 msgstr "Parameteren %s var for lang til at kunne læses fra registret"
 
-#: vncviewer/parameters.cxx:409
+#: vncviewer/parameters.cxx:402
 #, c-format
 msgid "Failed to create registry key: %ld"
 msgstr "Kunne ikke oprette registernøgle: %ld"
 
-#: vncviewer/parameters.cxx:423 vncviewer/parameters.cxx:472
-#: vncviewer/parameters.cxx:534 vncviewer/parameters.cxx:665
+#: vncviewer/parameters.cxx:416 vncviewer/parameters.cxx:465
+#: vncviewer/parameters.cxx:527 vncviewer/parameters.cxx:660
 #, c-format
 msgid "Unknown parameter type for parameter %s"
 msgstr "Ukendt parametertype for parameteren %s"
 
-#: vncviewer/parameters.cxx:430 vncviewer/parameters.cxx:479
+#: vncviewer/parameters.cxx:423 vncviewer/parameters.cxx:472
 #, c-format
 msgid "Failed to close registry key: %ld"
 msgstr "Kunne ikke lukke registernøgle: %ld"
 
-#: vncviewer/parameters.cxx:446
+#: vncviewer/parameters.cxx:439
 #, c-format
 msgid "Failed to open registry key: %ld"
 msgstr "Kunne ikke åbne registernøgle: %ld"
 
-#: vncviewer/parameters.cxx:503
+#: vncviewer/parameters.cxx:496
 msgid "Failed to write configuration file, can't obtain home directory path."
 msgstr "Kunne ikke skrive konfigurationsfil, kan ikke indhente hjemmemappens sti."
 
-#: vncviewer/parameters.cxx:516
+#: vncviewer/parameters.cxx:509
 #, c-format
 msgid "Failed to write configuration file, can't open %s: %s"
 msgstr "Kunne ikke skrive konfigurationsfil, kan ikke åbne %s: %s"
 
-#: vncviewer/parameters.cxx:559
+#: vncviewer/parameters.cxx:554
 msgid "Failed to read configuration file, can't obtain home directory path."
 msgstr "Kunne ikke læse konfigurationsfil, kan ikke indhente hjemmemappens sti."
 
-#: vncviewer/parameters.cxx:572
+#: vncviewer/parameters.cxx:567
 #, c-format
 msgid "Failed to read configuration file, can't open %s: %s"
 msgstr "Kunne ikke læse konfigurationsfil, kan ikke åbne %s: %s"
 
-#: vncviewer/parameters.cxx:585 vncviewer/parameters.cxx:590
-#: vncviewer/parameters.cxx:615 vncviewer/parameters.cxx:628
-#: vncviewer/parameters.cxx:644
+#: vncviewer/parameters.cxx:580 vncviewer/parameters.cxx:585
+#: vncviewer/parameters.cxx:610 vncviewer/parameters.cxx:623
+#: vncviewer/parameters.cxx:639
 #, c-format
 msgid "Failed to read line %d in file %s: %s"
 msgstr "Kunne ikke læse linje %d i filen %s: %s"
 
-#: vncviewer/parameters.cxx:591
+#: vncviewer/parameters.cxx:586
 msgid "Line too long"
 msgstr "Linjen er for lang"
 
-#: vncviewer/parameters.cxx:598
+#: vncviewer/parameters.cxx:593
 #, c-format
 msgid "Configuration file %s is in an invalid format"
 msgstr "Konfigurationsfilen %s er i et ugyldigt format"
 
-#: vncviewer/parameters.cxx:616
+#: vncviewer/parameters.cxx:611
 msgid "Invalid format"
 msgstr "Ugyldigt format"
 
-#: vncviewer/parameters.cxx:629 vncviewer/parameters.cxx:645
+#: vncviewer/parameters.cxx:624 vncviewer/parameters.cxx:640
 msgid "Invalid format or too large value"
 msgstr "Ugyldigt format eller for stor værdi"
 
-#: vncviewer/parameters.cxx:672
+#: vncviewer/parameters.cxx:667
 #, c-format
 msgid "Unknown parameter %s on line %d in file %s"
 msgstr "Ukendt parameter %s på linje %d i filen %s"
@@ -561,12 +625,12 @@
 msgid ""
 "TigerVNC Viewer %d-bit v%s\n"
 "Built on: %s\n"
-"Copyright (C) 1999-%d TigerVNC Team and many others (see README.txt)\n"
+"Copyright (C) 1999-%d TigerVNC Team and many others (see README.rst)\n"
 "See http://www.tigervnc.org for information on TigerVNC."
 msgstr ""
 "TigerVNC-fremviser %d-bit v%s\n"
 "Bygget på: %s\n"
-"Ophavsret 1999-%d TigerVNC-holdet og mange andre (se README.txt)\n"
+"Ophavsret 1999-%d TigerVNC-holdet og mange andre (se README.rst)\n"
 "Se http://www.tigervnc.org for information om TigerVNC."
 
 #: vncviewer/vncviewer.cxx:127
@@ -587,14 +651,10 @@
 msgid "Termination signal %d has been received. TigerVNC Viewer will now exit."
 msgstr "Opsigelsessignalet %d er blevet modtaget. TigerVNC-fremviseren vil nu afslutte."
 
-#: vncviewer/vncviewer.cxx:271
+#: vncviewer/vncviewer.cxx:271 vncviewer/vncviewer.desktop.in.in:3
 msgid "TigerVNC Viewer"
 msgstr "TigerVNC-fremviser"
 
-#: vncviewer/vncviewer.cxx:279
-msgid "No"
-msgstr "Nej"
-
 #: vncviewer/vncviewer.cxx:280
 msgid "Yes"
 msgstr "Ja"
@@ -648,15 +708,26 @@
 
 #. TRANSLATORS: "Parameters" are command line arguments, or settings
 #. from a file or the Windows registry.
-#: vncviewer/vncviewer.cxx:534 vncviewer/vncviewer.cxx:535
+#: vncviewer/vncviewer.cxx:581 vncviewer/vncviewer.cxx:583
 msgid "Parameters -listen and -via are incompatible"
 msgstr "Parameterne -listen og -via er ikke kompatible"
 
-#: vncviewer/vncviewer.cxx:550
+#: vncviewer/vncviewer.cxx:598
 #, c-format
 msgid "Listening on port %d"
 msgstr "Lytter på port %d"
 
-#: vncviewer/vncviewer.desktop.in.in:6
+#: vncviewer/vncviewer.desktop.in.in:4
+msgid "Remote Desktop Viewer"
+msgstr "Ekstern skrivebordsviser"
+
+#: vncviewer/vncviewer.desktop.in.in:5
 msgid "Connect to VNC server and display remote desktop"
-msgstr "Opret forbindelse til VNC-server og vis fjern-skrivebord"
+msgstr "Forbind til VNC-server og vis eksternt skrivebord"
+
+#: vncviewer/vncviewer.desktop.in.in:7
+msgid "tigervnc"
+msgstr "tigervnc"
+
+#~ msgid "Not enough memory for framebuffer"
+#~ msgstr "Ikke nok hukommelse for framebuffer"
diff --git a/release/tigervnc.iss.in b/release/tigervnc.iss.in
index 092345a..5850148 100644
--- a/release/tigervnc.iss.in
+++ b/release/tigervnc.iss.in
@@ -9,7 +9,7 @@
 #endif
 AppVersion=@VERSION@
 AppPublisher=TigerVNC project
-AppPublisherURL=http://tigervnc.org
+AppPublisherURL=https://tigervnc.org
 DefaultDirName={pf}\TigerVNC
 #ifdef WIN64
 DefaultGroupName=TigerVNC 64-bit
diff --git a/unix/vncconfig/vncconfig.man b/unix/vncconfig/vncconfig.man
index 06f9ca9..b685a46 100644
--- a/unix/vncconfig/vncconfig.man
+++ b/unix/vncconfig/vncconfig.man
@@ -114,7 +114,7 @@
 .BR vncserver (1),
 .BR Xvnc (1)
 .br
-http://www.tigervnc.org
+https://www.tigervnc.org
 
 .SH AUTHOR
 Tristan Richardson, RealVNC Ltd. and others.
diff --git a/unix/vncpasswd/vncpasswd.man b/unix/vncpasswd/vncpasswd.man
index a62c0ed..9e68181 100644
--- a/unix/vncpasswd/vncpasswd.man
+++ b/unix/vncpasswd/vncpasswd.man
@@ -47,7 +47,7 @@
 .BR Xvnc (1)
 .BR vncconfig (1),
 .br
-http://www.tigervnc.org
+https://www.tigervnc.org
 
 .SH AUTHORS
 Tristan Richardson, RealVNC Ltd., Antoine Martin, D. R. Commander and others.
diff --git a/unix/vncserver b/unix/vncserver
index 9e7a6ac..bb4f2fe 100755
--- a/unix/vncserver
+++ b/unix/vncserver
@@ -33,8 +33,6 @@
     $exedir = substr($0, 0, $slashndx+1);
 }
 
-$vncClasses = "";
-
 &SanityCheck();
 
 #
@@ -44,9 +42,6 @@
 
 $geometry = "1024x768";
 #$depth = 16;
-$vncJavaFiles = (((-d "$vncClasses") && "$vncClasses") ||
-                 ((-d "/usr/share/vnc/classes") && "/usr/share/vnc/classes") ||
-                 ((-d "/usr/local/vnc/classes") && "/usr/local/vnc/classes"));
 
 $vncUserDir = "$ENV{HOME}/.vnc";
 $vncUserConfig = "$vncUserDir/config";
@@ -206,7 +201,6 @@
 # We set some reasonable defaults. Config file settings
 # override these where present.
 $default_opts{desktop} = &quotedString($desktopName);
-$default_opts{httpd} = $vncJavaFiles if ($vncJavaFiles);
 $default_opts{auth} = &quotedString($xauthorityFile);
 $default_opts{geometry} = $geometry if ($geometry);
 $default_opts{depth} = $depth if ($depth);
@@ -848,7 +842,6 @@
 	foreach $cmd ("Xvnc","vncpasswd") {
 	    for (split(/:/,$ENV{PATH})) {
 		if (-x "$_/$cmd") {
-		    $vncClasses = "$_/../vnc/classes";
 		    next cmd2;
 		}
 	    }
@@ -860,7 +853,6 @@
 	foreach $cmd ($exedir."Xvnc",$exedir."vncpasswd") {
 	    for (split(/:/,$ENV{PATH})) {
 		if (-x "$cmd") {
-		    $vncClasses = $exedir."../vnc/classes";
 		    next cmd3;
 		}
 	    }
diff --git a/unix/vncserver.man b/unix/vncserver.man
index 9108683..95f7960 100644
--- a/unix/vncserver.man
+++ b/unix/vncserver.man
@@ -192,7 +192,7 @@
 .BR vncconfig (1),
 .BR Xvnc (1)
 .br
-http://www.tigervnc.org
+https://www.tigervnc.org
 
 .SH AUTHOR
 Tristan Richardson, RealVNC Ltd., D. R. Commander and others.
diff --git a/unix/x0vncserver/XDesktop.cxx b/unix/x0vncserver/XDesktop.cxx
index 5f67f29..1fdc9e2 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;
@@ -611,6 +653,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;
@@ -684,6 +728,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 c08572b..cf2c35a 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
 {
 
@@ -307,8 +257,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 +322,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 +357,7 @@
         }
       }
 
-      server.checkTimeouts();
+      Timer::checkTimeouts();
 
       // Client list could have been changed.
       server.getSockets(&sockets);
@@ -439,6 +387,13 @@
 
   TXWindow::handleXEvents(dpy);
 
+  // Run listener destructors; remove UNIX sockets etc
+  for (std::list<SocketListener*>::iterator i = listeners.begin();
+       i != listeners.end();
+       i++) {
+    delete *i;
+  }
+
   vlog.info("Terminated");
   return 0;
 }
diff --git a/unix/x0vncserver/x0vncserver.man b/unix/x0vncserver/x0vncserver.man
index 5dc0b05..5f1508c 100644
--- a/unix/x0vncserver/x0vncserver.man
+++ b/unix/x0vncserver/x0vncserver.man
@@ -302,7 +302,7 @@
 .BR Xvnc (1),
 .BR vncpasswd (1),
 .br
-http://www.tigervnc.org/
+https://www.tigervnc.org/
 
 .SH AUTHOR
 Constantin Kaplinsky and others.
diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc
index 7eaf3f9..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>
@@ -35,7 +36,6 @@
 #include <network/Socket.h>
 #include <rfb/Exception.h>
 #include <rfb/VNCServerST.h>
-#include <rfb/HTTPServer.h>
 #include <rfb/LogWriter.h>
 #include <rfb/Configuration.h>
 #include <rfb/ServerCore.h>
@@ -67,54 +67,14 @@
                                  "rejecting the connection",
                                  10);
 
-class FileHTTPServer : public rfb::HTTPServer {
-public:
-  FileHTTPServer(XserverDesktop* d) : desktop(d) {}
-  virtual ~FileHTTPServer() {}
-
-  virtual rdr::InStream* getFile(const char* name, const char** contentType,
-                                 int* contentLength, time_t* lastModified)
-  {
-    if (name[0] != '/' || strstr(name, "..") != 0) {
-      vlog.info("http request was for invalid file name");
-      return 0;
-    }
-
-    if (strcmp(name, "/") == 0) name = "/index.vnc";
-
-    CharArray httpDirStr(httpDir.getData());
-    CharArray fname(strlen(httpDirStr.buf)+strlen(name)+1);
-    sprintf(fname.buf, "%s%s", httpDirStr.buf, name);
-    int fd = open(fname.buf, O_RDONLY);
-    if (fd < 0) return 0;
-    rdr::InStream* is = new rdr::FdInStream(fd, -1, 0, true);
-    *contentType = guessContentType(name, *contentType);
-    if (strlen(name) > 4 && strcasecmp(&name[strlen(name)-4], ".vnc") == 0) {
-      is = new rdr::SubstitutingInStream(is, desktop, 20);
-      *contentType = "text/html";
-    } else {
-      struct stat st;
-      if (fstat(fd, &st) == 0) {
-        *contentLength = st.st_size;
-        *lastModified = st.st_mtime;
-      }
-    }
-    return is;
-  }
-
-  XserverDesktop* desktop;
-};
-
 
 XserverDesktop::XserverDesktop(int screenIndex_,
                                std::list<network::SocketListener*> listeners_,
-                               std::list<network::SocketListener*> httpListeners_,
                                const char* name, const rfb::PixelFormat &pf,
                                int width, int height,
                                void* fbptr, int stride)
   : screenIndex(screenIndex_),
-    server(0), httpServer(0),
-    listeners(listeners_), httpListeners(httpListeners_),
+    server(0), listeners(listeners_),
     directFbptr(true),
     queryConnectId(0), queryConnectTimer(this)
 {
@@ -122,22 +82,12 @@
 
   server = new VNCServerST(name, this);
   setFramebuffer(width, height, fbptr, stride);
-  server->setQueryConnectionHandler(this);
-
-  if (!httpListeners.empty ())
-    httpServer = new FileHTTPServer(this);
 
   for (std::list<SocketListener*>::iterator i = listeners.begin();
        i != listeners.end();
        i++) {
     vncSetNotifyFd((*i)->getFd(), screenIndex, true, false);
   }
-
-  for (std::list<SocketListener*>::iterator i = httpListeners.begin();
-       i != httpListeners.end();
-       i++) {
-    vncSetNotifyFd((*i)->getFd(), screenIndex, true, false);
-  }
 }
 
 XserverDesktop::~XserverDesktop()
@@ -147,14 +97,8 @@
     delete listeners.back();
     listeners.pop_back();
   }
-  while (!httpListeners.empty()) {
-    vncRemoveNotifyFd(listeners.back()->getFd());
-    delete httpListeners.back();
-    httpListeners.pop_back();
-  }
   if (!directFbptr)
     delete [] data;
-  delete httpServer;
   delete server;
 }
 
@@ -201,72 +145,31 @@
   server->setScreenLayout(::computeScreenLayout(&outputIdMap));
 }
 
-char* XserverDesktop::substitute(const char* varName)
+void XserverDesktop::start(rfb::VNCServer* vs)
 {
-  if (strcmp(varName, "$$") == 0) {
-    return rfb::strDup("$");
-  }
-  if (strcmp(varName, "$PORT") == 0) {
-    char* str = new char[10];
-    sprintf(str, "%d", listeners.empty () ? 0 : (*listeners.begin ())->getMyPort());
-    return str;
-  }
-  if (strcmp(varName, "$WIDTH") == 0) {
-    char* str = new char[10];
-    sprintf(str, "%d", width());
-    return str;
-  }
-  if (strcmp(varName, "$HEIGHT") == 0) {
-    char* str = new char[10];
-    sprintf(str, "%d", height());
-    return str;
-  }
-  if (strcmp(varName, "$APPLETWIDTH") == 0) {
-    char* str = new char[10];
-    sprintf(str, "%d", width());
-    return str;
-  }
-  if (strcmp(varName, "$APPLETHEIGHT") == 0) {
-    char* str = new char[10];
-    sprintf(str, "%d", height());
-    return str;
-  }
-  if (strcmp(varName, "$DESKTOP") == 0) {
-    return rfb::strDup(server->getName());
-  }
-  if (strcmp(varName, "$DISPLAY") == 0) {
-    struct utsname uts;
-    uname(&uts);
-    char* str = new char[256];
-    strncpy(str, uts.nodename, 240);
-    str[239] = '\0'; /* Ensure string is zero-terminated */
-    strcat(str, ":");
-    strncat(str, vncGetDisplay(), 10);
-    return str;
-  }
-  if (strcmp(varName, "$USER") == 0) {
-    struct passwd* user = getpwuid(getuid());
-    return rfb::strDup(user ? user->pw_name : "?");
-  }
-  return 0;
+  // We already own the server object, and we always keep it in a
+  // ready state
+  assert(vs == server);
 }
 
-rfb::VNCServerST::queryResult
-XserverDesktop::queryConnection(network::Socket* sock,
-                                const char* userName,
-                                char** reason)
+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());
@@ -277,8 +180,6 @@
   queryConnectSocket = sock;
 
   queryConnectTimer.start(queryConnectTimeout * 1000);
-
-  return rfb::VNCServerST::PENDING;
 }
 
 void XserverDesktop::bell()
@@ -370,14 +271,10 @@
     if (read) {
       if (handleListenerEvent(fd, &listeners, server))
         return;
-      if (handleListenerEvent(fd, &httpListeners, httpServer))
-        return;
     }
 
     if (handleSocketEvent(fd, server, read, write))
       return;
-    if (handleSocketEvent(fd, httpServer, read, write))
-      return;
 
     vlog.error("Cannot find file descriptor for socket event");
   } catch (rdr::Exception& e) {
@@ -458,21 +355,6 @@
         vncSetNotifyFd(fd, screenIndex, true, (*i)->outStream().bufferUsage() > 0);
       }
     }
-    if (httpServer) {
-      httpServer->getSockets(&sockets);
-      for (i = sockets.begin(); i != sockets.end(); i++) {
-        int fd = (*i)->getFd();
-        if ((*i)->isShutdown()) {
-          vlog.debug("http client gone, sock %d",fd);
-          vncRemoveNotifyFd(fd);
-          httpServer->removeSocket(*i);
-          delete (*i);
-        } else {
-          /* Update existing NotifyFD to listen for write (or not) */
-          vncSetNotifyFd(fd, screenIndex, true, (*i)->outStream().bufferUsage() > 0);
-        }
-      }
-    }
 
     // We are responsible for propagating mouse movement between clients
     int cursorX, cursorY;
@@ -486,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) {
@@ -542,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 f866a4c..1253935 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.h
+++ b/unix/xserver/hw/vnc/XserverDesktop.h
@@ -32,11 +32,9 @@
 #include <stdint.h>
 
 #include <rfb/SDesktop.h>
-#include <rfb/HTTPServer.h>
 #include <rfb/PixelBuffer.h>
 #include <rfb/Configuration.h>
-#include <rfb/VNCServerST.h>
-#include <rdr/SubstitutingInStream.h>
+#include <rfb/Timer.h>
 #include <unixcommon.h>
 #include "Input.h"
 
@@ -47,14 +45,11 @@
 namespace network { class SocketListener; class Socket; class SocketServer; }
 
 class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer,
-                       public rdr::Substitutor,
-                       public rfb::VNCServerST::QueryConnectionHandler,
                        public rfb::Timer::Callback {
 public:
 
   XserverDesktop(int screenIndex,
                  std::list<network::SocketListener*> listeners_,
-                 std::list<network::SocketListener*> httpListeners_,
                  const char* name, const rfb::PixelFormat &pf,
                  int width, int height, void* fbptr, int stride);
   virtual ~XserverDesktop();
@@ -90,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);
@@ -99,14 +99,6 @@
   // rfb::PixelBuffer callbacks
   virtual void grabRegion(const rfb::Region& r);
 
-  // rdr::Substitutor callback
-  virtual char* substitute(const char* varName);
-
-  // 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,
@@ -120,10 +112,8 @@
 private:
 
   int screenIndex;
-  rfb::VNCServerST* server;
-  rfb::HTTPServer* httpServer;
+  rfb::VNCServer* server;
   std::list<network::SocketListener*> listeners;
-  std::list<network::SocketListener*> httpListeners;
   bool directFbptr;
 
   uint32_t queryConnectId;
diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man
index 269be9a..9991650 100644
--- a/unix/xserver/hw/vnc/Xvnc.man
+++ b/unix/xserver/hw/vnc/Xvnc.man
@@ -107,19 +107,6 @@
 mean an update will be aborted after this time.  Default is 20000 (20 seconds).
 .
 .TP
-.B \-httpd \fIdirectory\fP
-Run a mini-HTTP server which serves files from the given directory.  Normally
-the directory will contain the classes for the Java viewer.  In addition, files
-with a .vnc extension will have certain substitutions made so that a single
-installation of the Java VNC viewer can be served by separate instances of
-Xvnc.
-.
-.TP
-.B \-httpPort \fIport\fP
-Specifies the port on which the mini-HTTP server runs.  Default is 5800 plus
-the display number.
-.
-.TP
 .B \-rfbauth \fIpasswd-file\fP, \-PasswordFile \fIpasswd-file\fP
 Password file for VNC authentication.  There is no default, you should
 specify the password file explicitly.  Password file should be created with
@@ -396,7 +383,7 @@
 .BR Xserver (1),
 .BR inetd (1)
 .br
-http://www.tigervnc.org
+https://www.tigervnc.org
 
 .SH AUTHOR
 Tristan Richardson, RealVNC Ltd. and others.
diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc
index 7ca71d9..20072f4 100644
--- a/unix/xserver/hw/vnc/vncExtInit.cc
+++ b/unix/xserver/hw/vnc/vncExtInit.cc
@@ -73,10 +73,6 @@
 typedef std::set<std::string, CaseInsensitiveCompare> ParamSet;
 static ParamSet allowOverrideSet;
 
-rfb::StringParameter httpDir("httpd",
-                             "Directory containing files to serve via HTTP",
-                             "");
-rfb::IntParameter httpPort("httpPort", "TCP port to listen for HTTP",0);
 rfb::AliasParameter rfbwait("rfbwait", "Alias for ClientWaitTimeMillis",
                             &rfb::Server::clientWaitTimeMillis);
 rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",0);
@@ -177,7 +173,6 @@
 
       if (!desktop[scr]) {
         std::list<network::SocketListener*> listeners;
-        std::list<network::SocketListener*> httpListeners;
         if (scr == 0 && vncInetdSock != -1) {
           if (network::isSocketListening(vncInetdSock))
           {
@@ -214,21 +209,6 @@
           vlog.info("Listening for VNC connections on %s interface(s), port %d",
                     localhostOnly ? "local" : (const char*)interface,
                     port);
-
-          CharArray httpDirStr(httpDir.getData());
-          if (httpDirStr.buf[0]) {
-            port = httpPort;
-            if (port == 0) port = 5800 + atoi(vncGetDisplay());
-            port += 1000 * scr;
-            if (localhostOnly)
-              network::createLocalTcpListeners(&httpListeners, port);
-            else
-              network::createTcpListeners(&httpListeners, addr, port);
-
-            vlog.info("Listening for HTTP connections on %s interface(s), port %d",
-                      localhostOnly ? "local" : (const char*)interface,
-                      port);
-          }
         }
 
         CharArray desktopNameStr(desktopName.getData());
@@ -237,7 +217,6 @@
         vncSetGlueContext(scr);
         desktop[scr] = new XserverDesktop(scr,
                                           listeners,
-                                          httpListeners,
                                           desktopNameStr.buf,
                                           pf,
                                           vncGetScreenWidth(),
diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h
index 9414723..5f97f96 100644
--- a/unix/xserver/hw/vnc/vncExtInit.h
+++ b/unix/xserver/hw/vnc/vncExtInit.h
@@ -23,13 +23,6 @@
 #include <stddef.h>
 #include <sys/select.h>
 
-// Only from C++
-#ifdef __cplusplus
-namespace rfb { class StringParameter; };
-
-extern rfb::StringParameter httpDir;
-#endif
-
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/unix/xserver/hw/vnc/xvnc.c b/unix/xserver/hw/vnc/xvnc.c
index c845ebc..98c4a15 100644
--- a/unix/xserver/hw/vnc/xvnc.c
+++ b/unix/xserver/hw/vnc/xvnc.c
@@ -89,7 +89,7 @@
 
 #define XVNCVERSION "TigerVNC 1.9.80"
 #define XVNCCOPYRIGHT ("Copyright (C) 1999-2018 TigerVNC Team and many others (see README.rst)\n" \
-                       "See http://www.tigervnc.org for information on TigerVNC.\n")
+                       "See https://www.tigervnc.org for information on TigerVNC.\n")
 
 #define VFB_DEFAULT_WIDTH  1024
 #define VFB_DEFAULT_HEIGHT 768
diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx
index 166597e..69186c5 100644
--- a/vncviewer/CConn.cxx
+++ b/vncviewer/CConn.cxx
@@ -113,14 +113,14 @@
       if (strchr(vncServerName, '/') != NULL) {
         sock = new network::UnixSocket(vncServerName);
         serverHost = sock->getPeerAddress();
-        vlog.info(_("connected to socket %s"), serverHost);
+        vlog.info(_("Connected to socket %s"), serverHost);
       } else
 #endif
       {
         getHostAndPort(vncServerName, &serverHost, &serverPort);
 
         sock = new network::TcpSocket(serverHost, serverPort);
-        vlog.info(_("connected to host %s port %d"), serverHost, serverPort);
+        vlog.info(_("Connected to host %s port %d"), serverHost, serverPort);
       }
     } catch (rdr::Exception& e) {
       vlog.error("%s", e.str());
@@ -575,9 +575,12 @@
   // Select best color level
   newFullColour = (kbitsPerSecond > 256);
   if (newFullColour != fullColour) {
-    vlog.info(_("Throughput %d kbit/s - full color is now %s"), 
-              kbitsPerSecond,
-              newFullColour ? _("enabled") : _("disabled"));
+    if (newFullColour)
+      vlog.info(_("Throughput %d kbit/s - full color is now enabled"),
+                kbitsPerSecond);
+    else
+      vlog.info(_("Throughput %d kbit/s - full color is now disabled"),
+                kbitsPerSecond);
     fullColour.setParam(newFullColour);
     formatChange = true;
   } 
diff --git a/vncviewer/PlatformPixelBuffer.cxx b/vncviewer/PlatformPixelBuffer.cxx
index c79b5c1..1e9803e 100644
--- a/vncviewer/PlatformPixelBuffer.cxx
+++ b/vncviewer/PlatformPixelBuffer.cxx
@@ -34,13 +34,8 @@
 static rfb::LogWriter vlog("PlatformPixelBuffer");
 
 PlatformPixelBuffer::PlatformPixelBuffer(int width, int height) :
-  FullFramePixelBuffer(rfb::PixelFormat(32, 24,
-#if !defined(WIN32) && !defined(__APPLE__)
-                                        ImageByteOrder(fl_display) == MSBFirst,
-#else
-                                        false,
-#endif
-                                        true, 255, 255, 255, 16, 8, 0),
+  FullFramePixelBuffer(rfb::PixelFormat(32, 24, false, true,
+                                        255, 255, 255, 16, 8, 0),
                        width, height, 0, stride),
   Surface(width, height)
 #if !defined(WIN32) && !defined(__APPLE__)
@@ -63,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;
@@ -106,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/Surface_X11.cxx b/vncviewer/Surface_X11.cxx
index 3523da3..6562634 100644
--- a/vncviewer/Surface_X11.cxx
+++ b/vncviewer/Surface_X11.cxx
@@ -109,6 +109,7 @@
 
 void Surface::alloc()
 {
+  XRenderPictFormat templ;
   XRenderPictFormat* format;
 
   // Might not be open at this point
@@ -117,7 +118,37 @@
   pixmap = XCreatePixmap(fl_display, XDefaultRootWindow(fl_display),
                          width(), height(), 32);
 
-  format = XRenderFindStandardFormat(fl_display, PictStandardARGB32);
+  // Our code assumes a BGRA byte order, regardless of what the endian
+  // of the machine is or the native byte order of XImage, so make sure
+  // we find such a format
+  templ.type = PictTypeDirect;
+  templ.depth = 32;
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+  templ.direct.alpha = 0;
+  templ.direct.red   = 8;
+  templ.direct.green = 16;
+  templ.direct.blue  = 24;
+#else
+  templ.direct.alpha = 24;
+  templ.direct.red   = 16;
+  templ.direct.green = 8;
+  templ.direct.blue  = 0;
+#endif
+  templ.direct.alphaMask = 0xff;
+  templ.direct.redMask = 0xff;
+  templ.direct.greenMask = 0xff;
+  templ.direct.blueMask = 0xff;
+
+  format = XRenderFindFormat(fl_display, PictFormatType | PictFormatDepth |
+                             PictFormatRed | PictFormatRedMask |
+                             PictFormatGreen | PictFormatGreenMask |
+                             PictFormatBlue | PictFormatBlueMask |
+                             PictFormatAlpha | PictFormatAlphaMask,
+                             &templ, 0);
+
+  if (!format)
+    throw rdr::Exception("XRenderFindFormat");
+
   picture = XRenderCreatePicture(fl_display, pixmap, format, 0, NULL);
 
   visFormat = XRenderFindVisualFormat(fl_display, fl_visual->visual);
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index 7b5df57..18ed69e 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/vncviewer.cxx b/vncviewer/vncviewer.cxx
index a9d4dfe..1c6f524 100644
--- a/vncviewer/vncviewer.cxx
+++ b/vncviewer/vncviewer.cxx
@@ -100,7 +100,7 @@
            _("TigerVNC Viewer %d-bit v%s\n"
              "Built on: %s\n"
              "Copyright (C) 1999-%d TigerVNC Team and many others (see README.rst)\n"
-             "See http://www.tigervnc.org for information on TigerVNC."),
+             "See https://www.tigervnc.org for information on TigerVNC."),
            (int)sizeof(size_t)*8, PACKAGE_VERSION,
            BUILD_TIMESTAMP, 2018);
 
@@ -348,10 +348,19 @@
 #endif
 
   fprintf(stderr,
-          "\nusage: %s [parameters] [host:displayNum] [parameters]\n"
-          "       %s [parameters] -listen [port] [parameters]\n"
+          "\n"
+          "usage: %s [parameters] [host][:displayNum]\n"
+          "       %s [parameters] [host][::port]\n"
+#ifndef WIN32
+          "       %s [parameters] [unix socket]\n"
+#endif
+          "       %s [parameters] -listen [port]\n"
           "       %s [parameters] [.tigervnc file]\n",
-          programName, programName, programName);
+          programName, programName,
+#ifndef WIN32
+          programName,
+#endif
+          programName, programName);
   fprintf(stderr,"\n"
           "Parameters can be turned on with -<param> or off with -<param>=0\n"
           "Parameters which take a value can be specified as "
diff --git a/vncviewer/vncviewer.man b/vncviewer/vncviewer.man
index 729c01d..8762dfb 100644
--- a/vncviewer/vncviewer.man
+++ b/vncviewer/vncviewer.man
@@ -11,13 +11,17 @@
 .RI [ host ][:: port ]
 .br
 .B vncviewer
+.RI [ options ]
+.RI [ unix\ socket ]
+.br
+.B vncviewer
 .RI [ options ] 
 .B \-listen
 .RI [ port ]
 .br
 .B vncviewer
 .RI [ options ]
-.RI [ .tigervnc file ]
+.RI [ .tigervnc\ file ]
 .SH DESCRIPTION
 .B vncviewer
 is a viewer (client) for Virtual Network Computing.  This manual page documents
@@ -316,7 +320,7 @@
 .BR vncconfig (1),
 .BR vncserver (1)
 .br
-http://www.tigervnc.org
+https://www.tigervnc.org
 
 .SH AUTHOR
 Tristan Richardson, RealVNC Ltd. and others.
diff --git a/win/rfb_win32/SDisplay.cxx b/win/rfb_win32/SDisplay.cxx
index ad55d49..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() {
 
@@ -314,21 +336,6 @@
 }
 
 
-Point SDisplay::getFbSize() {
-  bool startAndStop = !core;
-
-  // If not started, do minimal initialisation to get desktop size.
-  if (startAndStop)
-    recreatePixelBuffer();
-  Point result = Point(pb->width(), pb->height());
-
-  // Destroy the initialised structures.
-  if (startAndStop)
-    stopCore();
-  return result;
-}
-
-
 void
 SDisplay::notifyClipboardChanged(const char* text, int len) {
   vlog.debug("clipboard text changed");
diff --git a/win/rfb_win32/SDisplay.h b/win/rfb_win32/SDisplay.h
index f36b2b7..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,17 +90,18 @@
       // -=- 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;}
 
-      // -=- Used (indirectly) by JavaViewer to get desktop size
+      // -=- Set handler for incoming connections
 
-      Point getFbSize();
-
-      friend class SDisplayCore;
+      void setQueryConnectionHandler(QueryConnectionHandler* qch) {
+        queryConnectionHandler = qch;
+      }
 
       static IntParameter updateMethod;
       static BoolParameter disableLocalInputs;
@@ -152,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/vncconfig/Connections.h b/win/vncconfig/Connections.h
index b3402ab..c3d6737 100644
--- a/win/vncconfig/Connections.h
+++ b/win/vncconfig/Connections.h
@@ -27,8 +27,6 @@
 #include <rfb/Blacklist.h>
 #include <network/TcpSocket.h>
 
-static rfb::IntParameter http_port("HTTPPortNumber",
-  "TCP/IP port on which the server will serve the Java applet VNC Viewer ", 5800);
 static rfb::IntParameter port_number("PortNumber",
   "TCP/IP port on which the server will accept connections", 5900);
 static rfb::StringParameter hosts("Hosts",
@@ -96,10 +94,6 @@
         setItemInt(IDC_PORT, port_number ? port_number : 5900);
         setItemChecked(IDC_RFB_ENABLE, port_number != 0);
         setItemInt(IDC_IDLE_TIMEOUT, rfb::Server::idleTimeout);
-        vlog.debug("set IDC_HTTP_PORT %d", (int)http_port);
-        setItemInt(IDC_HTTP_PORT, http_port ? http_port : 5800);
-        setItemChecked(IDC_HTTP_ENABLE, http_port != 0);
-        enableItem(IDC_HTTP_PORT, http_port != 0);
         setItemChecked(IDC_LOCALHOST, localHost);
 
         HWND listBox = GetDlgItem(handle, IDC_HOSTS);
@@ -133,26 +127,14 @@
           return true;
 
         case IDC_PORT:
-          if (cmd == EN_CHANGE) {
-            try {
-              setItemInt(IDC_HTTP_PORT, rfbPortToHTTP(getItemInt(IDC_PORT)));
-            } catch (...) {
-            }
-          }
-        case IDC_HTTP_PORT:
         case IDC_IDLE_TIMEOUT:
           if (cmd == EN_CHANGE)
             setChanged(isChanged());
           return false;
 
-        case IDC_HTTP_ENABLE:
         case IDC_RFB_ENABLE:
         case IDC_LOCALHOST:
           {
-            // HTTP port
-            enableItem(IDC_HTTP_PORT, isItemChecked(IDC_HTTP_ENABLE) && isItemChecked(IDC_RFB_ENABLE));
-            enableItem(IDC_HTTP_ENABLE, isItemChecked(IDC_RFB_ENABLE));
-
             // RFB port
             enableItem(IDC_PORT, isItemChecked(IDC_RFB_ENABLE));
 
@@ -245,8 +227,6 @@
       bool onOk() {
         regKey.setInt(_T("PortNumber"), isItemChecked(IDC_RFB_ENABLE) ? getItemInt(IDC_PORT) : 0);
         regKey.setInt(_T("IdleTimeout"), getItemInt(IDC_IDLE_TIMEOUT));
-        regKey.setInt(_T("HTTPPortNumber"), isItemChecked(IDC_HTTP_ENABLE) && isItemChecked(IDC_RFB_ENABLE)
-                                            ? getItemInt(IDC_HTTP_PORT) : 0);
         regKey.setInt(_T("LocalHost"), isItemChecked(IDC_LOCALHOST));
         regKey.setString(_T("Hosts"), TCharArray(getHosts()).buf);
         return true;
@@ -258,8 +238,6 @@
           return (strcmp(new_hosts.buf, old_hosts.buf) != 0) ||
               (localHost != isItemChecked(IDC_LOCALHOST)) ||
               (port_number != getItemInt(IDC_PORT)) ||
-              (http_port != getItemInt(IDC_HTTP_PORT)) ||
-              ((http_port!=0) != (isItemChecked(IDC_HTTP_ENABLE)!=0)) ||
               (rfb::Server::idleTimeout != getItemInt(IDC_IDLE_TIMEOUT));
         } catch (rdr::Exception&) {
           return false;
@@ -281,15 +259,6 @@
         }
         return strDup(hosts_str.buf);
       }
-      int rfbPortToHTTP(int rfbPort) {
-        int offset = -100;
-        if (http_port)
-          offset = http_port - port_number;
-        int httpPort = rfbPort + offset;
-        if (httpPort <= 0)
-          httpPort = rfbPort;
-        return httpPort;
-      }
 
     protected:
       RegKey regKey;
diff --git a/win/vncconfig/Legacy.cxx b/win/vncconfig/Legacy.cxx
index b56eb37..deba0ff 100644
--- a/win/vncconfig/Legacy.cxx
+++ b/win/vncconfig/Legacy.cxx
@@ -192,10 +192,6 @@
 
       void LegacyPage::LoadUserPrefs(const RegKey& key)
       {
-        if (key.getBool(_T("HTTPConnect"), true))
-          regKey.setInt(_T("HTTPPortNumber"), key.getInt(_T("PortNumber"), 5900)-100);
-        else
-          regKey.setInt(_T("HTTPPortNumber"), 0);
         regKey.setInt(_T("PortNumber"), key.getBool(_T("SocketConnect")) ? key.getInt(_T("PortNumber"), 5900) : 0);
         if (key.getBool(_T("AutoPortSelect"), false)) {
           MsgBox(0, _T("The AutoPortSelect setting is not supported by this release.")
diff --git a/win/vncconfig/resource.h b/win/vncconfig/resource.h
index 16bbc15..7350f5b 100644
--- a/win/vncconfig/resource.h
+++ b/win/vncconfig/resource.h
@@ -52,8 +52,6 @@
 #define IDC_BUILDTIME                   1040
 #define IDC_VERSION                     1041
 #define IDC_COPYRIGHT                   1042
-#define IDC_HTTP_ENABLE                 1043
-#define IDC_HTTP_PORT                   1044
 #define IDC_BL_THRESHOLD                1046
 #define IDC_BL_TIMEOUT                  1047
 #define IDC_AFFECT_SCREENSAVER          1048
diff --git a/win/vncconfig/vncconfig.rc b/win/vncconfig/vncconfig.rc
index 27a8b5d..781277c 100644
--- a/win/vncconfig/vncconfig.rc
+++ b/win/vncconfig/vncconfig.rc
@@ -110,9 +110,6 @@
     LTEXT           "Disconnect idle clients after (seconds):",IDC_STATIC,7,
                     25,138,15,SS_CENTERIMAGE
     EDITTEXT        IDC_IDLE_TIMEOUT,150,25,61,15,ES_AUTOHSCROLL | ES_NUMBER
-    CONTROL         "Serve Java viewer via HTTP on port:",IDC_HTTP_ENABLE,
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,40,138,15
-    EDITTEXT        IDC_HTTP_PORT,150,40,61,15,ES_AUTOHSCROLL | ES_NUMBER
     GROUPBOX        "Access Control",IDC_STATIC,7,55,204,135
     CONTROL         "Only accept connections from the local machine",
                     IDC_LOCALHOST,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,
@@ -233,7 +230,7 @@
     LTEXT           ">version<",IDC_VERSION,165,7,77,18
     LTEXT           ">buildtime<",IDC_BUILDTIME,40,25,202,15
     LTEXT           ">copyright<",IDC_COPYRIGHT,40,40,256,15
-    LTEXT           "See http://www.tigervnc.org for more information on TigerVNC.",
+    LTEXT           "See https://www.tigervnc.org for more information on TigerVNC.",
                     IDC_STATIC,40,55,202,15
 END
 
diff --git a/win/winvnc/CMakeLists.txt b/win/winvnc/CMakeLists.txt
index ac9ae29..113796c 100644
--- a/win/winvnc/CMakeLists.txt
+++ b/win/winvnc/CMakeLists.txt
@@ -1,29 +1,18 @@
 include_directories(${CMAKE_BINARY_DIR}/win ${CMAKE_CURRENT_SOURCE_DIR})
 
-set(VNCVIEWER_JAR_PATH ${CMAKE_BINARY_DIR}/java/VncViewer.jar)
-set(INDEX_VNC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/index.vnc)
-
-configure_file(winvnc.rc.in winvnc.rc)
-
 add_executable(winvnc4 WIN32
   buildTime.cxx
   ControlPanel.cxx
-  JavaViewer.cxx
   ManagedListener.cxx
   QueryConnectDialog.cxx
   STrayIcon.cxx
   VNCServerService.cxx
   VNCServerWin32.cxx
   winvnc.cxx
-  ${CMAKE_CURRENT_BINARY_DIR}/winvnc.rc)
+  winvnc.rc)
 
 target_link_libraries(winvnc4 rfb rfb_win32 network rdr ws2_32.lib)
 
-if(BUILD_JAVA)
-  set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/winvnc.rc
-    PROPERTIES OBJECT_DEPENDS ${CMAKE_BINARY_DIR}/java/VncViewer.jar)
-endif()
-
 install(TARGETS winvnc4
   RUNTIME DESTINATION ${BIN_DIR}
 )
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/win/winvnc/JavaViewer.cxx b/win/winvnc/JavaViewer.cxx
deleted file mode 100644
index e2e307e..0000000
--- a/win/winvnc/JavaViewer.cxx
+++ /dev/null
@@ -1,107 +0,0 @@
-/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * 
- * This 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 2 of the License, or
- * (at your option) any later version.
- * 
- * This software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
- * USA.
- */
-
-#include <winvnc/JavaViewer.h>
-#include <winvnc/VNCServerWin32.h>
-#include <winvnc/resource.h>
-#include <rdr/MemInStream.h>
-#include <rfb/LogWriter.h>
-#include <rfb/VNCServerST.h>
-#include <rfb_win32/TCharArray.h>
-
-#include <windows.h>
-
-using namespace winvnc;
-using namespace rfb;
-
-
-static rfb::LogWriter vlog("JavaViewerServer");
-
-JavaViewerServer::JavaViewerServer(VNCServerWin32* svr) : server(svr) {
-}
-
-JavaViewerServer::~JavaViewerServer() {
-}
-
-rdr::InStream* JavaViewerServer::getFile(const char* name,
-                                         const char** contentType,
-                                         int* contentLength,
-                                         time_t* lastModified)
-{
-  if (strcmp(name, "/") == 0)
-    name = "/index.vnc";
-  if (strcmp(name, "/VncViewer.jar") == 0)
-    name = "VncViewer.jar";
-  if (strcmp(name, "/index.vnc") == 0)
-    name = "index.vnc";
-
-  HRSRC resource = FindResource(0, TStr(name), _T("HTTPFILE"));
-  if (!resource) return 0;
-  HGLOBAL handle = LoadResource(0, resource);
-  if (!handle) return 0;
-  void* buf = LockResource(handle);
-  int len = SizeofResource(0, resource);
-
-  rdr::InStream* is = new rdr::MemInStream(buf, len);
-  if (strlen(name) > 4 && strcasecmp(&name[strlen(name)-4], ".vnc") == 0) {
-    is = new rdr::SubstitutingInStream(is, this, 20);
-    *contentType = "text/html";
-  }
-  return is;
-}
-
-char* JavaViewerServer::substitute(const char* varName)
-{
-  if (strcmp(varName, "$$") == 0) {
-    return rfb::strDup("$");
-  }
-  if (strcmp(varName, "$PORT") == 0) {
-    char* str = new char[10];
-    sprintf(str, "%d", rfbPort);
-    return str;
-  }
-  if (strcmp(varName, "$WIDTH") == 0) {
-    char* str = new char[10];
-    sprintf(str, "%d", server->getDesktopSize().x);
-    return str;
-  }
-  if (strcmp(varName, "$HEIGHT") == 0) {
-    char* str = new char[10];
-    sprintf(str, "%d", server->getDesktopSize().y);
-    return str;
-  }
-  if (strcmp(varName, "$APPLETWIDTH") == 0) {
-    char* str = new char[10];
-    sprintf(str, "%d", server->getDesktopSize().x);
-    return str;
-  }
-  if (strcmp(varName, "$APPLETHEIGHT") == 0) {
-    char* str = new char[10];
-    sprintf(str, "%d", server->getDesktopSize().y);
-    return str;
-  }
-  if (strcmp(varName, "$DESKTOP") == 0) {
-    return rfb::strDup(server->getName());
-  }
-  if (strcmp(varName, "$USER") == 0) {
-    char tempStr[256];  DWORD tempStrLen = 256;
-    GetUserName(tempStr, &tempStrLen);
-    return rfb::strDup(tempStr);
-  }
-  return 0;
-}
diff --git a/win/winvnc/JavaViewer.h b/win/winvnc/JavaViewer.h
deleted file mode 100644
index 79a3969..0000000
--- a/win/winvnc/JavaViewer.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * 
- * This 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 2 of the License, or
- * (at your option) any later version.
- * 
- * This software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
- * USA.
- */
-
-// -=- JavaViewer.h
-
-// Custom HTTPServer-derived class which serves the Java VNC Viewer
-// to clients, using resource files compiled in to the WinVNC executable.
-
-#ifndef WINVNC_JAVA_VIEWER
-#define WINVNC_JAVA_VIEWER
-
-#include <rfb/HTTPServer.h>
-#include <rdr/SubstitutingInStream.h>
-
-namespace winvnc {
-
-  class VNCServerWin32;
-
-  class JavaViewerServer : public rfb::HTTPServer, public rdr::Substitutor {
-  public:
-    JavaViewerServer(VNCServerWin32* desktop);
-    virtual ~JavaViewerServer();
-
-    virtual rdr::InStream* getFile(const char* name, const char** contentType,
-                                   int* contentLength, time_t* lastModified);
-
-    // rdr::Substitutor callback
-    virtual char* substitute(const char* varName);
-
-    void setRFBport(int port) {
-      rfbPort = port;
-    }
-  protected:
-    int rfbPort;
-    VNCServerWin32* server;
-  };
-
-};
-
-#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 b164c65..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>
@@ -42,8 +43,6 @@
 const TCHAR* winvnc::VNCServerWin32::RegConfigPath = _T("Software\\TigerVNC\\WinVNC4");
 
 
-static IntParameter http_port("HTTPPortNumber",
-  "TCP/IP port on which the server will serve the Java applet VNC Viewer ", 5800);
 static IntParameter port_number("PortNumber",
   "TCP/IP port on which the server will accept connections", 5900);
 static StringParameter hosts("Hosts",
@@ -63,8 +62,7 @@
       CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNC") : 0),
     vncServer(CStr(ComputerName().buf), &desktop),
     thread_id(-1), runServer(false), isDesktopStarted(false),
-    httpServer(this), config(&sockMgr),
-    rfbSock(&sockMgr), httpSock(&sockMgr), trayIcon(0),
+    config(&sockMgr), rfbSock(&sockMgr), trayIcon(0),
     queryConnectDialog(0)
 {
   commandLock = new os::Mutex;
@@ -74,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);
@@ -148,16 +145,10 @@
   // -=- Make sure we're listening on the right ports.
   rfbSock.setServer(&vncServer);
   rfbSock.setPort(port_number, localHost);
-  httpSock.setServer(&httpServer);
-  httpSock.setPort(http_port, localHost);
-
-  // -=- Update the Java viewer's web page port number.
-  httpServer.setRFBport(rfbSock.isListening() ? port_number : 0);
 
   // -=- Update the TCP address filter for both ports, if open.
   CharArray pattern(hosts.getData());
   rfbSock.setFilter(pattern.buf);
-  httpSock.setFilter(pattern.buf);
 
   // -=- Update the tray icon tooltip text with IP addresses
   processAddressChange();
@@ -250,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() {
@@ -318,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:
@@ -345,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 ed051dc..1a73782 100644
--- a/win/winvnc/VNCServerWin32.h
+++ b/win/winvnc/VNCServerWin32.h
@@ -27,7 +27,6 @@
 #include <rfb_win32/SocketManager.h>
 #include <rfb_win32/TCharArray.h>
 #include <winvnc/QueryConnectDialog.h>
-#include <winvnc/JavaViewer.h>
 #include <winvnc/ManagedListener.h>
 
 namespace os {
@@ -38,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 {
@@ -74,21 +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);
-
-    // Used by JavaViewerServer
-    const char* getName() {return vncServer.getName();}
-    rfb::Point getDesktopSize() {return desktop.getFbSize();}
+    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
@@ -102,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;
@@ -121,12 +119,10 @@
     DWORD thread_id;
     bool runServer;
     bool isDesktopStarted;
-    JavaViewerServer httpServer;
     rfb::win32::SocketManager sockMgr;
     rfb::win32::RegConfig config;
 
     ManagedListener rfbSock;
-    ManagedListener httpSock;
     STrayIconThread* trayIcon;
 
     QueryConnectDialog* queryConnectDialog;
diff --git a/win/winvnc/index.vnc b/win/winvnc/index.vnc
deleted file mode 100644
index 560fa2e..0000000
--- a/win/winvnc/index.vnc
+++ /dev/null
@@ -1,22 +0,0 @@
-<!-- 
-     index.vnc - default HTML page for TigerVNC Java viewer applet, to be
-     used with WinVNC. On any file ending in .vnc, the HTTP server embedded in
-     WinVNC will substitute the following variables when preceded by a dollar:
-     USER, DESKTOP, APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT,
-     Use two dollar signs ($$) to get a dollar sign in the generated
-     HTML page.
--->
-
-<HTML>
-<TITLE>
-$USER's $DESKTOP desktop
-</TITLE>
-<APPLET CODE=com.tigervnc.vncviewer.VncViewer ARCHIVE=VncViewer.jar
-        WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
-<param name=PORT value=$PORT>
-<param name="Embed" value="true">
-<param name="draggable" value="true">
-</APPLET>
-<BR>
-<A href="http://www.tigervnc.org/">TigerVNC site</A>
-</HTML>
diff --git a/win/winvnc/resource.h b/win/winvnc/resource.h
index 68316be..0e52368 100644
--- a/win/winvnc/resource.h
+++ b/win/winvnc/resource.h
@@ -8,7 +8,6 @@
 #define IDD_DIALOG1                     103
 #define IDD_ABOUT                       104
 #define IDI_CONNECTED                   105
-#define IDR_VNCVIEWER_JAR               106
 #define IDD_QUERY_CONNECT               107
 #define IDD_ADD_NEW_CLIENT              108
 #define IDB_BITMAP                      109
diff --git a/win/winvnc/winvnc.rc.in b/win/winvnc/winvnc.rc
similarity index 95%
rename from win/winvnc/winvnc.rc.in
rename to win/winvnc/winvnc.rc
index 2b50966..91fc0f4 100644
--- a/win/winvnc/winvnc.rc.in
+++ b/win/winvnc/winvnc.rc
@@ -154,7 +154,7 @@
     LTEXT           ">version<",IDC_VERSION,170,10,72,15
     LTEXT           ">buildtime<",IDC_BUILDTIME,45,25,202,15
     LTEXT           ">copyright<",IDC_COPYRIGHT,45,40,256,15
-    LTEXT           "See http://www.tigervnc.org for more information on VNC.",
+    LTEXT           "See https://www.tigervnc.org for more information on VNC.",
                     IDC_STATIC,45,55,202,15
 END
 
@@ -217,18 +217,6 @@
 
 /////////////////////////////////////////////////////////////////////////////
 //
-// HTTPFILE
-//
-
-#cmakedefine BUILD_JAVA
-
-#ifdef BUILD_JAVA
-VNCVIEWER.JAR          HTTPFILE DISCARDABLE    "@VNCVIEWER_JAR_PATH@"
-INDEX.VNC              HTTPFILE DISCARDABLE    "@INDEX_VNC_PATH@"
-#endif
-
-/////////////////////////////////////////////////////////////////////////////
-//
 // 24
 //