Merge branches 'meta' and 'tlscrash' of https://github.com/CendioOssman/tigervnc
diff --git a/cmake/StaticBuild.cmake b/cmake/StaticBuild.cmake
index 4b58b1d..a994309 100644
--- a/cmake/StaticBuild.cmake
+++ b/cmake/StaticBuild.cmake
@@ -19,10 +19,7 @@
   set(BUILD_STATIC_GCC 1)
 
   set(JPEG_LIBRARIES "-Wl,-Bstatic -ljpeg -Wl,-Bdynamic")
-
-  if(WIN32)
-    set(ZLIB_LIBRARIES "-Wl,-Bstatic -lz -Wl,-Bdynamic")
-  endif()
+  set(ZLIB_LIBRARIES "-Wl,-Bstatic -lz -Wl,-Bdynamic")
 
   # gettext is included in libc on many unix systems
   if(NOT LIBC_HAS_DGETTEXT)
diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx
index ce489b1..fb95377 100644
--- a/common/rfb/CConnection.cxx
+++ b/common/rfb/CConnection.cxx
@@ -49,7 +49,8 @@
 CConnection::~CConnection()
 {
   setFramebuffer(NULL);
-  if (csecurity) csecurity->destroy();
+  if (csecurity)
+    delete csecurity;
   delete reader_;
   reader_ = 0;
   delete writer_;
@@ -234,14 +235,14 @@
   }
 
   state_ = RFBSTATE_SECURITY;
