Merge pull request #80 from bphinz/buildfixes

More RPM spec file cleanup
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1dcfd9e..3091d7b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,10 +18,10 @@
 include(CMakeMacroLibtoolFile)
 
 project(tigervnc)
-set(VERSION 1.3.80)
+set(VERSION 1.4.80)
 
 # The RC version must always be four comma-separated numbers
-set(RCVERSION 1,3,80,0)
+set(RCVERSION 1,4,80,0)
 
 # Installation paths
 set(BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin")
@@ -321,6 +321,12 @@
   set(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h)
 endif()
 check_function_exists(inet_aton HAVE_INET_ATON)
+# This might give a false positive on Windows as it is also guarded by
+# a version check that we do not satisfy (requires Vista, but we target
+# Windows 2000).
+if(NOT WIN32)
+check_function_exists(inet_pton HAVE_INET_PTON)
+endif()
 check_function_exists(getaddrinfo HAVE_GETADDRINFO)
 set(CMAKE_EXTRA_INCLUDE_FILES) 
 set(CMAKE_REQUIRED_LIBRARIES)
diff --git a/common/network/TcpSocket.cxx b/common/network/TcpSocket.cxx
index d9e9376..0db6ff6 100644
--- a/common/network/TcpSocket.cxx
+++ b/common/network/TcpSocket.cxx
@@ -239,27 +239,66 @@
 }
 
 char* TcpSocket::getPeerAddress() {
-  struct sockaddr_in  info;
-  struct in_addr    addr;
-  socklen_t info_size = sizeof(info);
+  vnc_sockaddr_t sa;
+  socklen_t sa_size = sizeof(sa);
 
-  getpeername(getFd(), (struct sockaddr *)&info, &info_size);
-  memcpy(&addr, &info.sin_addr, sizeof(addr));
-
-  char* name = inet_ntoa(addr);
-  if (name) {
-    return rfb::strDup(name);
-  } else {
+  if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) {
+    vlog.error("unable to get peer name for socket");
     return rfb::strDup("");
   }
+
+#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_PTON)
+  if (sa.u.sa.sa_family == AF_INET6) {
+    char buffer[INET6_ADDRSTRLEN + 2];
+    const char *name;
+
+    buffer[0] = '[';
+
+    name = inet_ntop(sa.u.sa.sa_family, &sa.u.sin6.sin6_addr,
+                     buffer + 1, sizeof(buffer) - 2);
+    if (name == NULL) {
+      vlog.error("unable to convert peer name to a string");
+      return rfb::strDup("");
+    }
+
+    strcat(buffer, "]");
+
+    return rfb::strDup(buffer);
+  }
+#endif
+
+  if (sa.u.sa.sa_family == AF_INET) {
+    char *name;
+
+    name = inet_ntoa(sa.u.sin.sin_addr);
+    if (name == NULL) {
+      vlog.error("unable to convert peer name to a string");
+      return rfb::strDup("");
+    }
+
+    return rfb::strDup(name);
+  }
+
+  vlog.error("unknown address family for socket");
+  return rfb::strDup("");
 }
 
 int TcpSocket::getPeerPort() {
-  struct sockaddr_in  info;
-  socklen_t info_size = sizeof(info);
+  vnc_sockaddr_t sa;
+  socklen_t sa_size = sizeof(sa);
 
-  getpeername(getFd(), (struct sockaddr *)&info, &info_size);
-  return ntohs(info.sin_port);
+  getpeername(getFd(), &sa.u.sa, &sa_size);
+
+  switch (sa.u.sa.sa_family) {
+#ifdef HAVE_GETADDRINFO
+  case AF_INET6:
+    return ntohs(sa.u.sin6.sin6_port);
+#endif /* HAVE_GETADDRINFO */
+  case AF_INET:
+    return ntohs(sa.u.sin.sin_port);
+  default:
+    return 0;
+  }
 }
 
 char* TcpSocket::getPeerEndpoint() {
@@ -293,7 +332,11 @@
 				&myaddr.u.sin6.sin6_addr);
 #endif
 
-  return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr);
+  if (peeraddr.u.sa.sa_family == AF_INET)
+    return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr);
+
+  // No idea what this is. Assume we're on different machines.
+  return false;
 }
 
 void TcpSocket::shutdown()
@@ -326,25 +369,34 @@
 
 bool TcpSocket::isSocket(int sock)
 {
-  struct sockaddr_in info;
-  socklen_t info_size = sizeof(info);
-  return getsockname(sock, (struct sockaddr *)&info, &info_size) >= 0;
+  vnc_sockaddr_t sa;
+  socklen_t sa_size = sizeof(sa);
+  return getsockname(sock, &sa.u.sa, &sa_size) >= 0;
 }
 
 bool TcpSocket::isConnected(int sock)
 {
-  struct sockaddr_in info;
-  socklen_t info_size = sizeof(info);
-  return getpeername(sock, (struct sockaddr *)&info, &info_size) >= 0;
+  vnc_sockaddr_t sa;
+  socklen_t sa_size = sizeof(sa);
+  return getpeername(sock, &sa.u.sa, &sa_size) >= 0;
 }
 
 int TcpSocket::getSockPort(int sock)
 {
-  struct sockaddr_in info;
-  socklen_t info_size = sizeof(info);
-  if (getsockname(sock, (struct sockaddr *)&info, &info_size) < 0)
+  vnc_sockaddr_t sa;
+  socklen_t sa_size = sizeof(sa);
+  if (getsockname(sock, &sa.u.sa, &sa_size) < 0)
     return 0;
-  return ntohs(info.sin_port);
+
+  switch (sa.u.sa.sa_family) {
+#ifdef HAVE_GETADDRINFO
+  case AF_INET6:
+    return ntohs(sa.u.sin6.sin6_port);
+#endif /* HAVE_GETADDRINFO */
+
+  default:
+    return ntohs(sa.u.sin.sin_port);
+  }
 }
 
 
