Merge branch 'ipv6' of https://github.com/CendioOssman/tigervnc
diff --git a/BUILDING.txt b/BUILDING.txt
index 8f686cc..41f493e 100644
--- a/BUILDING.txt
+++ b/BUILDING.txt
@@ -15,6 +15,7 @@
 
 -- If building TLS support:
    * GnuTLS
+   * See "Building TLS Support" below.
 
 -- If building native language support (NLS):
    * Gnu gettext 0.14.4 or later
@@ -322,6 +323,26 @@
 
 
 ======================================
+Building TLS Support
+======================================
+
+TLS requires GnuTLS, which is supplied with most Linux distributions and
+with MinGW for Windows and can be built from source on OS X and other 
+Unix variants. However, GnuTLS versions > 2.12.x && < 3.3.x should be
+avoided because of potential incompatibilities during initial handshaking.
+
+You can override the GNUTLS_LIBRARY and GNUTLS_INCLUDE_DIR CMake variables
+to specify the locations of libgnutls and any dependencies.  For instance,
+adding
+
+  -DGNUTLS_INCLUDE_DIR=/usr/local/include \
+  -DGNUTLS_LIBRARY=/usr/local/lib/libgnutls.a
+
+to the CMake command line would link TigerVNC against a static version of
+libgnutls located under /usr/local.
+
+
+======================================
 Building Native Language Support (NLS)
 ======================================
 
diff --git a/java/com/tigervnc/vncviewer/ClipboardDialog.java b/java/com/tigervnc/vncviewer/ClipboardDialog.java
index d4cde6e..fff7dc3 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,83 @@
 
 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 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 +109,24 @@
     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.selectAll();
+    textArea.copy();
+  }
+
+  public void clientCutText() {
+    int hc = textArea.getText().hashCode();
+    textArea.setText("");
+    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 +138,9 @@
     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();
+      if (cc.viewer.sendClipboard.getValue())
+        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() &&