-  csecurity = security.GetCSecurity(secType);
+  csecurity = security.GetCSecurity(this, secType);
   processSecurityMsg();
 }
 
 void CConnection::processSecurityMsg()
 {
   vlog.debug("processing security message");
-  if (csecurity->processMsg(this)) {
+  if (csecurity->processMsg()) {
     state_ = RFBSTATE_SECURITY_RESULT;
     processSecurityResultMsg();
   }
diff --git a/common/rfb/CSecurity.h b/common/rfb/CSecurity.h
index 3fedc50..2e703c6 100644
--- a/common/rfb/CSecurity.h
+++ b/common/rfb/CSecurity.h
@@ -44,9 +44,9 @@
   class CConnection;
   class CSecurity {
   public:
+    CSecurity(CConnection* cc) { this->cc = cc; }
     virtual ~CSecurity() {}
-    virtual bool processMsg(CConnection* cc)=0;
-    virtual void destroy() { delete this; }
+    virtual bool processMsg() = 0;
     virtual int getType() const = 0;
     virtual const char* description() const = 0;
     virtual bool isSecure() const { return false; }
@@ -56,6 +56,9 @@
      * It MUST be set by viewer.
      */
     static UserPasswdGetter *upg;
+
+  protected:
+    CConnection* cc;
   };
 }
 #endif
diff --git a/common/rfb/CSecurityNone.h b/common/rfb/CSecurityNone.h
index a7db6e0..d07815f 100644
--- a/common/rfb/CSecurityNone.h
+++ b/common/rfb/CSecurityNone.h
@@ -29,7 +29,8 @@
 
   class CSecurityNone : public CSecurity {
   public:
-    virtual bool processMsg(CConnection* cc) { return true; }
+    CSecurityNone(CConnection* cc) : CSecurity(cc) {}
+    virtual bool processMsg() { return true; }
     virtual int getType() const {return secTypeNone;}
     virtual const char* description() const {return "No Encryption";}
   };
diff --git a/common/rfb/CSecurityPlain.cxx b/common/rfb/CSecurityPlain.cxx
index 8e383c3..b2fb736 100644
--- a/common/rfb/CSecurityPlain.cxx
+++ b/common/rfb/CSecurityPlain.cxx
@@ -26,7 +26,7 @@
 
 using namespace rfb;
 
-bool CSecurityPlain::processMsg(CConnection* cc)
+bool CSecurityPlain::processMsg()
 {
    rdr::OutStream* os = cc->getOutStream();
 
diff --git a/common/rfb/CSecurityPlain.h b/common/rfb/CSecurityPlain.h
index fb0d7a5..4ea8c9d 100644
--- a/common/rfb/CSecurityPlain.h
+++ b/common/rfb/CSecurityPlain.h
@@ -26,8 +26,8 @@
 
   class CSecurityPlain : public CSecurity {
   public:
-    CSecurityPlain() {}
-    virtual bool processMsg(CConnection* cc);
+    CSecurityPlain(CConnection* cc) : CSecurity(cc) {}
+    virtual bool processMsg();
     virtual int getType() const { return secTypePlain; }
     virtual const char* description() const { return "ask for username and password"; }
   };
diff --git a/common/rfb/CSecurityStack.cxx b/common/rfb/CSecurityStack.cxx
index 47c3f6d..55f3133 100644
--- a/common/rfb/CSecurityStack.cxx
+++ b/common/rfb/CSecurityStack.cxx
@@ -21,9 +21,9 @@
 
 using namespace rfb;
 
-CSecurityStack::CSecurityStack(int Type, const char*Name, CSecurity* s0,
-			       CSecurity* s1)
-  :name(Name),type(Type)
+CSecurityStack::CSecurityStack(CConnection* cc, int Type, const char* Name,
+                               CSecurity* s0, CSecurity* s1)
+  : CSecurity(cc), name(Name), type(Type)
 {
   state = 0;
   state0 = s0;
@@ -38,12 +38,12 @@
     delete state1;
 }
 
-bool CSecurityStack::processMsg(CConnection* cc)
+bool CSecurityStack::processMsg()
 {
   bool res=true;
   if (state == 0) {
     if (state0)
-      res = state0->processMsg(cc);
+      res = state0->processMsg();
 
     if (!res)
       return res;
@@ -53,7 +53,7 @@
 
   if (state == 1) {
     if(state1)
-      res = state1->processMsg(cc);
+      res = state1->processMsg();
 
     if(!res)
       return res;
diff --git a/common/rfb/CSecurityStack.h b/common/rfb/CSecurityStack.h
index a16003f..4be507e 100644
--- a/common/rfb/CSecurityStack.h
+++ b/common/rfb/CSecurityStack.h
@@ -27,9 +27,10 @@
 
   class CSecurityStack : public CSecurity {
   public:
-    CSecurityStack(int Type, const char *Name, CSecurity* s0 = 0, CSecurity* s1 = 0);
+    CSecurityStack(CConnection* cc, int Type, const char *Name,
+                   CSecurity* s0 = NULL, CSecurity* s1 = NULL);
     ~CSecurityStack();
-    virtual bool processMsg(CConnection* cc);
+    virtual bool processMsg();
     virtual int getType() const {return type;};
     virtual const char* description() const {return name;}
     virtual bool isSecure() const;
diff --git a/common/rfb/CSecurityTLS.cxx b/common/rfb/CSecurityTLS.cxx
index 9eeb76c..e1a31f7 100644
--- a/common/rfb/CSecurityTLS.cxx
+++ b/common/rfb/CSecurityTLS.cxx
@@ -67,8 +67,9 @@
 
 static LogWriter vlog("TLS");
 
-CSecurityTLS::CSecurityTLS(bool _anon) : session(0), anon_cred(0),
-						 anon(_anon), fis(0), fos(0)
+CSecurityTLS::CSecurityTLS(CConnection* cc, bool _anon)
+  : CSecurity(cc), session(NULL), anon_cred(NULL), cert_cred(NULL),
+    anon(_anon), tlsis(NULL), tlsos(NULL), rawis(NULL), rawos(NULL)
 {
   cafile = X509CA.getData();
   crlfile = X509CRL.getData();
@@ -115,6 +116,21 @@
     cert_cred = 0;
   }
 
+  if (rawis && rawos) {
+    cc->setStreams(rawis, rawos);
+    rawis = NULL;
+    rawos = NULL;
+  }
+
+  if (tlsis) {
+    delete tlsis;
+    tlsis = NULL;
+  }
+  if (tlsos) {
+    delete tlsos;
+    tlsos = NULL;
+  }
+
   if (session) {
     gnutls_deinit(session);
     session = 0;
@@ -126,18 +142,13 @@
 {
   shutdown(true);
 
-  if (fis)
-    delete fis;
-  if (fos)
-    delete fos;
-
   delete[] cafile;
   delete[] crlfile;
 
   gnutls_global_deinit();
 }
 
-bool CSecurityTLS::processMsg(CConnection* cc)
+bool CSecurityTLS::processMsg()
 {
   rdr::InStream* is = cc->getInStream();
   rdr::OutStream* os = cc->getOutStream();
@@ -164,17 +175,19 @@
       throw AuthFailureException("gnutls_set_default_priority failed");
 
     setParam();
-  }
 
-  rdr::TLSInStream *tlsis = new rdr::TLSInStream(is, session);
-  rdr::TLSOutStream *tlsos = new rdr::TLSOutStream(os, session);
+    // Create these early as they set up the push/pull functions
+    // for GnuTLS
+    tlsis = new rdr::TLSInStream(is, session);
+    tlsos = new rdr::TLSOutStream(os, session);
+
+    rawis = is;
+    rawos = os;
+  }
 
   int err;
   err = gnutls_handshake(session);
   if (err != GNUTLS_E_SUCCESS) {
-    delete tlsis;
-    delete tlsos;
-
     if (!gnutls_error_is_fatal(err))
       return false;
 
@@ -185,7 +198,7 @@
 
   checkSession();
 
-  cc->setStreams(fis = tlsis, fos = tlsos);
+  cc->setStreams(tlsis, tlsos);
 
   return true;
 }
diff --git a/common/rfb/CSecurityTLS.h b/common/rfb/CSecurityTLS.h
index e726d1e..4932c07 100644
--- a/common/rfb/CSecurityTLS.h
+++ b/common/rfb/CSecurityTLS.h
@@ -42,9 +42,9 @@
   class UserMsgBox;
   class CSecurityTLS : public CSecurity {
   public:
-    CSecurityTLS(bool _anon);
+    CSecurityTLS(CConnection* cc, bool _anon);
     virtual ~CSecurityTLS();
-    virtual bool processMsg(CConnection* cc);
+    virtual bool processMsg();
     virtual int getType() const { return anon ? secTypeTLSNone : secTypeX509None; }
     virtual const char* description() const
       { return anon ? "TLS Encryption without VncAuth" : "X509 Encryption without VncAuth"; }
@@ -69,8 +69,12 @@
     bool anon;
 
     char *cafile, *crlfile;
-    rdr::InStream* fis;
-    rdr::OutStream* fos;
+
+    rdr::InStream* tlsis;
+    rdr::OutStream* tlsos;
+
+    rdr::InStream* rawis;
+    rdr::OutStream* rawos;
   };
 }
 
diff --git a/common/rfb/CSecurityVeNCrypt.cxx b/common/rfb/CSecurityVeNCrypt.cxx
index f9597cc..22201dd 100644
--- a/common/rfb/CSecurityVeNCrypt.cxx
+++ b/common/rfb/CSecurityVeNCrypt.cxx
@@ -36,7 +36,8 @@
 
 static LogWriter vlog("CVeNCrypt");
 
-CSecurityVeNCrypt::CSecurityVeNCrypt(SecurityClient* sec) : csecurity(NULL), security(sec)
+CSecurityVeNCrypt::CSecurityVeNCrypt(CConnection* cc, SecurityClient* sec)
+  : CSecurity(cc), csecurity(NULL), security(sec)
 {
   haveRecvdMajorVersion = false;
   haveRecvdMinorVersion = false;
@@ -59,7 +60,7 @@
 	delete[] availableTypes;
 }
 
-bool CSecurityVeNCrypt::processMsg(CConnection* cc)
+bool CSecurityVeNCrypt::processMsg()
 {
   InStream* is = cc->getInStream();
   OutStream* os = cc->getOutStream();
@@ -171,7 +172,7 @@
       if (chosenType == secTypeInvalid || chosenType == secTypeVeNCrypt)
 	throw AuthFailureException("No valid VeNCrypt sub-type");
 
-      csecurity = security->GetCSecurity(chosenType);
+      csecurity = security->GetCSecurity(cc, chosenType);
 
       /* send chosen type to server */
       os->writeU32(chosenType);
@@ -188,7 +189,7 @@
     throw AuthFailureException("The server reported 0 VeNCrypt sub-types");
   }
 
-  return csecurity->processMsg(cc);
+  return csecurity->processMsg();
 }
 
 const char* CSecurityVeNCrypt::description() const
diff --git a/common/rfb/CSecurityVeNCrypt.h b/common/rfb/CSecurityVeNCrypt.h
index 6d978c7..d015e8f 100644
--- a/common/rfb/CSecurityVeNCrypt.h
+++ b/common/rfb/CSecurityVeNCrypt.h
@@ -34,9 +34,9 @@
   class CSecurityVeNCrypt : public CSecurity {
   public:
 
-    CSecurityVeNCrypt(SecurityClient* sec);
+    CSecurityVeNCrypt(CConnection* cc, SecurityClient* sec);
     ~CSecurityVeNCrypt();
-    virtual bool processMsg(CConnection* cc);// { return true; }
+    virtual bool processMsg();
     int getType() const {return chosenType;}
     virtual const char* description() const;
     virtual bool isSecure() const;
diff --git a/common/rfb/CSecurityVncAuth.cxx b/common/rfb/CSecurityVncAuth.cxx
index 46463e0..6a87498 100644
--- a/common/rfb/CSecurityVncAuth.cxx
+++ b/common/rfb/CSecurityVncAuth.cxx
@@ -40,7 +40,7 @@
 
 static const int vncAuthChallengeSize = 16;
 
-bool CSecurityVncAuth::processMsg(CConnection* cc)
+bool CSecurityVncAuth::processMsg()
 {
   rdr::InStream* is = cc->getInStream();
   rdr::OutStream* os = cc->getOutStream();
diff --git a/common/rfb/CSecurityVncAuth.h b/common/rfb/CSecurityVncAuth.h
index 391ed23..2da9817 100644
--- a/common/rfb/CSecurityVncAuth.h
+++ b/common/rfb/CSecurityVncAuth.h
@@ -25,9 +25,9 @@
 
   class CSecurityVncAuth : public CSecurity {
   public:
-    CSecurityVncAuth(void) {}
+    CSecurityVncAuth(CConnection* cc) : CSecurity(cc) {}
     virtual ~CSecurityVncAuth() {}
-    virtual bool processMsg(CConnection* cc);
+    virtual bool processMsg();
     virtual int getType() const {return secTypeVncAuth;};
     virtual const char* description() const {return "No Encryption";}
   };
diff --git a/common/rfb/Congestion.cxx b/common/rfb/Congestion.cxx
index 8162808..4a78452 100644
--- a/common/rfb/Congestion.cxx
+++ b/common/rfb/Congestion.cxx
@@ -291,11 +291,20 @@
 
 size_t Congestion::getBandwidth()
 {
+  size_t bandwidth;
+
   // No measurements yet? Guess RTT of 60 ms
   if (safeBaseRTT == (unsigned)-1)
-    return congWindow * 1000 / 60;
+    bandwidth = congWindow * 1000 / 60;
+  else
+    bandwidth = congWindow * 1000 / safeBaseRTT;
 
-  return congWindow * 1000 / safeBaseRTT;
+  // We're still probing so guess actual bandwidth is halfway between
+  // the current guess and the next one (slow start doubles each time)
+  if (inSlowStart)
+    bandwidth = bandwidth + bandwidth / 2;
+
+  return bandwidth;
 }
 
 void Congestion::debugTrace(const char* filename, int fd)
diff --git a/common/rfb/EncodeManager.cxx b/common/rfb/EncodeManager.cxx
index 53e0365..02128f7 100644
--- a/common/rfb/EncodeManager.cxx
+++ b/common/rfb/EncodeManager.cxx
@@ -1,6 +1,7 @@
 /* Copyright (C) 2000-2003 Constantin Kaplinsky.  All Rights Reserved.
  * Copyright (C) 2011 D. R. Commander.  All Rights Reserved.
  * Copyright 2014-2018 Pierre Ossman for Cendio AB
+ * Copyright 2018 Peter Astrand for Cendio AB
  * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -50,6 +51,9 @@
 // Don't bother with blocks smaller than this
 static const int SolidBlockMinArea = 2048;
 
+// How long we consider a region recently changed (in ms)
+static const int RecentChangeTimeout = 50;
+
 namespace rfb {
 
 enum EncoderClass {
@@ -123,7 +127,8 @@
   return "Unknown Encoder Type";
 }
 
-EncodeManager::EncodeManager(SConnection* conn_) : conn(conn_)
+EncodeManager::EncodeManager(SConnection* conn_)
+  : conn(conn_), recentChangeTimer(this)
 {
   StatsVector::iterator iter;
 
@@ -253,23 +258,57 @@
   return !lossyRegion.intersect(req).is_empty();
 }
 
+int EncodeManager::getNextLosslessRefresh(const Region& req)
+{
+  // Do we have something we can send right away?
+  if (!pendingRefreshRegion.intersect(req).is_empty())
+    return 0;
+
+  assert(needsLosslessRefresh(req));
+  assert(recentChangeTimer.isStarted());
+
+  return recentChangeTimer.getNextTimeout();
+}
+
 void EncodeManager::pruneLosslessRefresh(const Region& limits)
 {
   lossyRegion.assign_intersect(limits);
+  pendingRefreshRegion.assign_intersect(limits);
 }
 
 void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
                                 const RenderedCursor* renderedCursor)
 {
-    doUpdate(true, ui.changed, ui.copied, ui.copy_delta, pb, renderedCursor);
+  doUpdate(true, ui.changed, ui.copied, ui.copy_delta, pb, renderedCursor);
+
+  recentlyChangedRegion.assign_union(ui.changed);
+  recentlyChangedRegion.assign_union(ui.copied);
+  if (!recentChangeTimer.isStarted())
+    recentChangeTimer.start(RecentChangeTimeout);
 }
 
 void EncodeManager::writeLosslessRefresh(const Region& req, const PixelBuffer* pb,
                                          const RenderedCursor* renderedCursor,
                                          size_t maxUpdateSize)
 {
-    doUpdate(false, getLosslessRefresh(req, maxUpdateSize),
-             Region(), Point(), pb, renderedCursor);
+  doUpdate(false, getLosslessRefresh(req, maxUpdateSize),
+           Region(), Point(), pb, renderedCursor);
+}
+
+bool EncodeManager::handleTimeout(Timer* t)
+{
+  if (t == &recentChangeTimer) {
+    // Any lossy region that wasn't recently updated can
+    // now be scheduled for a refresh
+    pendingRefreshRegion.assign_union(lossyRegion.subtract(recentlyChangedRegion));
+    recentlyChangedRegion.clear();
+
+    // Will there be more to do? (i.e. do we need another round)
+    if (!lossyRegion.subtract(pendingRefreshRegion).is_empty())
+      return true;
+  }
+
+  return false;
 }
 
 void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
@@ -325,6 +364,8 @@
   enum EncoderClass solid, bitmap, bitmapRLE;
   enum EncoderClass indexed, indexedRLE, fullColour;
 
+  bool allowJPEG;
+
   rdr::S32 preferred;
 
   std::vector<int>::iterator iter;
@@ -332,6 +373,12 @@
   solid = bitmap = bitmapRLE = encoderRaw;
   indexed = indexedRLE = fullColour = encoderRaw;
 
+  allowJPEG = conn->cp.pf().bpp >= 16;
+  if (!allowLossy) {
+    if (encoders[encoderTightJPEG]->losslessQuality == -1)
+      allowJPEG = false;
+  }
+
   // Try to respect the client's wishes
   preferred = conn->getPreferredEncoding();
   switch (preferred) {
@@ -344,8 +391,7 @@
     bitmapRLE = indexedRLE = fullColour = encoderHextile;
     break;
   case encodingTight:
-    if (encoders[encoderTightJPEG]->isSupported() &&
-        (conn->cp.pf().bpp >= 16) && allowLossy)
+    if (encoders[encoderTightJPEG]->isSupported() && allowJPEG)
       fullColour = encoderTightJPEG;
     else
       fullColour = encoderTight;
@@ -362,8 +408,7 @@
   // Any encoders still unassigned?
 
   if (fullColour == encoderRaw) {
-    if (encoders[encoderTightJPEG]->isSupported() &&
-        (conn->cp.pf().bpp >= 16) && allowLossy)
+    if (encoders[encoderTightJPEG]->isSupported() && allowJPEG)
       fullColour = encoderTightJPEG;
     else if (encoders[encoderZRLE]->isSupported())
       fullColour = encoderZRLE;
@@ -421,9 +466,17 @@
     encoder = encoders[*iter];
 
     encoder->setCompressLevel(conn->cp.compressLevel);
-    encoder->setQualityLevel(conn->cp.qualityLevel);
-    encoder->setFineQualityLevel(conn->cp.fineQualityLevel,
-                                 conn->cp.subsampling);
+
+    if (allowLossy) {
+      encoder->setQualityLevel(conn->cp.qualityLevel);
+      encoder->setFineQualityLevel(conn->cp.fineQualityLevel,
+                                   conn->cp.subsampling);
+    } else {
+      int level = __rfbmax(conn->cp.qualityLevel,
+                           encoder->losslessQuality);
+      encoder->setQualityLevel(level);
+      encoder->setFineQualityLevel(-1, subsampleUndefined);
+    }
   }
 }
 
@@ -437,8 +490,11 @@
   // We make a conservative guess at the compression ratio at 2:1
   maxUpdateSize *= 2;
 
+  // We will measure pixels, not bytes (assume 32 bpp)
+  maxUpdateSize /= 4;
+
   area = 0;
-  lossyRegion.intersect(req).get_rects(&rects);
+  pendingRefreshRegion.intersect(req).get_rects(&rects);
   while (!rects.empty()) {
     size_t idx;
     Rect rect;
@@ -525,11 +581,17 @@
   encoder = encoders[klass];
   conn->writer()->startRect(rect, encoder->encoding);
 
-  if (encoder->flags & EncoderLossy)
+  if ((encoder->flags & EncoderLossy) &&
+      ((encoder->losslessQuality == -1) ||
+       (encoder->getQualityLevel() < encoder->losslessQuality)))
     lossyRegion.assign_union(Region(rect));
   else
     lossyRegion.assign_subtract(Region(rect));
 
+  // This was either a rect getting refreshed, or a rect that just got
+  // new content. Either way we should not try to refresh it anymore.
+  pendingRefreshRegion.assign_subtract(Region(rect));
+
   return encoder;
 }
 
@@ -574,6 +636,10 @@
   lossyCopy.translate(delta);
   lossyCopy.assign_intersect(copied);
   lossyRegion.assign_union(lossyCopy);
+
+  // Stop any pending refresh as a copy is enough that we consider
+  // this region to be recently changed
+  pendingRefreshRegion.assign_subtract(copied);
 }
 
 void EncodeManager::writeSolidRects(Region *changed, const PixelBuffer* pb)
diff --git a/common/rfb/EncodeManager.h b/common/rfb/EncodeManager.h
index a91c544..bdae906 100644
--- a/common/rfb/EncodeManager.h
+++ b/common/rfb/EncodeManager.h
@@ -25,6 +25,7 @@
 #include <rdr/types.h>
 #include <rfb/PixelBuffer.h>
 #include <rfb/Region.h>
+#include <rfb/Timer.h>
 
 namespace rfb {
   class SConnection;
@@ -36,7 +37,7 @@
 
   struct RectInfo;
 
-  class EncodeManager {
+  class EncodeManager : public Timer::Callback {
   public:
     EncodeManager(SConnection* conn);
     ~EncodeManager();
@@ -47,6 +48,8 @@
     static bool supported(int encoding);
 
     bool needsLosslessRefresh(const Region& req);
+    int getNextLosslessRefresh(const Region& req);
+
     void pruneLosslessRefresh(const Region& limits);
 
     void writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
@@ -57,6 +60,8 @@
                               size_t maxUpdateSize);
 
   protected:
+    virtual bool handleTimeout(Timer* t);
+
     void doUpdate(bool allowLossy, const Region& changed,
                   const Region& copied, const Point& copy_delta,
                   const PixelBuffer* pb,
@@ -117,6 +122,10 @@
     std::vector<int> activeEncoders;
 
     Region lossyRegion;
+    Region recentlyChangedRegion;
+    Region pendingRefreshRegion;
+
+    Timer recentChangeTimer;
 
     struct EncoderStats {
       unsigned rects;
diff --git a/common/rfb/Encoder.cxx b/common/rfb/Encoder.cxx
index 18b6680..0e29f1d 100644
--- a/common/rfb/Encoder.cxx
+++ b/common/rfb/Encoder.cxx
@@ -24,9 +24,11 @@
 using namespace rfb;
 
 Encoder::Encoder(SConnection *conn_, int encoding_,
-                 enum EncoderFlags flags_, unsigned int maxPaletteSize_) :
+                 enum EncoderFlags flags_,
+                 unsigned int maxPaletteSize_, int losslessQuality_) :
   encoding(encoding_), flags(flags_),
-  maxPaletteSize(maxPaletteSize_), conn(conn_)
+  maxPaletteSize(maxPaletteSize_), losslessQuality(losslessQuality_),
+  conn(conn_)
 {
 }
 
diff --git a/common/rfb/Encoder.h b/common/rfb/Encoder.h
index 66a10d2..d5a0288 100644
--- a/common/rfb/Encoder.h
+++ b/common/rfb/Encoder.h
@@ -42,7 +42,8 @@
   class Encoder {
   public:
     Encoder(SConnection* conn, int encoding,
-            enum EncoderFlags flags, unsigned int maxPaletteSize);
+            enum EncoderFlags flags, unsigned int maxPaletteSize=-1,
+            int losslessQuality=-1);
     virtual ~Encoder();
 
     // isSupported() should return a boolean indicating if this encoder
@@ -54,6 +55,9 @@
     virtual void setQualityLevel(int level) {};
     virtual void setFineQualityLevel(int quality, int subsampling) {};
 
+    virtual int getCompressLevel() { return -1; };
+    virtual int getQualityLevel() { return -1; };
+
     // writeRect() is the main interface that encodes the given rectangle
     // with data from the PixelBuffer onto the SConnection given at
     // encoder creation.
@@ -92,6 +96,10 @@
     // Maximum size of the palette per rect
     const unsigned int maxPaletteSize;
 
+    // Minimum level where the quality loss will not be noticed by
+    // most users (only relevant with EncoderLossy flag)
+    const int losslessQuality;
+
   protected:
     SConnection* conn;
   };
diff --git a/common/rfb/HextileEncoder.cxx b/common/rfb/HextileEncoder.cxx
index 418a440..47e5251 100644
--- a/common/rfb/HextileEncoder.cxx
+++ b/common/rfb/HextileEncoder.cxx
@@ -45,7 +45,7 @@
 #undef BPP
 
 HextileEncoder::HextileEncoder(SConnection* conn) :
-  Encoder(conn, encodingHextile, EncoderPlain, -1)
+  Encoder(conn, encodingHextile, EncoderPlain)
 {
 }
 
diff --git a/common/rfb/RREEncoder.cxx b/common/rfb/RREEncoder.cxx
index 60a0663..7287e7e 100644
--- a/common/rfb/RREEncoder.cxx
+++ b/common/rfb/RREEncoder.cxx
@@ -37,7 +37,7 @@
 #undef BPP
 
 RREEncoder::RREEncoder(SConnection* conn) :
-  Encoder(conn, encodingRRE, EncoderPlain, -1)
+  Encoder(conn, encodingRRE, EncoderPlain)
 {
 }
 
diff --git a/common/rfb/RawEncoder.cxx b/common/rfb/RawEncoder.cxx
index 4090427..b12cf06 100644
--- a/common/rfb/RawEncoder.cxx
+++ b/common/rfb/RawEncoder.cxx
@@ -25,7 +25,7 @@
 using namespace rfb;
 
 RawEncoder::RawEncoder(SConnection* conn) :
-  Encoder(conn, encodingRaw, EncoderPlain, -1)
+  Encoder(conn, encodingRaw, EncoderPlain)
 {
 }
 
diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx
index 6b81055..efc26ac 100644
--- a/common/rfb/SConnection.cxx
+++ b/common/rfb/SConnection.cxx
@@ -64,7 +64,8 @@
 
 SConnection::~SConnection()
 {
-  if (ssecurity) ssecurity->destroy();
+  if (ssecurity)
+    delete ssecurity;
   delete reader_;
   reader_ = 0;
   delete writer_;
@@ -155,7 +156,7 @@
     os->writeU32(*i);
     if (*i == secTypeNone) os->flush();
     state_ = RFBSTATE_SECURITY;
-    ssecurity = security.GetSSecurity(*i);
+    ssecurity = security.GetSSecurity(this, *i);
     processSecurityMsg();
     return;
   }
@@ -198,7 +199,7 @@
 
   try {
     state_ = RFBSTATE_SECURITY;
-    ssecurity = security.GetSSecurity(secType);
+    ssecurity = security.GetSSecurity(this, secType);
   } catch (rdr::Exception& e) {
     throwConnFailedException("%s", e.str());
   }
@@ -210,7 +211,7 @@
 {
   vlog.debug("processing security message");
   try {
-    bool done = ssecurity->processMsg(this);
+    bool done = ssecurity->processMsg();
     if (done) {
       state_ = RFBSTATE_QUERYING;
       setAccessRights(ssecurity->getAccessRights());
diff --git a/common/rfb/SSecurity.h b/common/rfb/SSecurity.h
index 6da63c3..8ae7902 100644
--- a/common/rfb/SSecurity.h
+++ b/common/rfb/SSecurity.h
@@ -52,9 +52,9 @@
 
   class SSecurity {
   public:
+    SSecurity(SConnection* sc) { this->sc = sc; }
     virtual ~SSecurity() {}
-    virtual bool processMsg(SConnection* sc)=0;
-    virtual void destroy() { delete this; }
+    virtual bool processMsg() = 0;
     virtual int getType() const = 0;
 
     // getUserName() gets the name of the user attempting authentication.  The
@@ -64,6 +64,9 @@
     virtual const char* getUserName() const = 0;
 
     virtual SConnection::AccessRights getAccessRights() const { return SConnection::AccessDefault; }
+
+  protected:
+    SConnection* sc;
   };
 
 }
diff --git a/common/rfb/SSecurityNone.h b/common/rfb/SSecurityNone.h
index 5c19f29..f14d83a 100644
--- a/common/rfb/SSecurityNone.h
+++ b/common/rfb/SSecurityNone.h
@@ -28,7 +28,8 @@
 
   class SSecurityNone : public SSecurity {
   public:
-    virtual bool processMsg(SConnection* sc) { return true; }
+    SSecurityNone(SConnection* sc) : SSecurity(sc) {}
+    virtual bool processMsg() { return true; }
     virtual int getType() const {return secTypeNone;}
     virtual const char* getUserName() const {return 0;}
   };
diff --git a/common/rfb/SSecurityPlain.cxx b/common/rfb/SSecurityPlain.cxx
index fc9dff2..6d48b65 100644
--- a/common/rfb/SSecurityPlain.cxx
+++ b/common/rfb/SSecurityPlain.cxx
@@ -60,7 +60,7 @@
   return false;
 }
 
-SSecurityPlain::SSecurityPlain()
+SSecurityPlain::SSecurityPlain(SConnection* sc) : SSecurity(sc)
 {
 #ifdef HAVE_PAM
   valid = new UnixPasswordValidator();
@@ -73,7 +73,7 @@
   state = 0;
 }
 
-bool SSecurityPlain::processMsg(SConnection* sc)
+bool SSecurityPlain::processMsg()
 {
   rdr::InStream* is = sc->getInStream();
   char* pw;
diff --git a/common/rfb/SSecurityPlain.h b/common/rfb/SSecurityPlain.h
index 4bf42b7..4b12da0 100644
--- a/common/rfb/SSecurityPlain.h
+++ b/common/rfb/SSecurityPlain.h
@@ -47,8 +47,8 @@
 
   class SSecurityPlain : public SSecurity {
   public:
-    SSecurityPlain();
-    virtual bool processMsg(SConnection* sc);
+    SSecurityPlain(SConnection* sc);
+    virtual bool processMsg();
     virtual int getType() const { return secTypePlain; };
     virtual const char* getUserName() const { return username.buf; }
 
diff --git a/common/rfb/SSecurityStack.cxx b/common/rfb/SSecurityStack.cxx
index 478ce4f..74509e7 100644
--- a/common/rfb/SSecurityStack.cxx
+++ b/common/rfb/SSecurityStack.cxx
@@ -20,8 +20,11 @@
 
 using namespace rfb;
 
-SSecurityStack::SSecurityStack(int Type, SSecurity* s0, SSecurity* s1)
-  :state(0), state0(s0), state1(s1), type(Type) {}
+SSecurityStack::SSecurityStack(SConnection* sc, int Type,
+                               SSecurity* s0, SSecurity* s1)
+  : SSecurity(sc), state(0), state0(s0), state1(s1), type(Type)
+{
+}
 
 SSecurityStack::~SSecurityStack()
 {
@@ -31,13 +34,13 @@
     delete state1;
 }
 
-bool SSecurityStack::processMsg(SConnection* cc)
+bool SSecurityStack::processMsg()
 {
   bool res = true;
 
   if (state == 0) {
     if (state0)
-      res = state0->processMsg(cc);
+      res = state0->processMsg();
     if (!res)
       return res;
     state++;
@@ -45,7 +48,7 @@
 
   if (state == 1) {
     if (state1)
-      res = state1->processMsg(cc);
+      res = state1->processMsg();
     if (!res)
       return res;
     state++;
diff --git a/common/rfb/SSecurityStack.h b/common/rfb/SSecurityStack.h
index dd743d2..8b412bd 100644
--- a/common/rfb/SSecurityStack.h
+++ b/common/rfb/SSecurityStack.h
@@ -26,9 +26,10 @@
 
   class SSecurityStack : public SSecurity {
   public:
-    SSecurityStack(int Type, SSecurity* s0 = 0, SSecurity* s1 = 0);
+    SSecurityStack(SConnection* sc, int Type,
+                   SSecurity* s0 = NULL, SSecurity* s1 = NULL);
     ~SSecurityStack();
-    virtual bool processMsg(SConnection* cc);
+    virtual bool processMsg();
     virtual int getType() const { return type; };
     virtual const char* getUserName() const;
     virtual SConnection::AccessRights getAccessRights() const;
diff --git a/common/rfb/SSecurityTLS.cxx b/common/rfb/SSecurityTLS.cxx
index 5576a06..49532f5 100644
--- a/common/rfb/SSecurityTLS.cxx
+++ b/common/rfb/SSecurityTLS.cxx
@@ -49,9 +49,10 @@
 
 static LogWriter vlog("TLS");
 
-SSecurityTLS::SSecurityTLS(bool _anon) : session(0), dh_params(0),
-						 anon_cred(0), cert_cred(0),
-						 anon(_anon), fis(0), fos(0)
+SSecurityTLS::SSecurityTLS(SConnection* sc, bool _anon)
+  : SSecurity(sc), session(NULL), dh_params(NULL), anon_cred(NULL),
+    cert_cred(NULL), anon(_anon), tlsis(NULL), tlsos(NULL),
+    rawis(NULL), rawos(NULL)
 {
   certfile = X509_CertFile.getData();
   keyfile = X509_KeyFile.getData();
@@ -84,6 +85,21 @@
     cert_cred = 0;
   }
 
+  if (rawis && rawos) {
+    sc->setStreams(rawis, rawos);
+    rawis = NULL;
+    rawos = NULL;
+  }
+
+  if (tlsis) {
+    delete tlsis;
+    tlsis = NULL;
+  }
+  if (tlsos) {
+    delete tlsos;
+    tlsos = NULL;
+  }
+
   if (session) {
     gnutls_deinit(session);
     session = 0;
@@ -95,25 +111,20 @@
 {
   shutdown();
 
-  if (fis)
-    delete fis;
-  if (fos)
-    delete fos;
-
   delete[] keyfile;
   delete[] certfile;
 
   gnutls_global_deinit();
 }
 
-bool SSecurityTLS::processMsg(SConnection *sc)
+bool SSecurityTLS::processMsg()
 {
-  rdr::InStream* is = sc->getInStream();
-  rdr::OutStream* os = sc->getOutStream();
-
   vlog.debug("Process security message (session %p)", session);
 
   if (!session) {
+    rdr::InStream* is = sc->getInStream();
+    rdr::OutStream* os = sc->getOutStream();
+
     if (gnutls_init(&session, GNUTLS_SERVER) != GNUTLS_E_SUCCESS)
       throw AuthFailureException("gnutls_init failed");
 
@@ -130,17 +141,19 @@
 
     os->writeU8(1);
     os->flush();
-  }
 
-  rdr::TLSInStream *tlsis = new rdr::TLSInStream(is, session);
-  rdr::TLSOutStream *tlsos = new rdr::TLSOutStream(os, session);
+    // Create these early as they set up the push/pull functions
+    // for GnuTLS
+    tlsis = new rdr::TLSInStream(is, session);
+    tlsos = new rdr::TLSOutStream(os, session);
+
+    rawis = is;
+    rawos = os;
+  }
 
   int err;
   err = gnutls_handshake(session);
   if (err != GNUTLS_E_SUCCESS) {
-    delete tlsis;
-    delete tlsos;
-
     if (!gnutls_error_is_fatal(err)) {
       vlog.debug("Deferring completion of TLS handshake: %s", gnutls_strerror(err));
       return false;
@@ -152,7 +165,7 @@
 
   vlog.debug("Handshake completed");
 
-  sc->setStreams(fis = tlsis, fos = tlsos);
+  sc->setStreams(tlsis, tlsos);
 
   return true;
 }
diff --git a/common/rfb/SSecurityTLS.h b/common/rfb/SSecurityTLS.h
index 30242a2..6f71182 100644
--- a/common/rfb/SSecurityTLS.h
+++ b/common/rfb/SSecurityTLS.h
@@ -40,9 +40,9 @@
 
   class SSecurityTLS : public SSecurity {
   public:
-    SSecurityTLS(bool _anon);
+    SSecurityTLS(SConnection* sc, bool _anon);
     virtual ~SSecurityTLS();
-    virtual bool processMsg(SConnection* sc);
+    virtual bool processMsg();
     virtual const char* getUserName() const {return 0;}
     virtual int getType() const { return anon ? secTypeTLSNone : secTypeX509None;}
 
@@ -63,8 +63,11 @@
     int type;
     bool anon;
 
-    rdr::InStream* fis;
-    rdr::OutStream* fos;
+    rdr::InStream* tlsis;
+    rdr::OutStream* tlsos;
+
+    rdr::InStream* rawis;
+    rdr::OutStream* rawos;
   };
 
 }
diff --git a/common/rfb/SSecurityVeNCrypt.cxx b/common/rfb/SSecurityVeNCrypt.cxx
index ce6c71b..d522ef6 100644
--- a/common/rfb/SSecurityVeNCrypt.cxx
+++ b/common/rfb/SSecurityVeNCrypt.cxx
@@ -38,7 +38,8 @@
 
 static LogWriter vlog("SVeNCrypt");
 
-SSecurityVeNCrypt::SSecurityVeNCrypt(SecurityServer *sec) : security(sec)
+SSecurityVeNCrypt::SSecurityVeNCrypt(SConnection* sc, SecurityServer *sec)
+  : SSecurity(sc), security(sec)
 {
   ssecurity = NULL;
   haveSentVersion = false;
@@ -63,7 +64,7 @@
   }
 }
 
-bool SSecurityVeNCrypt::processMsg(SConnection* sc)
+bool SSecurityVeNCrypt::processMsg()
 {
   rdr::InStream* is = sc->getInStream();
   rdr::OutStream* os = sc->getOutStream();
@@ -166,11 +167,11 @@
     if (chosenType == secTypeInvalid || chosenType == secTypeVeNCrypt)
       throw AuthFailureException("No valid VeNCrypt sub-type");
 
-    ssecurity = security->GetSSecurity(chosenType);
+    ssecurity = security->GetSSecurity(sc, chosenType);
   }
 
   /* continue processing the messages */
-  return ssecurity->processMsg(sc);
+  return ssecurity->processMsg();
 }
 
 const char* SSecurityVeNCrypt::getUserName() const
diff --git a/common/rfb/SSecurityVeNCrypt.h b/common/rfb/SSecurityVeNCrypt.h
index f9c753f..06758b8 100644
--- a/common/rfb/SSecurityVeNCrypt.h
+++ b/common/rfb/SSecurityVeNCrypt.h
@@ -36,9 +36,9 @@
 
   class SSecurityVeNCrypt : public SSecurity {
   public:
-    SSecurityVeNCrypt(SecurityServer *sec);
+    SSecurityVeNCrypt(SConnection* sc, SecurityServer *sec);
     ~SSecurityVeNCrypt();
-    virtual bool processMsg(SConnection* sc);// { return true; }
+    virtual bool processMsg();
     virtual int getType() const { return chosenType; }
     virtual const char* getUserName() const;
     virtual SConnection::AccessRights getAccessRights() const;
diff --git a/common/rfb/SSecurityVncAuth.cxx b/common/rfb/SSecurityVncAuth.cxx
index 05488f6..882f0b0 100644
--- a/common/rfb/SSecurityVncAuth.cxx
+++ b/common/rfb/SSecurityVncAuth.cxx
@@ -48,8 +48,9 @@
 ("Password", "Obfuscated binary encoding of the password which clients must supply to "
  "access the server", &SSecurityVncAuth::vncAuthPasswdFile);
 
-SSecurityVncAuth::SSecurityVncAuth(void)
-  : sentChallenge(false), responsePos(0), pg(&vncAuthPasswd), accessRights(0)
+SSecurityVncAuth::SSecurityVncAuth(SConnection* sc)
+  : SSecurity(sc), sentChallenge(false), responsePos(0),
+    pg(&vncAuthPasswd), accessRights(0)
 {
 }
 
@@ -70,7 +71,7 @@
   return memcmp(response, expectedResponse, vncAuthChallengeSize) == 0;
 }
 
-bool SSecurityVncAuth::processMsg(SConnection* sc)
+bool SSecurityVncAuth::processMsg()
 {
   rdr::InStream* is = sc->getInStream();
   rdr::OutStream* os = sc->getOutStream();
diff --git a/common/rfb/SSecurityVncAuth.h b/common/rfb/SSecurityVncAuth.h
index a1d1747..fe00b03 100644
--- a/common/rfb/SSecurityVncAuth.h
+++ b/common/rfb/SSecurityVncAuth.h
@@ -51,8 +51,8 @@
 
   class SSecurityVncAuth : public SSecurity {
   public:
-    SSecurityVncAuth(void);
-    virtual bool processMsg(SConnection* sc);
+    SSecurityVncAuth(SConnection* sc);
+    virtual bool processMsg();
     virtual int getType() const {return secTypeVncAuth;}
     virtual const char* getUserName() const {return 0;}
     virtual SConnection::AccessRights getAccessRights() const { return accessRights; }
diff --git a/common/rfb/SecurityClient.cxx b/common/rfb/SecurityClient.cxx
index 9bd780f..23c1d67 100644
--- a/common/rfb/SecurityClient.cxx
+++ b/common/rfb/SecurityClient.cxx
@@ -55,7 +55,7 @@
 #endif
 ConfViewer);
 
-CSecurity* SecurityClient::GetCSecurity(U32 secType)
+CSecurity* SecurityClient::GetCSecurity(CConnection* cc, U32 secType)
 {
   assert (CSecurity::upg != NULL); /* (upg == NULL) means bug in the viewer */
 #ifdef HAVE_GNUTLS
@@ -66,29 +66,39 @@
     goto bail;
 
   switch (secType) {
-  case secTypeNone: return new CSecurityNone();
-  case secTypeVncAuth: return new CSecurityVncAuth();
-  case secTypeVeNCrypt: return new CSecurityVeNCrypt(this);
-  case secTypePlain: return new CSecurityPlain();
+  case secTypeNone: return new CSecurityNone(cc);
+  case secTypeVncAuth: return new CSecurityVncAuth(cc);
+  case secTypeVeNCrypt: return new CSecurityVeNCrypt(cc, this);
+  case secTypePlain: return new CSecurityPlain(cc);
 #ifdef HAVE_GNUTLS
   case secTypeTLSNone:
-    return new CSecurityStack(secTypeTLSNone, "TLS with no password",
-			      new CSecurityTLS(true));
+    return new CSecurityStack(cc, secTypeTLSNone,
+                              "TLS with no password",
+                              new CSecurityTLS(cc, true));
   case secTypeTLSVnc:
-    return new CSecurityStack(secTypeTLSVnc, "TLS with VNCAuth",
-			      new CSecurityTLS(true), new CSecurityVncAuth());
+    return new CSecurityStack(cc, secTypeTLSVnc,
+                              "TLS with VNCAuth",
+                              new CSecurityTLS(cc, true),
+                              new CSecurityVncAuth(cc));
   case secTypeTLSPlain:
-    return new CSecurityStack(secTypeTLSPlain, "TLS with Username/Password",
-			      new CSecurityTLS(true), new CSecurityPlain());
+    return new CSecurityStack(cc, secTypeTLSPlain,
+                              "TLS with Username/Password",
+                              new CSecurityTLS(cc, true),
+                              new CSecurityPlain(cc));
   case secTypeX509None:
-    return new CSecurityStack(secTypeX509None, "X509 with no password",
-			      new CSecurityTLS(false));
+    return new CSecurityStack(cc, secTypeX509None,
+                              "X509 with no password",
+                              new CSecurityTLS(cc, false));
   case secTypeX509Vnc:
-    return new CSecurityStack(secTypeX509Vnc, "X509 with VNCAuth",
-			      new CSecurityTLS(false), new CSecurityVncAuth());
+    return new CSecurityStack(cc, secTypeX509Vnc,
+                              "X509 with VNCAuth",
+                              new CSecurityTLS(cc, false),
+                              new CSecurityVncAuth(cc));
   case secTypeX509Plain:
-    return new CSecurityStack(secTypeX509Plain, "X509 with Username/Password",
-			      new CSecurityTLS(false), new CSecurityPlain());
+    return new CSecurityStack(cc, secTypeX509Plain,
+                              "X509 with Username/Password",
+                              new CSecurityTLS(cc, false),
+                              new CSecurityPlain(cc));
 #endif
   }
 
diff --git a/common/rfb/SecurityClient.h b/common/rfb/SecurityClient.h
index b8ad831..3074a87 100644
--- a/common/rfb/SecurityClient.h
+++ b/common/rfb/SecurityClient.h
@@ -33,7 +33,7 @@
     SecurityClient(void) : Security(secTypes) {}
 
     /* Create client side CSecurity class instance */
-    CSecurity* GetCSecurity(rdr::U32 secType);
+    CSecurity* GetCSecurity(CConnection* cc, rdr::U32 secType);
 
     static void setDefaults(void);
 
diff --git a/common/rfb/SecurityServer.cxx b/common/rfb/SecurityServer.cxx
index e0aee13..97b133c 100644
--- a/common/rfb/SecurityServer.cxx
+++ b/common/rfb/SecurityServer.cxx
@@ -49,29 +49,29 @@
 #endif
 ConfServer);
 
-SSecurity* SecurityServer::GetSSecurity(U32 secType)
+SSecurity* SecurityServer::GetSSecurity(SConnection* sc, U32 secType)
 {
   if (!IsSupported(secType))
     goto bail;
 
   switch (secType) {
-  case secTypeNone: return new SSecurityNone();
-  case secTypeVncAuth: return new SSecurityVncAuth();
-  case secTypeVeNCrypt: return new SSecurityVeNCrypt(this);
-  case secTypePlain: return new SSecurityPlain();
+  case secTypeNone: return new SSecurityNone(sc);
+  case secTypeVncAuth: return new SSecurityVncAuth(sc);
+  case secTypeVeNCrypt: return new SSecurityVeNCrypt(sc, this);
+  case secTypePlain: return new SSecurityPlain(sc);
 #ifdef HAVE_GNUTLS
   case secTypeTLSNone:
-    return new SSecurityStack(secTypeTLSNone, new SSecurityTLS(true));
+    return new SSecurityStack(sc, secTypeTLSNone, new SSecurityTLS(sc, true));
   case secTypeTLSVnc:
-    return new SSecurityStack(secTypeTLSVnc, new SSecurityTLS(true), new SSecurityVncAuth());
+    return new SSecurityStack(sc, secTypeTLSVnc, new SSecurityTLS(sc, true), new SSecurityVncAuth(sc));
   case secTypeTLSPlain:
-    return new SSecurityStack(secTypeTLSPlain, new SSecurityTLS(true), new SSecurityPlain());
+    return new SSecurityStack(sc, secTypeTLSPlain, new SSecurityTLS(sc, true), new SSecurityPlain(sc));
   case secTypeX509None:
-    return new SSecurityStack(secTypeX509None, new SSecurityTLS(false));
+    return new SSecurityStack(sc, secTypeX509None, new SSecurityTLS(sc, false));
   case secTypeX509Vnc:
-    return new SSecurityStack(secTypeX509None, new SSecurityTLS(false), new SSecurityVncAuth());
+    return new SSecurityStack(sc, secTypeX509None, new SSecurityTLS(sc, false), new SSecurityVncAuth(sc));
   case secTypeX509Plain:
-    return new SSecurityStack(secTypeX509Plain, new SSecurityTLS(false), new SSecurityPlain());
+    return new SSecurityStack(sc, secTypeX509Plain, new SSecurityTLS(sc, false), new SSecurityPlain(sc));
 #endif
   }
 
diff --git a/common/rfb/SecurityServer.h b/common/rfb/SecurityServer.h
index 019d67f..354f642 100644
--- a/common/rfb/SecurityServer.h
+++ b/common/rfb/SecurityServer.h
@@ -24,7 +24,8 @@
 #include <rfb/Security.h>
 
 namespace rfb {
-  
+
+  class SConnection;
   class SSecurity;
 
   class SecurityServer : public Security {
@@ -32,7 +33,7 @@
     SecurityServer(void) : Security(secTypes) {}
 
     /* Create server side SSecurity class instance */
-    SSecurity* GetSSecurity(rdr::U32 secType);
+    SSecurity* GetSSecurity(SConnection* sc, rdr::U32 secType);
 
     static StringParameter secTypes;
   };
diff --git a/common/rfb/TightJPEGEncoder.cxx b/common/rfb/TightJPEGEncoder.cxx
index 385207f..38cb4eb 100644
--- a/common/rfb/TightJPEGEncoder.cxx
+++ b/common/rfb/TightJPEGEncoder.cxx
@@ -64,7 +64,8 @@
 
 
 TightJPEGEncoder::TightJPEGEncoder(SConnection* conn) :
-  Encoder(conn, encodingTight, (EncoderFlags)(EncoderUseNativePF | EncoderLossy), -1),
+  Encoder(conn, encodingTight,
+          (EncoderFlags)(EncoderUseNativePF | EncoderLossy), -1, 9),
   qualityLevel(-1), fineQuality(-1), fineSubsampling(subsampleUndefined)
 {
 }
@@ -101,6 +102,11 @@
   fineSubsampling = subsampling;
 }
 
+int TightJPEGEncoder::getQualityLevel()
+{
+  return qualityLevel;
+}
+
 void TightJPEGEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)
 {
   const rdr::U8* buffer;
diff --git a/common/rfb/TightJPEGEncoder.h b/common/rfb/TightJPEGEncoder.h
index 458c383..3d8fa8c 100644
--- a/common/rfb/TightJPEGEncoder.h
+++ b/common/rfb/TightJPEGEncoder.h
@@ -35,6 +35,8 @@
     virtual void setQualityLevel(int level);
     virtual void setFineQualityLevel(int quality, int subsampling);
 
+    virtual int getQualityLevel();
+
     virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
     virtual void writeSolidRect(int width, int height,
                                 const PixelFormat& pf,
diff --git a/common/rfb/Timer.cxx b/common/rfb/Timer.cxx
index fd1a87a..b9dd2f6 100644
--- a/common/rfb/Timer.cxx
+++ b/common/rfb/Timer.cxx
@@ -151,7 +151,7 @@
 int Timer::getRemainingMs() {
   timeval now;
   gettimeofday(&now, 0);
-  return __rfbmax(0, diffTimeMillis(pending.front()->dueTime, now));
+  return __rfbmax(0, diffTimeMillis(dueTime, now));
 }
 
 bool Timer::isBefore(timeval other) {
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index f22b993..f1591f4 100644
--- a/common/rfb/VNCSConnectionST.cxx
+++ b/common/rfb/VNCSConnectionST.cxx
@@ -1,5 +1,6 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
  * Copyright 2009-2018 Pierre Ossman for Cendio AB
+ * Copyright 2018 Peter Astrand for Cendio AB
  * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -48,7 +49,7 @@
     inProcessMessages(false),
     pendingSyncFence(false), syncFence(false), fenceFlags(0),
     fenceDataLen(0), fenceData(NULL), congestionTimer(this),
-    server(server_), updates(false),
+    losslessTimer(this), server(server_), updates(false),
     updateRenderedCursor(false), removeRenderedCursor(false),
     continuousUpdates(false), encodeManager(this), pointerEventTime(0),
     clientHasCursor(false),
@@ -839,7 +840,8 @@
 bool VNCSConnectionST::handleTimeout(Timer* t)
 {
   try {
-    if (t == &congestionTimer)
+    if ((t == &congestionTimer) ||
+        (t == &losslessTimer))
       writeFramebufferUpdate();
   } catch (rdr::Exception& e) {
     close(e.str());
@@ -1065,28 +1067,46 @@
   }
 
   // Return if there is nothing to send the client.
+  if (ui.is_empty() && !writer()->needFakeUpdate()) {
+    int eta;
 
-  if (ui.is_empty() && !writer()->needFakeUpdate() &&
-      !encodeManager.needsLosslessRefresh(req))
-    return;
+    // Any lossless refresh that needs handling?
+    if (!encodeManager.needsLosslessRefresh(req))
+      return;
+
+    // Now? Or later?
+    eta = encodeManager.getNextLosslessRefresh(req);
+    if (eta > 0) {
+      losslessTimer.start(eta);
+      return;
+    }
+  }
 
   writeRTTPing();
 
   if (!ui.is_empty())
     encodeManager.writeUpdate(ui, server->getPixelBuffer(), cursor);
   else {
-    size_t maxUpdateSize;
+    int nextUpdate;
 
     // FIXME: If continuous updates aren't used then the client might
     //        be slower than frameRate in its requests and we could
     //        afford a larger update size
+    nextUpdate = server->msToNextUpdate();
+    if (nextUpdate > 0) {
+      size_t bandwidth, maxUpdateSize;
 
-    // FIXME: Bandwidth estimation without congestion control
-    maxUpdateSize = congestion.getBandwidth() *
-                    server->msToNextUpdate() / 1000;
+      // FIXME: Bandwidth estimation without congestion control
+      bandwidth = congestion.getBandwidth();
 
-    encodeManager.writeLosslessRefresh(req, server->getPixelBuffer(),
-                                       cursor, maxUpdateSize);
+      // FIXME: Hard coded value for maximum CPU throughput
+      if (bandwidth > 5000000)
+        bandwidth = 5000000;
+
+      maxUpdateSize = bandwidth * nextUpdate / 1000;
+      encodeManager.writeLosslessRefresh(req, server->getPixelBuffer(),
+                                         cursor, maxUpdateSize);
+    }
   }
 
   writeRTTPing();
diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h
index 2f075a6..dfc8bcb 100644
--- a/common/rfb/VNCSConnectionST.h
+++ b/common/rfb/VNCSConnectionST.h
@@ -191,6 +191,7 @@
 
     Congestion congestion;
     Timer congestionTimer;
+    Timer losslessTimer;
 
     VNCServerST* server;
     SimpleUpdateTracker updates;