@@ -356,9 +408,40 @@
     return;
   }
 
+  bool use_ipv6;
+  int af;
+#ifdef HAVE_GETADDRINFO
+  use_ipv6 = true;
+  af = AF_INET6;
+#else
+  use_ipv6 = false;
+  af = AF_INET;
+#endif
+
   initSockets();
-  if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
-    throw SocketException("unable to create listening socket", errorNumber);
+  if ((fd = socket(af, SOCK_STREAM, 0)) < 0) {
+    // - Socket creation failed
+    if (use_ipv6) {
+      // - Trying to make an IPv6-capable socket failed - try again, IPv4-only
+      use_ipv6 = false;
+      af = AF_INET;
+      fd = socket(af, SOCK_STREAM, 0);
+    }
+    if (fd < 0)
+      throw SocketException("unable to create listening socket", errorNumber);
+  } else {
+    // - Socket creation succeeded
+    if (use_ipv6) {
+#ifdef IPV6_V6ONLY
+      // - We made an IPv6-capable socket, and we need it to do IPv4 too
+      int opt = 0;
+      setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, sizeof(opt));
+#else
+      vlog.error("IPV6_V6ONLY support is missing. "
+		 "IPv4 clients may not be able to connect.");
+#endif
+    }
+  }
 
 #ifndef WIN32
   // - By default, close the socket on exec()
@@ -375,27 +458,62 @@
 
   // - Bind it to the desired port
   struct sockaddr_in addr;
-  memset(&addr, 0, sizeof(addr));
-  addr.sin_family = AF_INET;
-
-  if (localhostOnly) {
-    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-  } else if (listenaddr != NULL) {
-#ifdef HAVE_INET_ATON
-    if (inet_aton(listenaddr, &addr.sin_addr) == 0)
-#else
-    /* Some systems (e.g. Windows) do not have inet_aton, sigh */
-    if ((addr.sin_addr.s_addr = inet_addr(listenaddr)) == INADDR_NONE)
+#ifdef HAVE_GETADDRINFO
+  struct sockaddr_in6 addr6;
 #endif
-    {
-      closesocket(fd);
-      throw Exception("invalid network interface address: %s", listenaddr);
+  struct sockaddr *sa;
+  int sa_len;
+
+#ifdef HAVE_GETADDRINFO
+  if (use_ipv6) {
+    memset(&addr6, 0, (sa_len = sizeof(addr6)));
+    addr6.sin6_family = af;
+    addr6.sin6_port = htons(port);
+
+    if (localhostOnly)
+      addr6.sin6_addr = in6addr_loopback;
+    else if (listenaddr != NULL) {
+#ifdef HAVE_INET_PTON
+      if (inet_pton(AF_INET6, listenaddr, &addr6.sin6_addr) != 1)
+	use_ipv6 = false;
+#else
+      // Unable to parse without inet_pton
+      use_ipv6 = false;
+#endif
     }
-  } else
-    addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Bind to 0.0.0.0 by default. */
+
+    if (use_ipv6)
+      sa = (struct sockaddr *)&addr6;
+  }
+#endif
+
+  if (!use_ipv6) {
+    memset(&addr, 0, (sa_len = sizeof(addr)));
+    addr.sin_family = af;
+    addr.sin_port = htons(port);
+
+    if (localhostOnly) {
+      addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    } else if (listenaddr != NULL) {
+#ifdef HAVE_INET_ATON
+      if (inet_aton(listenaddr, &addr.sin_addr) == 0)
+#else
+	/* Some systems (e.g. Windows) do not have inet_aton, sigh */
+	if ((addr.sin_addr.s_addr = inet_addr(listenaddr)) == INADDR_NONE)
+#endif
+	{
+	  closesocket(fd);
+	  throw Exception("invalid network interface address: %s", listenaddr);
+	}
+    } else
+      /* Bind to 0.0.0.0 by default. */
+      addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+    sa = (struct sockaddr *)&addr;
+  }
 
   addr.sin_port = htons(port);
-  if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+  if (bind(fd, sa, sa_len) < 0) {
     int e = errorNumber;
     closesocket(fd);
     throw SocketException("unable to bind listening socket", e);
@@ -449,6 +567,30 @@
 }
 
 void TcpListener::getMyAddresses(std::list<char*>* result) {
+#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_PTON)
+  vnc_sockaddr_t sa;
+  struct addrinfo *ai, *current, hints;
+
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_canonname = NULL;
+  hints.ai_addr = NULL;
+  hints.ai_next = NULL;
+
+  if ((getaddrinfo(NULL, NULL, &hints, &ai)) != 0)
+    return;
+
+  for (current= ai; current != NULL; current = current->ai_next) {
+    if (current->ai_family != AF_INET && current->ai_family != AF_INET6)
+      continue;
+
+    char *addr = new char[INET6_ADDRSTRLEN];
+    inet_ntop(current->ai_family, current->ai_addr, addr, INET6_ADDRSTRLEN);
+    result->push_back(addr);
+  }
+  freeaddrinfo(ai);
+#else
   const hostent* addrs = gethostbyname(0);
   if (addrs == 0)
     throw rdr::SystemException("gethostbyname", errorNumber);
@@ -460,6 +602,7 @@
     strcpy(addr, addrC);
     result->push_back(addr);
   }
+#endif /* defined(HAVE_GETADDRINFO) && defined(HAVE_INET_PTON) */
 }
 
 int TcpListener::getMyPort() {
@@ -493,6 +636,15 @@
 TcpFilter::verifyConnection(Socket* s) {
   rfb::CharArray name;
 
+#ifdef HAVE_GETADDRINFO
+  vnc_sockaddr_t sa;
+  socklen_t sa_size = sizeof(sa);
+  if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0 ||
+      sa.u.sa.sa_family != AF_INET)
+    /* Matching only works for IPv4 */
+    return false;
+#endif /* HAVE_GETADDRINFO */
+
   name.buf = s->getPeerAddress();
   std::list<TcpFilter::Pattern>::iterator i;
   for (i=filter.begin(); i!=filter.end(); i++) {
diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx
index 1695c3a..e0a23b5 100644
--- a/common/rfb/CConnection.cxx
+++ b/common/rfb/CConnection.cxx
@@ -181,7 +181,7 @@
     if (secType != secTypeInvalid) {
       os->writeU8(secType);
       os->flush();
-      vlog.debug("Choosing security type %s(%d)",secTypeName(secType),secType);
+      vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType);
     }
   }
 
diff --git a/common/rfb/CSecurityVeNCrypt.cxx b/common/rfb/CSecurityVeNCrypt.cxx
index 77eeef9..a15da4a 100644
--- a/common/rfb/CSecurityVeNCrypt.cxx
+++ b/common/rfb/CSecurityVeNCrypt.cxx
@@ -164,7 +164,7 @@
 	  break;
       }
 
-      vlog.debug("Choosing security type %s (%d)", secTypeName(chosenType),
+      vlog.info("Choosing security type %s (%d)", secTypeName(chosenType),
 		 chosenType);
 
       /* Set up the stack according to the chosen type: */
diff --git a/common/rfb/SSecurityVeNCrypt.cxx b/common/rfb/SSecurityVeNCrypt.cxx
index 9e4260f..39647f6 100644
--- a/common/rfb/SSecurityVeNCrypt.cxx
+++ b/common/rfb/SSecurityVeNCrypt.cxx
@@ -157,7 +157,7 @@
     if (!haveChosenType)
       chosenType = secTypeInvalid;
 
-    vlog.debug("Choosing security type %s (%d)", secTypeName(chosenType),
+    vlog.info("Client requests security type %s (%d)", secTypeName(chosenType),
 	       chosenType);
 
     /* Set up the stack according to the chosen type */
diff --git a/common/rfb/SecurityServer.cxx b/common/rfb/SecurityServer.cxx
index 5bd7b2c..e0aee13 100644
--- a/common/rfb/SecurityServer.cxx
+++ b/common/rfb/SecurityServer.cxx
@@ -43,7 +43,7 @@
 #endif
  ")",
 #ifdef HAVE_GNUTLS
- "VncAuth,TLSVnc",
+ "TLSVnc,VncAuth",
 #else
  "VncAuth",
 #endif
diff --git a/config.h.in b/config.h.in
index a50e723..490d7f6 100644
--- a/config.h.in
+++ b/config.h.in
@@ -2,6 +2,7 @@
 #define PACKAGE_VERSION "@VERSION@"
 
 #cmakedefine HAVE_INET_ATON
+#cmakedefine HAVE_INET_PTON
 #cmakedefine HAVE_GETADDRINFO
 #cmakedefine HAVE_GNUTLS_SET_GLOBAL_ERRNO
 #cmakedefine HAVE_GNUTLS_SET_ERRNO
diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt
index c98c69c..bf1cffb 100644
--- a/java/CMakeLists.txt
+++ b/java/CMakeLists.txt
@@ -2,7 +2,7 @@
 
 project(tigervnc-java Java)
 if(NOT VERSION)
-  set(VERSION 1.3.80)
+  set(VERSION 1.4.80)
 endif()
 
 find_package(Java)
diff --git a/java/com/tigervnc/network/SSLEngineManager.java b/java/com/tigervnc/network/SSLEngineManager.java
index cb1f7c4..c011099 100644
--- a/java/com/tigervnc/network/SSLEngineManager.java
+++ b/java/com/tigervnc/network/SSLEngineManager.java
@@ -41,14 +41,14 @@
   private ByteBuffer peerNetData;
 
   private Executor executor;
-  private FdInStream inStream;
-  private FdOutStream outStream;
+  private FdInStream in;
+  private FdOutStream os;
 
-  public SSLEngineManager(SSLEngine sslEngine, FdInStream is,
-                          FdOutStream os) throws IOException {
+  public SSLEngineManager(SSLEngine sslEngine, FdInStream is_,
+                          FdOutStream os_) throws IOException {
 
-    inStream = is;
-    outStream = os;
+    in = is_;
+    os = os_;
     engine = sslEngine;
 
     executor = Executors.newSingleThreadExecutor();
@@ -56,7 +56,8 @@
     pktBufSize = engine.getSession().getPacketBufferSize();
     appBufSize = engine.getSession().getApplicationBufferSize();
 
-    myAppData = ByteBuffer.allocate(appBufSize);
+    myAppData =
+      ByteBuffer.allocate(Math.max(appBufSize, os.getBufSize()));
     myNetData = ByteBuffer.allocate(pktBufSize);
     peerAppData = ByteBuffer.allocate(appBufSize);
     peerNetData = ByteBuffer.allocate(pktBufSize);
@@ -80,13 +81,14 @@
         SSLEngineResult res = engine.unwrap(peerNetData, peerAppData);
         peerNetData.compact();
         hs = res.getHandshakeStatus();
+        // Check status
         switch (res.getStatus()) {
           case BUFFER_UNDERFLOW:
-            int len = Math.min(peerNetData.remaining(), inStream.getBufSize());
-            int m = inStream.check(1, len, false);
-            byte[] buf = new byte[m];
-            inStream.readBytes(buf, 0, m);
-            peerNetData.put(buf, 0, m);
+            int max = Math.min(peerNetData.remaining(), in.getBufSize());
+            int m = in.check(1, max, true);
+            int pos = peerNetData.position();
+            in.readBytes(peerNetData.array(), pos, m);
+            peerNetData.position(pos+m);
             peerNetData.flip();
             peerNetData.compact();
             break;
@@ -102,7 +104,7 @@
         }
         break;
     
-      case NEED_WRAP :
+      case NEED_WRAP:
         // Empty the local network packet buffer.
         myNetData.clear();
     
@@ -112,20 +114,17 @@
     
         // Check status
         switch (res.getStatus()) {
-          case OK :
+          case OK:
             myAppData.compact();
             myNetData.flip();
-            int n = myNetData.remaining();
-            byte[] b = new byte[n];
-            myNetData.get(b);
+            os.writeBytes(myNetData.array(), 0, myNetData.remaining());
+            os.flush();
             myNetData.clear();
-            outStream.writeBytes(b, 0, n);
-            outStream.flush();
             break;
     
           case BUFFER_OVERFLOW:
             // FIXME: How much larger should the buffer be?
-            // fallthrough
+            break;
     
           case CLOSED:
             engine.closeOutbound();
@@ -133,7 +132,8 @@
     
         }
         break;
-      case NEED_TASK :
+
+      case NEED_TASK:
         // Handle blocking tasks
         executeTasks();
         break;
@@ -151,29 +151,28 @@
 
   public int read(byte[] data, int dataPtr, int length) throws IOException {
     // Read SSL/TLS encoded data from peer
-    int len = Math.min(pktBufSize,inStream.getBufSize());
-    int bytesRead = inStream.check(1,len,false);
-    byte[] buf = new byte[bytesRead];
-    inStream.readBytes(buf, 0, bytesRead);
-    if (peerNetData.remaining() < bytesRead) {
-      peerNetData.flip();
-      ByteBuffer b = ByteBuffer.allocate(peerNetData.remaining() + bytesRead);
-      b.put(peerNetData);
-      peerNetData = b;
-    }
-    peerNetData.put(buf);
+    int bytesRead = 0;
     peerNetData.flip();
     SSLEngineResult res = engine.unwrap(peerNetData, peerAppData);
     peerNetData.compact();
     switch (res.getStatus()) {
       case OK :
+        bytesRead = Math.min(length, res.bytesProduced());
         peerAppData.flip();
-        peerAppData.get(data, dataPtr, res.bytesProduced());
+        peerAppData.get(data, dataPtr, bytesRead);
         peerAppData.compact();
         break;
 
       case BUFFER_UNDERFLOW:
-        // normal (need more net data)
+        // need more net data
+        int pos = peerNetData.position();
+        // attempt to drain the underlying buffer first
+        int need = peerNetData.remaining();
+        int avail = in.check(1, in.getBufSize(), false);
+        if (avail < need)
+          avail = in.check(1, Math.min(need, in.getBufSize()), true);
+        in.readBytes(peerNetData.array(), pos, Math.min(need, avail));
+        peerNetData.position(pos+Math.min(need, avail));
         break;
 
       case CLOSED:
@@ -181,24 +180,28 @@
         break;
 
     }
-    return res.bytesProduced();
+    return bytesRead;
   }
 
   public int write(byte[] data, int dataPtr, int length) throws IOException {
     int n = 0;
-    // FIXME: resize myAppData if necessary
     myAppData.put(data, dataPtr, length);
     myAppData.flip();
     while (myAppData.hasRemaining()) {
       SSLEngineResult res = engine.wrap(myAppData, myNetData);
       n += res.bytesConsumed();
       switch (res.getStatus()) {
-        case BUFFER_OVERFLOW:
-          ByteBuffer b = ByteBuffer.allocate(myNetData.capacity() + myAppData.remaining());
-          myNetData.flip();
-          b.put(myNetData);
-          myNetData = b;
+        case OK:
           break;
+
+        case BUFFER_OVERFLOW:
+          // Make room in the buffer by flushing the outstream
+          myNetData.flip();
+          os.writeBytes(myNetData.array(), 0, myNetData.remaining());
+          os.flush();
+          myNetData.clear();
+          break;
+
         case CLOSED:
           engine.closeOutbound();
           break;
@@ -206,12 +209,9 @@
     }
     myAppData.clear();
     myNetData.flip();
-    int len = myNetData.remaining();
-    byte[] buf = new byte[len];
-    myNetData.get(buf);
+    os.writeBytes(myNetData.array(), 0, myNetData.remaining());
+    os.flush();
     myNetData.clear();
-    outStream.writeBytes(buf, 0, len);
-    outStream.flush();
     return n;
   }
 
diff --git a/java/com/tigervnc/network/Socket.java b/java/com/tigervnc/network/Socket.java
index bcc920d..651dc05 100644
--- a/java/com/tigervnc/network/Socket.java
+++ b/java/com/tigervnc/network/Socket.java
@@ -58,7 +58,6 @@
   // Is the remote end on the same machine?
   abstract public boolean sameMachine();
 
-  // Was there a "?" in the ConnectionFilter used to accept this Socket?
   public void setRequiresQuery() {queryConnection = true;}
   public final boolean requiresQuery() {return queryConnection;}
 
@@ -78,29 +77,3 @@
   boolean isShutdown_;
   boolean queryConnection;
 }
-
-/*
-abstract class ConnectionFilter {
-  public abstract boolean verifyConnection(Socket s);
-};
-
-abstract class SocketListener {
-  public SocketListener() {
-    fd = null; filter = null;
-  }
-
-  // shutdown() stops the socket from accepting further connections
-  public abstract void shutdown();
-
-  // accept() returns a new Socket object if there is a connection
-  // attempt in progress AND if the connection passes the filter
-  // if one is installed.  Otherwise, returns 0.
-  public abstract Socket accept();
-
-  // setFilter() applies the specified filter to all new connections
-  public void setFilter(ConnectionFilter f) {filter = f;}
-  //public SocketDescriptor getFd() {return fd;}
-  protected FileDescriptor fd;
-  protected ConnectionFilter filter;
-};
-*/
diff --git a/java/com/tigervnc/network/SocketListener.java b/java/com/tigervnc/network/SocketListener.java
index a1b2c12..a1f5ea1 100644
--- a/java/com/tigervnc/network/SocketListener.java
+++ b/java/com/tigervnc/network/SocketListener.java
@@ -29,18 +29,15 @@
   public SocketListener() {}
 
   // shutdown() stops the socket from accepting further connections
-  abstract public void shutdown();
+  abstract public void shutdown() throws Exception;
 
   // accept() returns a new Socket object if there is a connection
   // attempt in progress AND if the connection passes the filter
   // if one is installed.  Otherwise, returns 0.
   abstract public Socket accept();
 
-  // setFilter() applies the specified filter to all new connections
-  //public void setFilter(ConnectionFilter* f) {filter = f;}
   public FileDescriptor getFd() {return fd;}
 
   protected FileDescriptor fd;
-  //protected ConnectionFilter* filter;
 
 }
diff --git a/java/com/tigervnc/network/TcpListener.java b/java/com/tigervnc/network/TcpListener.java
index bab9e4c..45ee8a4 100644
--- a/java/com/tigervnc/network/TcpListener.java
+++ b/java/com/tigervnc/network/TcpListener.java
@@ -84,12 +84,21 @@
     this(listenaddr, port, false, null, true);
   }
 
-//  TcpListener::~TcpListener() {
-//    if (closeFd) closesocket(fd);
-//  }
+  protected void finalize() throws Exception {
+    if (closeFd)
+      try {
+        ((SocketDescriptor)getFd()).close();
+      } catch (IOException e) {
+        throw new Exception(e.getMessage());
+      }
+  }
 
-  public void shutdown() {
-    //shutdown(getFd(), 2);
+  public void shutdown() throws Exception {
+    try {
+      ((SocketDescriptor)getFd()).shutdown();
+    } catch (IOException e) {
+      throw new Exception(e.getMessage());
+    }
   }
 
   public TcpSocket accept() {
@@ -132,32 +141,12 @@
     }
     fd.setChannel(new_sock);
     TcpSocket s = new TcpSocket(fd);
-    //if (filter && !filter->verifyConnection(s)) {
-    //  delete s;
-    //  return 0;
-    //}
     return s;
   }
 
-/*
-void TcpListener::getMyAddresses(std::list<char*>* result) {
-  const hostent* addrs = gethostbyname(0);
-  if (addrs == 0)
-    throw rdr::SystemException("gethostbyname", errorNumber);
-  if (addrs->h_addrtype != AF_INET)
-    throw rdr::Exception("getMyAddresses: bad family");
-  for (int i=0; addrs->h_addr_list[i] != 0; i++) {
-    const char* addrC = inet_ntoa(*((struct in_addr*)addrs->h_addr_list[i]));
-    char* addr = new char[strlen(addrC)+1];
-    strcpy(addr, addrC);
-    result->push_back(addr);
+  public int getMyPort() {
+    return ((SocketDescriptor)getFd()).socket().getLocalPort();
   }
-}
-  */
-
-  //public int getMyPort() {
-  //  return TcpSocket.getSockPort();
-  //}
 
   private boolean closeFd;
   private ServerSocketChannel channel;
diff --git a/java/com/tigervnc/network/TcpSocket.java b/java/com/tigervnc/network/TcpSocket.java
index 051a9d8..43787e4 100644
--- a/java/com/tigervnc/network/TcpSocket.java
+++ b/java/com/tigervnc/network/TcpSocket.java
@@ -113,8 +113,7 @@
   }
 
   public int getMyPort() {
-    SocketAddress address = ((SocketDescriptor)getFd()).socket().getLocalSocketAddress();
-    return ((InetSocketAddress)address).getPort();
+    return getSockPort();
   }
 
   public String getPeerAddress() {
@@ -184,7 +183,7 @@
   }
 
   public int getSockPort() {
-    return ((InetSocketAddress)((SocketDescriptor)getFd()).socket().getRemoteSocketAddress()).getPort();
+    return ((SocketDescriptor)getFd()).socket().getLocalPort();
   }
 
   /* Tunnelling support. */
diff --git a/java/com/tigervnc/vncviewer/ClipboardDialog.java b/java/com/tigervnc/vncviewer/ClipboardDialog.java
index d4cde6e..e7aa7e9 100644
--- a/java/com/tigervnc/vncviewer/ClipboardDialog.java
+++ b/java/com/tigervnc/vncviewer/ClipboardDialog.java
@@ -1,5 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * Copyright (C) 2011 Brian P. Hinz
+ * Copyright (C) 2011-2014 Brian P. Hinz
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,27 +21,91 @@
 
 import java.awt.*;
 import java.awt.event.*;
-import java.awt.datatransfer.Clipboard;
-import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.*;
+import java.io.*;
+import java.nio.*;
 import javax.swing.*;
 import javax.swing.border.*;
+import javax.swing.text.*;
+
 import com.tigervnc.rfb.LogWriter;
 
 class ClipboardDialog extends Dialog implements ActionListener {
 
+  private class VncTransferHandler extends TransferHandler {
+    // Custom TransferHandler designed to limit the size of outbound
+    // clipboard transfers to VncViewer.maxCutText.getValue() bytes.
+    private LogWriter vlog = new LogWriter("VncTransferHandler");
+
+    public void exportToClipboard(JComponent c, Clipboard clip, int a)
+        throws IllegalStateException {
+      if (!(c instanceof JTextComponent)) return;
+      StringSelection selection =
+        new StringSelection(((JTextComponent)c).getText());
+      clip.setContents(selection, null);
+    }
+
+    public boolean importData(JComponent c, Transferable t) {
+      if (canImport(c, t.getTransferDataFlavors())) {
+        try {
+          DataFlavor VncFlavor = null;
+          for (DataFlavor f : t.getTransferDataFlavors())
+            if (f.isFlavorTextType() && f.isRepresentationClassInputStream())
+              VncFlavor = f;
+          if (VncFlavor == null) return false;
+          Reader reader = (Reader)VncFlavor.getReaderForText(t);
+          CharBuffer cbuf =
+            CharBuffer.allocate(VncViewer.maxCutText.getValue());
+          cbuf.limit(reader.read(cbuf.array(), 0, cbuf.length()));
+          reader.close();
+          if (c instanceof JTextComponent)
+            ((JTextComponent)c).setText(cbuf.toString());
+          return true;
+        } catch (OutOfMemoryError oome) {
+          vlog.error("ERROR: Too much data on local clipboard!");
+        } catch (UnsupportedFlavorException ufe) {
+          // Skip import
+          vlog.info(ufe.toString());
+        } catch (IOException ioe) {
+          // Skip import
+          vlog.info(ioe.toString());
+        }
+      }
+      return false;
+    }
+
+    public boolean canImport(JComponent c, DataFlavor[] flavors) {
+      for (DataFlavor f : flavors)
+        if (f.isFlavorTextType() && f.isRepresentationClassReader())
+          return true;
+      return false;
+    }
+  }
+
   public ClipboardDialog(CConn cc_) {
     super(false);
+    setTitle("VNC Clipboard Viewer");
+    setPreferredSize(new Dimension(640, 480));
+    addWindowFocusListener(new WindowAdapter() {
+      // Necessary to ensure that updates from the system clipboard
+      // still occur when the ClipboardDialog has the focus.
+      public void WindowGainedFocus(WindowEvent e) {
+        clientCutText();
+      }
+    });
     cc = cc_;
-    setTitle("VNC clipboard");
-    JPanel pt = new JPanel();
-    textArea = new JTextArea(5,50);
-    textArea.setBorder(BorderFactory.createLineBorder(Color.gray));
-    textArea.setLineWrap(true);
+    textArea = new JTextArea();
+    textArea.setTransferHandler(new VncTransferHandler());
+    // If the textArea can receive the focus, then text within the textArea
+    // can be selected.  On platforms that don't support separate selection
+    // and clipboard buffers, this triggers a replacement of the textAra's
+    // contents with the selected text.
+    textArea.setFocusable(false);
+    textArea.setLineWrap(false);
     textArea.setWrapStyleWord(true);
     JScrollPane sp = new JScrollPane(textArea);
-    pt.add(sp, BorderLayout.CENTER);
-    getContentPane().add("North", pt);
-
+    getContentPane().add(sp, BorderLayout.CENTER);
+    // button panel placed below the scrollpane
     JPanel pb = new JPanel();
     clearButton = new JButton("Clear");
     pb.add(clearButton);
@@ -53,40 +117,22 @@
     pb.add(cancelButton);
     cancelButton.addActionListener(this);
     getContentPane().add("South", pb);
-
     pack();
   }
 
-  public boolean compareContentsTo(String str) {
-    return str.equals(textArea.getText());
-
-  }
-
-  public void setContents(String str) {
-    textArea.setText(str);
-  }
-
-  public String getContents() {
-    return textArea.getText();
-  }
-
   public void serverCutText(String str, int len) {
-    setContents(str);
-    SecurityManager sm = System.getSecurityManager();
-    try {
-      if (sm != null) sm.checkSystemClipboardAccess();
-      Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
-      if (cb != null) {
-        StringSelection ss = new StringSelection(str);
-        try {
-          cb.setContents(ss, null);
-        } catch(Exception e) {
-          vlog.debug(e.getMessage());
-        }
-      }
-    } catch(SecurityException e) {
-      vlog.debug("Cannot access the system clipboard: "+e.getMessage());
-    }
+    textArea.setText(str);
+    textArea.copy();
+  }
+
+  public void clientCutText() {
+    int hc = textArea.getText().hashCode();
+    textArea.paste();
+    textArea.setCaretPosition(0);
+    String text = textArea.getText();
+    if (cc.viewer.sendClipboard.getValue())
+      if (hc != text.hashCode())
+        cc.writeClientCutText(text, text.length());
   }
 
   public void setSendingEnabled(boolean b) {
@@ -98,7 +144,8 @@
     if (s instanceof JButton && (JButton)s == clearButton) {
       serverCutText(new String(""), 0);
     } else if (s instanceof JButton && (JButton)s == sendButton) {
-      cc.writeClientCutText(textArea.getText(), textArea.getText().length());
+      String text = textArea.getText();
+      cc.writeClientCutText(text, text.length());
       endDialog();
     } else if (s instanceof JButton && (JButton)s == cancelButton) {
       endDialog();
diff --git a/java/com/tigervnc/vncviewer/DesktopWindow.java b/java/com/tigervnc/vncviewer/DesktopWindow.java
index e78ee27..10d158c 100644
--- a/java/com/tigervnc/vncviewer/DesktopWindow.java
+++ b/java/com/tigervnc/vncviewer/DesktopWindow.java
@@ -85,7 +85,7 @@
     addKeyListener(this);
     addFocusListener(new FocusAdapter() {
       public void focusGained(FocusEvent e) {
-        checkClipboard();
+        cc.clipboardDialog.clientCutText();
       }
       public void focusLost(FocusEvent e) {
         cc.releaseDownKeys();
@@ -359,36 +359,6 @@
     g2.dispose();
   }
 
-  public synchronized void checkClipboard() {
-    SecurityManager sm = System.getSecurityManager();
-    try {
-      if (sm != null) sm.checkSystemClipboardAccess();
-      Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
-      if (cb != null) {
-        Transferable t = cb.getContents(null);
-        if (t == null) return;
-        DataFlavor flavor = 
-          DataFlavor.selectBestTextFlavor(t.getTransferDataFlavors());
-        if (flavor == null) return;
-        BufferedReader br = new BufferedReader(flavor.getReaderForText(t));
-        CharBuffer cbuf =
-          CharBuffer.allocate(VncViewer.maxCutText.getValue());
-        br.read(cbuf);
-        cbuf.flip();
-        String newContents = cbuf.toString();
-        if (!cc.clipboardDialog.compareContentsTo(newContents)) {
-          cc.clipboardDialog.setContents(newContents);
-          if (cc.viewer.sendClipboard.getValue())
-            cc.writeClientCutText(newContents, newContents.length());
-        }
-        br.close();
-        System.gc();
-      }
-    } catch(java.lang.Exception e) {
-      vlog.debug("Exception getting clipboard data: " + e.getMessage());
-    }
-  }
-
   // Mouse-Motion callback function
   private void mouseMotionCB(MouseEvent e) {
     if (!cc.viewer.viewOnly.getValue() &&
diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java
index 0df39c8..1e0f223 100644
--- a/java/com/tigervnc/vncviewer/VncViewer.java
+++ b/java/com/tigervnc/vncviewer/VncViewer.java
@@ -457,7 +457,7 @@
       while (true) {
         Socket new_sock = listener.accept();
         if (new_sock != null)
-          newViewer(this, new_sock);
+          newViewer(this, new_sock, true);
       }
     }
 
diff --git a/po/tigervnc.pot b/po/tigervnc.pot
index 5df3639..5a91804 100644
--- a/po/tigervnc.pot
+++ b/po/tigervnc.pot
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: tigervnc-devel@googlegroups.com\n"
-"POT-Creation-Date: 2014-09-22 11:15+0000\n"
+"POT-Creation-Date: 2014-11-19 12:46+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -30,7 +30,7 @@
 msgid "About TigerVNC Viewer"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1086
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1097
 msgid "About TigerVNC viewer..."
 msgstr ""
 
@@ -50,7 +50,7 @@
 msgid "Allow JPEG compression:"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1070
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1081
 msgid "Alt"
 msgstr ""
 
@@ -71,7 +71,7 @@
 msgid "Bad Name/Value pair on line: %d in file: %s"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Win32PixelBuffer.cxx:92
+#: /home/ossman/devel/tigervnc/vncviewer/Win32PixelBuffer.cxx:91
 msgid "BitBlt failed"
 msgstr ""
 
@@ -101,7 +101,7 @@
 msgid "Connect"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1085
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1096
 msgid "Connection info..."
 msgstr ""
 
@@ -144,6 +144,18 @@
 msgid "Could not create VNC home directory: can't obtain home directory path."
 msgstr ""
 
+#: /home/ossman/devel/tigervnc/vncviewer/OSXPixelBuffer.cxx:58
+msgid "Could not create framebuffer bitmap"
+msgstr ""
+
+#: /home/ossman/devel/tigervnc/vncviewer/OSXPixelBuffer.cxx:52
+msgid "Could not create framebuffer device"
+msgstr ""
+
+#: /home/ossman/devel/tigervnc/vncviewer/X11PixelBuffer.cxx:107
+msgid "Could not create framebuffer image"
+msgstr ""
+
 #: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:310
 #, c-format
 msgid "Could not encode the parameter-value %s when writing to the Registry."
@@ -156,11 +168,15 @@
 "small."
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Win32PixelBuffer.cxx:80
+#: /home/ossman/devel/tigervnc/vncviewer/X11PixelBuffer.cxx:68
+msgid "Couldn't find suitable pixmap format"
+msgstr ""
+
+#: /home/ossman/devel/tigervnc/vncviewer/Win32PixelBuffer.cxx:79
 msgid "CreateCompatibleDC failed"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1068
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1079
 msgid "Ctrl"
 msgstr ""
 
@@ -179,10 +195,14 @@
 msgid "Desktop name: %.80s"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1088
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1099
 msgid "Dismiss menu"
 msgstr ""
 
+#: /home/ossman/devel/tigervnc/vncviewer/X11PixelBuffer.cxx:59
+msgid "Display lacks pixmap format for default depth"
+msgstr ""
+
 #: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:778
 msgid "Enable full-screen mode over all monitors"
 msgstr ""
@@ -252,24 +272,7 @@
 msgid "Error(%d) writing %s(REG_SZ) to Registry."
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/OSXPixelBuffer.cxx:50
-#: /home/ossman/devel/tigervnc/vncviewer/FLTKPixelBuffer.cxx:33
-msgid "Error: Not enough memory for framebuffer"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/X11PixelBuffer.cxx:69
-msgid "Error: couldn't find suitable pixmap format"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/X11PixelBuffer.cxx:60
-msgid "Error: display lacks pixmap format for default depth"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/X11PixelBuffer.cxx:78
-msgid "Error: only true colour displays supported"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1056
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1067
 msgid "Exit viewer"
 msgstr ""
 
@@ -310,7 +313,7 @@
 msgid "Full (all available colors)"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1059
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1070
 msgid "Full screen"
 msgstr ""
 
@@ -352,7 +355,7 @@
 msgid "Invalid parameter name on line: %d in file: %s"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/DesktopWindow.cxx:797
+#: /home/ossman/devel/tigervnc/vncviewer/DesktopWindow.cxx:805
 msgid "Invalid screen layout computed for resize request!"
 msgstr ""
 
@@ -399,7 +402,7 @@
 msgid "Misc."
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1026
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1037
 #, c-format
 msgid "Multiple characters given for key code %d (0x%04x): '%s'"
 msgstr ""
@@ -408,32 +411,32 @@
 msgid "No"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:688
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:691
 #, c-format
 msgid "No scan code for extended virtual key 0x%02x"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:690
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:693
 #, c-format
 msgid "No scan code for virtual key 0x%02x"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:701
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:710
 #, c-format
 msgid "No symbol for extended virtual key 0x%02x"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:765
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:776
 #, c-format
 msgid "No symbol for key code %d (in the current state)"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:739
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:750
 #, c-format
 msgid "No symbol for key code 0x%02x (in the current state)"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:703
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:712
 #, c-format
 msgid "No symbol for virtual key 0x%02x"
 msgstr ""
@@ -444,16 +447,26 @@
 msgid "None"
 msgstr ""
 
+#: /home/ossman/devel/tigervnc/vncviewer/OSXPixelBuffer.cxx:48
+#: /home/ossman/devel/tigervnc/vncviewer/FLTKPixelBuffer.cxx:33
+#: /home/ossman/devel/tigervnc/vncviewer/X11PixelBuffer.cxx:111
+msgid "Not enough memory for framebuffer"
+msgstr ""
+
 #: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:220
 #: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:88
 msgid "OK"
 msgstr ""
 
+#: /home/ossman/devel/tigervnc/vncviewer/X11PixelBuffer.cxx:77
+msgid "Only true colour displays supported"
+msgstr ""
+
 #: /home/ossman/devel/tigervnc/vncviewer/UserDialog.cxx:74
 msgid "Opening password file failed"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1084
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1095
 #: /home/ossman/devel/tigervnc/vncviewer/ServerDialog.cxx:64
 msgid "Options..."
 msgstr ""
@@ -498,7 +511,7 @@
 msgid "Quit"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1082
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1093
 msgid "Refresh screen"
 msgstr ""
 
@@ -515,7 +528,7 @@
 msgid "Resize remote session to the local window"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1062
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1073
 msgid "Resize window to session"
 msgstr ""
 
@@ -536,16 +549,16 @@
 msgid "Security method: %s"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Win32PixelBuffer.cxx:83
+#: /home/ossman/devel/tigervnc/vncviewer/Win32PixelBuffer.cxx:82
 msgid "SelectObject failed"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1075
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1086
 #, c-format
 msgid "Send %s"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1080
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1091
 msgid "Send Ctrl-Alt-Del"
 msgstr ""
 
@@ -632,12 +645,17 @@
 "See http://www.tigervnc.org for information on TigerVNC."
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1004
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:456
+#, c-format
+msgid "Unable to create platform specific framebuffer: %s"
+msgstr ""
+
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1015
 #, c-format
 msgid "Unknown FLTK key code %d (0x%04x)"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:878
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:889
 #, c-format
 msgid "Unknown decimal separator: '%s'"
 msgstr ""
@@ -671,7 +689,7 @@
 msgid "Using %s encoding"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/X11PixelBuffer.cxx:80
+#: /home/ossman/devel/tigervnc/vncviewer/X11PixelBuffer.cxx:79
 #, c-format
 msgid "Using default colormap and visual, %sdepth %d."
 msgstr ""
@@ -681,6 +699,10 @@
 msgid "Using pixel format %s"
 msgstr ""
 
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:457
+msgid "Using platform independent framebuffer"
+msgstr ""
+
 #: /home/ossman/devel/tigervnc/vncviewer/ServerDialog.cxx:42
 msgid "VNC Viewer: Connection Details"
 msgstr ""
@@ -694,7 +716,7 @@
 msgid "VNC authentication"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1176
+#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1187
 msgid "VNC connection info"
 msgstr ""
 
@@ -736,6 +758,6 @@
 msgid "quality (0=poor, 9=best)"
 msgstr ""
 
-#: /home/ossman/devel/tigervnc/vncviewer/Win32PixelBuffer.cxx:63
+#: /home/ossman/devel/tigervnc/vncviewer/Win32PixelBuffer.cxx:62
 msgid "unable to create DIB section"
 msgstr ""
diff --git a/unix/xserver/hw/vnc/xvnc.cc b/unix/xserver/hw/vnc/xvnc.cc
index 2ef888d..0fd7b0c 100644
--- a/unix/xserver/hw/vnc/xvnc.cc
+++ b/unix/xserver/hw/vnc/xvnc.cc
@@ -98,7 +98,7 @@
 #define Xfree free
 #endif
 
-#define XVNCVERSION "TigerVNC 1.3.80"
+#define XVNCVERSION "TigerVNC 1.4.80"
 #define XVNCCOPYRIGHT ("Copyright (C) 1999-2013 TigerVNC Team and many others (see README.txt)\n" \
                        "See http://www.tigervnc.org for information on TigerVNC.\n")
 
diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx
index 3579618..423d2df 100644
--- a/vncviewer/DesktopWindow.cxx
+++ b/vncviewer/DesktopWindow.cxx
@@ -658,12 +658,20 @@
 
 void DesktopWindow::handleDesktopSize()
 {
-  int width, height;
+  if (desktopSize.hasBeenSet()) {
+    int width, height;
 
-  if (sscanf(desktopSize.getValueStr(), "%dx%d", &width, &height) != 2)
-    return;
+    // An explicit size has been requested
 
-  remoteResize(width, height);
+    if (sscanf(desktopSize.getValueStr(), "%dx%d", &width, &height) != 2)
+      return;
+
+    remoteResize(width, height);
+  } else if (::remoteResize) {
+    // No explicit size, but remote resizing is on so make sure it
+    // matches whatever size the window ended up being
+    remoteResize(w(), h());
+  }
 }
 
 
diff --git a/vncviewer/FLTKPixelBuffer.cxx b/vncviewer/FLTKPixelBuffer.cxx
index 1eafe10..ab116d1 100644
--- a/vncviewer/FLTKPixelBuffer.cxx
+++ b/vncviewer/FLTKPixelBuffer.cxx
@@ -30,7 +30,7 @@
 {
   data = new rdr::U8[width * height * format.bpp/8];
   if (data == NULL)
-    throw rfb::Exception(_("Error: Not enough memory for framebuffer"));
+    throw rfb::Exception(_("Not enough memory for framebuffer"));
 }
 
 FLTKPixelBuffer::~FLTKPixelBuffer()