Merge branch 'allowoverride' of https://github.com/michalsrb/tigervnc
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 773556a..cd5a23b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,6 +3,9 @@
 #
 
 cmake_minimum_required(VERSION 2.8)
+if(POLICY CMP0022)
+  cmake_policy(SET CMP0022 OLD)
+endif()
 
 # Internal cmake modules
 set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules)
@@ -146,18 +149,22 @@
     find_path(GETTEXT_INCLUDE_DIR libintl.h)
     if(GETTEXT_INCLUDE_DIR)
       set(CMAKE_REQUIRED_LIBRARIES ${ICONV_LIBRARIES})
+      set(CMAKE_REQUIRED_FLAGS -fno-builtin-dgettext)
       check_function_exists(dgettext LIBC_HAS_DGETTEXT)
       if(LIBC_HAS_DGETTEXT)
         set(GETTEXT_FOUND TRUE)
       else()
         find_library(LIBINTL_LIBRARY NAMES intl libintl)
-        check_library_exists(${LIBINTL_LIBRARY} "dgettext" "" LIBINTL_HAS_DGETTEXT)
-        if(LIBINTL_HAS_DGETTEXT)
-          set(GETTEXT_LIBRARIES ${LIBINTL_LIBRARY} ${ICONV_LIBRARIES})
-          set(GETTEXT_FOUND TRUE)
+        if(LIBINTL_LIBRARY)
+          check_library_exists(${LIBINTL_LIBRARY} "dgettext" "" LIBINTL_HAS_DGETTEXT)
+          if(LIBINTL_HAS_DGETTEXT)
+            set(GETTEXT_LIBRARIES ${LIBINTL_LIBRARY} ${ICONV_LIBRARIES})
+            set(GETTEXT_FOUND TRUE)
+          endif()
         endif()
       endif()
       set(CMAKE_REQUIRED_LIBRARIES)
+      set(CMAKE_REQUIRED_FLAGS)
     endif()
   endif()
 
diff --git a/contrib/packages/rpm/el6/SOURCES/nettle-2.7.1-ecc-cve.patch b/contrib/packages/rpm/el6/SOURCES/nettle-2.7.1-ecc-cve.patch
new file mode 100644
index 0000000..130f2fc
--- /dev/null
+++ b/contrib/packages/rpm/el6/SOURCES/nettle-2.7.1-ecc-cve.patch
@@ -0,0 +1,275 @@
+diff --git a/ecc-256.c b/ecc-256.c
+index 571cf73..07841b1 100644
+--- a/ecc-256.c
++++ b/ecc-256.c
+@@ -108,7 +108,10 @@ ecc_256_modp (const struct ecc_curve *ecc, mp_limb_t *rp)
+       u0 -= t;
+       t = (u1 < cy);
+       u1 -= cy;
+-      u1 += cnd_add_n (t, rp + n - 4, ecc->p, 3);
++
++      cy = cnd_add_n (t, rp + n - 4, ecc->p, 2);
++      u0 += cy;
++      u1 += (u0 < cy);
+       u1 -= (-t) & 0xffffffff;
+     }
+   rp[2] = u0;
+@@ -195,7 +198,7 @@ ecc_256_modq (const struct ecc_curve *ecc, mp_limb_t *rp)
+ 
+       /* Conditional add of p */
+       u1 += t;
+-      u2 += (t<<32) + (u0 < t);
++      u2 += (t<<32) + (u1 < t);
+ 
+       t = cnd_add_n (t, rp + n - 4, ecc->q, 2);
+       u1 += t;
+diff --git a/x86_64/ecc-384-modp.asm b/x86_64/ecc-384-modp.asm
+index 698838f..31b739e 100644
+--- a/x86_64/ecc-384-modp.asm
++++ b/x86_64/ecc-384-modp.asm
+@@ -20,7 +20,7 @@ C MA 02111-1301, USA.
+ 	.file "ecc-384-modp.asm"
+ 
+ define(<RP>, <%rsi>)
+-define(<D4>, <%rax>)
++define(<D5>, <%rax>)
+ define(<T0>, <%rbx>)
+ define(<T1>, <%rcx>)
+ define(<T2>, <%rdx>)
+@@ -35,8 +35,8 @@ define(<H4>, <%r13>)
+ define(<H5>, <%r14>)
+ define(<C2>, <%r15>)
+ define(<C0>, H5)	C Overlap
+-define(<D0>, RP)	C Overlap
+-define(<TMP>, H4)	C Overlap
++define(<TMP>, RP)	C Overlap
++
+ 
+ PROLOGUE(nettle_ecc_384_modp)
+ 	W64_ENTRY(2, 0)
+@@ -48,34 +48,38 @@ PROLOGUE(nettle_ecc_384_modp)
+ 	push	%r14
+ 	push	%r15
+ 
+-	C First get top 2 limbs, which need folding twice
++	C First get top 2 limbs, which need folding twice.
++	C B^10 = B^6 + B^4 + 2^32 (B-1)B^4.
++	C We handle the terms as follow:
+ 	C
+-	C   H5 H4
+-	C     -H5
+-	C  ------
+-	C   H0 D4
++	C B^6: Folded immediatly.
+ 	C
+-	C Then shift right, (H1,H0,D4)  <--  (H0,D4) << 32
+-	C and add
++	C B^4: Delayed, added in in the next folding.
+ 	C
+-	C     H5 H4
+-	C     H1 H0
+-	C ----------
+-	C  C2 H1 H0
+-
+-	mov	80(RP), D4
+-	mov	88(RP), H0
+-	mov	D4, H4
+-	mov	H0, H5
+-	sub	H0, D4
+-	sbb	$0, H0
+-
+-	mov	D4, T2
+-	mov	H0, H1
+-	shl	$32, H0
+-	shr	$32, T2
++	C 2^32(B-1) B^4: Low half limb delayed until the next
++	C folding. Top 1.5 limbs subtracted and shifter now, resulting
++	C in 2.5 limbs. The low limb saved in D5, high 1.5 limbs added
++	C in.
++
++	mov	80(RP), H4
++	mov	88(RP), H5
++	C Shift right 32 bits, into H1, H0
++	mov	H4, H0
++	mov	H5, H1
++	mov	H5, D5
+ 	shr	$32, H1
+-	or	T2, H0
++	shl	$32, D5
++	shr	$32, H0
++	or	D5, H0
++
++	C	H1 H0
++	C       -  H1 H0
++	C       --------
++	C       H1 H0 D5
++	mov	H0, D5
++	neg	D5
++	sbb	H1, H0
++	sbb	$0, H1
+ 
+ 	xor	C2, C2
+ 	add	H4, H0
+@@ -114,118 +118,95 @@ PROLOGUE(nettle_ecc_384_modp)
+ 	adc	H3, T5
+ 	adc	$0, C0
+ 
+-	C   H3 H2 H1 H0  0
+-	C - H4 H3 H2 H1 H0
+-	C  ---------------
+-	C   H3 H2 H1 H0 D0
+-
+-	mov	XREG(D4), XREG(D4)
+-	mov	H0, D0
+-	neg	D0
+-	sbb	H1, H0
+-	sbb	H2, H1
+-	sbb	H3, H2
+-	sbb	H4, H3
+-	sbb	$0, D4
+-
+-	C Shift right. High bits are sign, to be added to C0.
+-	mov	D4, TMP
+-	sar	$32, TMP
+-	shl	$32, D4
+-	add	TMP, C0
+-
++	C Shift left, including low half of H4
+ 	mov	H3, TMP
++	shl	$32, H4
+ 	shr	$32, TMP
+-	shl	$32, H3
+-	or	TMP, D4
++	or	TMP, H4
+ 
+ 	mov	H2, TMP
++	shl	$32, H3
+ 	shr	$32, TMP
+-	shl	$32, H2
+ 	or	TMP, H3
+ 
+ 	mov	H1, TMP
++	shl	$32, H2
+ 	shr	$32, TMP
+-	shl	$32, H1
+ 	or	TMP, H2
+ 
+ 	mov	H0, TMP
++	shl	$32, H1
+ 	shr	$32, TMP
+-	shl	$32, H0
+ 	or	TMP, H1
+ 
+-	mov	D0, TMP
+-	shr	$32, TMP
+-	shl	$32, D0
+-	or	TMP, H0
++	shl	$32, H0
++
++	C   H4 H3 H2 H1 H0  0
++	C  -   H4 H3 H2 H1 H0
++	C  ---------------
++	C   H4 H3 H2 H1 H0 TMP
+ 
+-	add	D0, T0
++	mov	H0, TMP
++	neg	TMP
++	sbb	H1, H0
++	sbb	H2, H1
++	sbb	H3, H2
++	sbb	H4, H3
++	sbb	$0, H4
++
++	add	TMP, T0
+ 	adc	H0, T1
+ 	adc	H1, T2
+ 	adc	H2, T3
+ 	adc	H3, T4
+-	adc	D4, T5
++	adc	H4, T5
+ 	adc	$0, C0
+ 
+ 	C Remains to add in C2 and C0
+-	C                         C0  C0<<32  (-2^32+1)C0
+-	C    C2  C2<<32  (-2^32+1)C2
+-	C where C2 is always positive, while C0 may be -1.
++	C Set H1, H0 = (2^96 - 2^32 + 1) C0
+ 	mov	C0, H0
+ 	mov	C0, H1
+-	mov	C0, H2
+-	sar	$63, C0		C Get sign
+ 	shl	$32, H1
+-	sub	H1, H0		C Gives borrow iff C0 > 0
++	sub	H1, H0
+ 	sbb	$0, H1
+-	add	C0, H2
+ 
++	C Set H3, H2 = (2^96 - 2^32 + 1) C2
++	mov	C2, H2
++	mov	C2, H3
++	shl	$32, H3
++	sub	H3, H2
++	sbb	$0, H3
++	add	C0, H2		C No carry. Could use lea trick
++
++	xor	C0, C0
+ 	add	H0, T0
+ 	adc	H1, T1
+-	adc	$0, H2
+-	adc	$0, C0
+-
+-	C Set (H1 H0)  <-- C2 << 96 - C2 << 32 + 1
+-	mov	C2, H0
+-	mov	C2, H1
+-	shl	$32, H1
+-	sub	H1, H0
+-	sbb	$0, H1
+-
+-	add	H2, H0
+-	adc	C0, H1
+-	adc	C2, C0
+-	mov	C0, H2
+-	sar	$63, C0
+-	add	H0, T2
+-	adc	H1, T3
+-	adc	H2, T4
+-	adc	C0, T5
+-	sbb	C0, C0
++	adc	H2, T2
++	adc	H3, T3
++	adc	C2, T4
++	adc	D5, T5		C Value delayed from initial folding
++	adc	$0, C0		C Use sbb and switch sign?
+ 
+ 	C Final unlikely carry
+ 	mov	C0, H0
+ 	mov	C0, H1
+-	mov	C0, H2
+-	sar	$63, C0
+ 	shl	$32, H1
+ 	sub	H1, H0
+ 	sbb	$0, H1
+-	add	C0, H2
+ 
+ 	pop	RP
+ 
+-	sub	H0, T0
++	add	H0, T0
+ 	mov	T0, (RP)
+-	sbb	H1, T1
++	adc	H1, T1
+ 	mov	T1, 8(RP)
+-	sbb	H2, T2
++	adc	C0, T2
+ 	mov	T2, 16(RP)
+-	sbb	C0, T3
++	adc	$0, T3
+ 	mov	T3, 24(RP)
+-	sbb	C0, T4
++	adc	$0, T4
+ 	mov	T4, 32(RP)
+-	sbb	C0, T5
++	adc	$0, T5
+ 	mov	T5, 40(RP)
+ 
+ 	pop	%r15
diff --git a/contrib/packages/rpm/el6/SPECS/tigervnc.spec b/contrib/packages/rpm/el6/SPECS/tigervnc.spec
index 67bd82d..16d1ba5 100644
--- a/contrib/packages/rpm/el6/SPECS/tigervnc.spec
+++ b/contrib/packages/rpm/el6/SPECS/tigervnc.spec
@@ -10,7 +10,7 @@
 
 Name: tigervnc
 Version: @VERSION@
-Release: 3%{?snap:.%{snap}}%{?dist}
+Release: 4%{?snap:.%{snap}}%{?dist}
 Summary: A TigerVNC remote display system
 
 Group: User Interface/Desktops
@@ -60,6 +60,7 @@
 Obsoletes: tightvnc < 1.5.0-0.15.20090204svn3586
 
 Patch16: tigervnc-xorg-manpages.patch
+Patch17: nettle-2.7.1-ecc-cve.patch
 
 %description
 Virtual Network Computing (VNC) is a remote display system which
@@ -165,6 +166,9 @@
 tar xjf %SOURCE14
 tar xzf %SOURCE15
 tar xzf %SOURCE16
+pushd nettle-*
+%patch17 -p1 -b .ecc-cve
+popd
 xzcat %SOURCE17 | tar xf -
 %endif
 
@@ -456,6 +460,9 @@
 %endif
 
 %changelog
+* Sat Apr 02 2016 Brian P. Hinz <bphinz@users.sourceforge.net> 1.6.80-4
+- Fixed CVE-2015-8803 CVE-2015-8804 CVE-2015-8805 secp256r1 and secp384r1 bugs
+
 * Fri Dec 11 2015 Brian P. Hinz <bphinz@users.sourceforge.net> 1.6.80-3
 - Configure with --host and --build to avoid build host-specific compiler opts
 
diff --git a/java/com/tigervnc/vncviewer/CConn.java b/java/com/tigervnc/vncviewer/CConn.java
index dbb2a29..b9680ef 100644
--- a/java/com/tigervnc/vncviewer/CConn.java
+++ b/java/com/tigervnc/vncviewer/CConn.java
@@ -78,7 +78,7 @@
   public CConn(VncViewer viewer_, Socket sock_,
                String vncServerName)
   {
-    serverHost = null; serverPort = 0; sock = sock_; viewer = viewer_;
+    sock = sock_; viewer = viewer_;
     pendingPFChange = false;
     currentEncoding = Encodings.encodingTight; lastServerEncoding = -1;
     fullColour = viewer.fullColour.getValue();
@@ -121,8 +121,8 @@
     } else {
       if (vncServerName != null &&
           !viewer.alwaysShowServerDialog.getValue()) {
-        serverHost = Hostname.getHost(vncServerName);
-        serverPort = Hostname.getPort(vncServerName);
+        setServerName(Hostname.getHost(vncServerName));
+        setServerPort(Hostname.getPort(vncServerName));
       } else {
         ServerDialog dlg = new ServerDialog(options, vncServerName, this);
         boolean ret = dlg.showDialog();
@@ -130,20 +130,27 @@
           close();
           return;
         }
-        serverHost = viewer.vncServerName.getValueStr();
-        serverPort = viewer.vncServerPort.getValue();
+        setServerName(viewer.vncServerName.getValueStr());
+        setServerPort(viewer.vncServerPort.getValue());
       }
 
       try {
-        sock = new TcpSocket(serverHost, serverPort);
+        if (viewer.tunnel.getValue() || (viewer.via.getValue() != null)) {
+          int localPort = TcpSocket.findFreeTcpPort();
+          if (localPort == 0)
+            throw new Exception("Could not obtain free TCP port");
+          Tunnel.createTunnel(this, localPort);
+          sock = new TcpSocket("localhost", localPort);
+        } else {
+          sock = new TcpSocket(getServerName(), getServerPort());
+        }
       } catch (java.lang.Exception e) {
         throw new Exception(e.getMessage());
       }
-      vlog.info("connected to host "+serverHost+" port "+serverPort);
+      vlog.info("connected to host "+getServerName()+" port "+getServerPort());
     }
 
     sock.inStream().setBlockCallback(this);
-    setServerName(serverHost);
     setStreams(sock.inStream(), sock.outStream());
     initialiseProtocol();
   }
@@ -896,10 +903,47 @@
       options.sendLocalUsername.setEnabled(false);
       options.cfLoadButton.setEnabled(false);
       options.cfSaveAsButton.setEnabled(true);
+      options.sshTunnel.setEnabled(false);
+      options.sshUseGateway.setEnabled(false);
+      options.sshUser.setEnabled(false);
+      options.sshHost.setEnabled(false);
+      options.sshPort.setEnabled(false);
+      options.sshUseExt.setEnabled(false);
+      options.sshClient.setEnabled(false);
+      options.sshClientBrowser.setEnabled(false);
+      options.sshArgsDefault.setEnabled(false);
+      options.sshArgsCustom.setEnabled(false);
+      options.sshArguments.setEnabled(false);
+      options.sshConfig.setEnabled(false);
+      options.sshConfigBrowser.setEnabled(false);
+      options.sshKeyFile.setEnabled(false);
+      options.sshKeyFileBrowser.setEnabled(false);
     } else {
       options.shared.setSelected(viewer.shared.getValue());
       options.sendLocalUsername.setSelected(viewer.sendLocalUsername.getValue());
       options.cfSaveAsButton.setEnabled(false);
+      if (viewer.tunnel.getValue() || viewer.via.getValue() != null)
+        options.sshTunnel.setSelected(true);
+      if (viewer.via.getValue() != null)
+        options.sshUseGateway.setSelected(true);
+      options.sshUser.setText(Tunnel.getSshUser(this));
+      options.sshHost.setText(Tunnel.getSshHost(this));
+      options.sshPort.setText(Integer.toString(Tunnel.getSshPort(this)));
+      options.sshUseExt.setSelected(viewer.extSSH.getValue());
+      File client = new File(viewer.extSSHClient.getValue());
+      if (client.exists() && client.canRead())
+        options.sshClient.setText(client.getAbsolutePath());
+      if (viewer.extSSHArgs.getValue() == null) {
+        options.sshArgsDefault.setSelected(true);
+        options.sshArguments.setText("");
+      } else {
+        options.sshArgsCustom.setSelected(true);
+        options.sshArguments.setText(viewer.extSSHArgs.getValue());
+      }
+      File config = new File(viewer.sshConfig.getValue());
+      if (config.exists() && config.canRead())
+        options.sshConfig.setText(config.getAbsolutePath());
+      options.sshKeyFile.setText(Tunnel.getSshKeyFile(this));
 
       /* Process non-VeNCrypt sectypes */
       java.util.List<Integer> secTypes = new ArrayList<Integer>();
@@ -990,6 +1034,46 @@
       options.secPlain.setEnabled(options.secVeNCrypt.isSelected());
       options.sendLocalUsername.setEnabled(options.secPlain.isSelected()||
         options.secIdent.isSelected());
+      options.sshTunnel.setEnabled(true);
+        options.sshUseGateway.setEnabled(options.sshTunnel.isSelected());
+        options.sshUser.setEnabled(options.sshTunnel.isSelected() &&
+                                   options.sshUseGateway.isEnabled() &&
+                                   options.sshUseGateway.isSelected());
+        options.sshHost.setEnabled(options.sshTunnel.isSelected() &&
+                                   options.sshUseGateway.isEnabled() &&
+                                   options.sshUseGateway.isSelected());
+        options.sshPort.setEnabled(options.sshTunnel.isSelected() &&
+                                   options.sshUseGateway.isEnabled() &&
+                                   options.sshUseGateway.isSelected());
+        options.sshUseExt.setEnabled(options.sshTunnel.isSelected());
+        options.sshClient.setEnabled(options.sshTunnel.isSelected() &&
+                                     options.sshUseExt.isEnabled() &&
+                                     options.sshUseExt.isSelected());
+        options.sshClientBrowser.setEnabled(options.sshTunnel.isSelected() &&
+                                            options.sshUseExt.isEnabled() &&
+                                            options.sshUseExt.isSelected());
+        options.sshArgsDefault.setEnabled(options.sshTunnel.isSelected() &&
+                                          options.sshUseExt.isEnabled() &&
+                                          options.sshUseExt.isSelected());
+        options.sshArgsCustom.setEnabled(options.sshTunnel.isSelected() &&
+                                         options.sshUseExt.isEnabled() &&
+                                         options.sshUseExt.isSelected());
+        options.sshArguments.setEnabled(options.sshTunnel.isSelected() &&
+                                        options.sshUseExt.isEnabled() &&
+                                        options.sshUseExt.isSelected() &&
+                                        options.sshArgsCustom.isSelected());
+        options.sshConfig.setEnabled(options.sshTunnel.isSelected() &&
+                                     options.sshUseExt.isEnabled() &&
+                                     !options.sshUseExt.isSelected());
+        options.sshConfigBrowser.setEnabled(options.sshTunnel.isSelected() &&
+                                            options.sshUseExt.isEnabled() &&
+                                            !options.sshUseExt.isSelected());
+        options.sshKeyFile.setEnabled(options.sshTunnel.isSelected() &&
+                                      options.sshUseExt.isEnabled() &&
+                                      !options.sshUseExt.isSelected());
+        options.sshKeyFileBrowser.setEnabled(options.sshTunnel.isSelected() &&
+                                             options.sshUseExt.isEnabled() &&
+                                             !options.sshUseExt.isSelected());
     }
 
     options.fullScreen.setSelected(fullScreen);
@@ -1111,6 +1195,7 @@
       if (desktop != null)
         desktop.resetLocalCursor();
     }
+    viewer.extSSH.setParam(options.sshUseExt.isSelected());
 
     checkEncodings();
 
@@ -1221,6 +1306,22 @@
         Security.DisableSecType(Security.secTypeTLSIdent);
         Security.DisableSecType(Security.secTypeX509Ident);
       }
+      if (options.sshTunnel.isSelected()) {
+        if (options.sshUseGateway.isSelected()) {
+          String user = options.sshUser.getText();
+          String host = options.sshHost.getText();
+          String port = options.sshPort.getText();
+          viewer.via.setParam(user+"@"+host+":"+port);
+        } else {
+          viewer.tunnel.setParam(true);
+        }
+      }
+      viewer.extSSH.setParam(options.sshUseExt.isSelected());
+      viewer.extSSHClient.setParam(options.sshClient.getText());
+      if (options.sshArgsCustom.isSelected())
+        viewer.extSSHArgs.setParam(options.sshArguments.getText());
+      viewer.sshConfig.setParam(options.sshConfig.getText());
+      viewer.sshKeyFile.setParam(options.sshKeyFile.getText());
     }
     String desktopSize = (options.desktopSize.isSelected()) ?
         options.desktopWidth.getText() + "x" + options.desktopHeight.getText() : "";
@@ -1472,8 +1573,6 @@
   // the following are only ever accessed by the GUI thread:
   int buttonMask;
 
-  private String serverHost;
-  private int serverPort;
   private Socket sock;
 
   protected DesktopWindow desktop;
diff --git a/java/com/tigervnc/vncviewer/Dialog.java b/java/com/tigervnc/vncviewer/Dialog.java
index 3d24619..8bf1979 100644
--- a/java/com/tigervnc/vncviewer/Dialog.java
+++ b/java/com/tigervnc/vncviewer/Dialog.java
@@ -99,6 +99,8 @@
     for (Component ch : c.getComponents()) {
       if (ch instanceof JCheckBox)
         ((JCheckBox)ch).addItemListener(this);
+      else if (ch instanceof JRadioButton)
+        ((JRadioButton)ch).addActionListener(this);
       else if (ch instanceof JButton)
         ((JButton)ch).addActionListener(this);
       else if (ch instanceof JComboBox)
diff --git a/java/com/tigervnc/vncviewer/OptionsDialog.java b/java/com/tigervnc/vncviewer/OptionsDialog.java
index 1681518..369b965 100644
--- a/java/com/tigervnc/vncviewer/OptionsDialog.java
+++ b/java/com/tigervnc/vncviewer/OptionsDialog.java
@@ -31,7 +31,6 @@
 import java.util.*;
 import java.util.Map.Entry;
 
-
 import com.tigervnc.rfb.*;
 
 import static java.awt.GridBagConstraints.BOTH;
@@ -50,16 +49,16 @@
   private class IntegerDocument extends PlainDocument {
     private int limit;
 
-    IntegerDocument(int limit) {
+    public IntegerDocument(int max) {
       super();
-      this.limit = limit;
+      limit = max;
     }
 
-    public void insertString(int offset, String  str, AttributeSet a)
+    public void insertString(int offset, String str, AttributeSet a)
           throws BadLocationException {
       if (str == null || !str.matches("^[0-9]+$")) return;
       if ((getLength() + str.length()) > limit)
-        java.awt.Toolkit.getDefaultToolkit().beep();
+        Toolkit.getDefaultToolkit().beep();
       else
         super.insertString(offset, str, a);
     }
@@ -68,11 +67,11 @@
   private class IntegerTextField extends JFormattedTextField {
     public IntegerTextField(int digits) {
       super();
-      this.setDocument(new IntegerDocument(digits));
+      setDocument(new IntegerDocument(digits));
       Font f = getFont();
       String template = String.format("%0"+digits+"d", 0);
       int w = getFontMetrics(f).stringWidth(template) +
-              getMargin().left + getMargin().right + 
+              getMargin().left + getMargin().right +
               getInsets().left + getInsets().right;
       int h = getPreferredSize().height;
       setPreferredSize(new Dimension(w, h));
@@ -95,17 +94,20 @@
   CConn cc;
   @SuppressWarnings({"rawtypes"})
   JComboBox menuKey, compressLevel, qualityLevel, scalingFactor;
-  ButtonGroup encodingGroup, colourGroup;
+  ButtonGroup encodingGroup, colourGroup, sshArgsGroup;
   JRadioButton zrle, hextile, tight, raw, fullColour, mediumColour,
-               lowColour, veryLowColour;
+               lowColour, veryLowColour, sshArgsDefault, sshArgsCustom;
   JCheckBox autoSelect, customCompressLevel, noJpeg, viewOnly,
             acceptClipboard, sendClipboard, acceptBell, desktopSize,
             fullScreen, fullScreenAllMonitors, shared, useLocalCursor,
             secVeNCrypt, encNone, encTLS, encX509, secNone, secVnc,
-            secPlain, secIdent, sendLocalUsername;
+            secPlain, secIdent, sendLocalUsername, sshTunnel, sshUseExt,
+            sshUseGateway;
   JButton okButton, cancelButton, caButton, crlButton, cfLoadButton,
-          cfSaveAsButton, defSaveButton, defReloadButton, defClearButton;
-  JTextField desktopWidth, desktopHeight, x509ca, x509crl;
+          cfSaveAsButton, defSaveButton, defReloadButton, defClearButton,
+          sshConfigBrowser, sshKeyFileBrowser, sshClientBrowser;
+  JTextField desktopWidth, desktopHeight, x509ca, x509crl, sshUser, sshHost,
+             sshPort, sshClient, sshArguments, sshConfig, sshKeyFile;
   JTabbedPane tabPane;
 
   @SuppressWarnings({"rawtypes","unchecked"})
@@ -123,6 +125,7 @@
 
     encodingGroup = new ButtonGroup();
     colourGroup = new ButtonGroup();
+    sshArgsGroup = new ButtonGroup();
     int indent = 0;
 
     // Compression tab
@@ -573,6 +576,248 @@
                                          new Insets(0, 0, 0, 0),
                                          NONE, NONE));
 
+    // SSH tab
+    JPanel sshPanel = new JPanel(new GridBagLayout());
+    sshPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
+    sshTunnel = new JCheckBox("Tunnel VNC over SSH");
+
+    JPanel tunnelPanel = new JPanel(new GridBagLayout());
+
+    sshUseGateway = new JCheckBox("Use SSH gateway");
+    JLabel sshUserLabel = new JLabel("Username");
+    sshUser = new JTextField();
+    JLabel sshUserAtLabel = new JLabel("@");
+    JLabel sshHostLabel = new JLabel("Hostname (or IP address)");
+    sshHost = new JTextField("");
+    JLabel sshPortLabel = new JLabel("Port");
+    sshPort = new IntegerTextField(5);
+
+    sshUseExt = new JCheckBox("Use external SSH client");
+    sshClient = new JTextField();
+    sshClient.setName(Configuration.getParam("extSSHClient").getName());
+    sshClientBrowser = new JButton("Browse");
+    JLabel sshConfigLabel = new JLabel("SSH config file");
+    sshConfig = new JTextField();
+    sshConfig.setName(Configuration.getParam("sshConfig").getName());
+    sshConfigBrowser = new JButton("Browse");
+    JLabel sshKeyFileLabel = new JLabel("SSH identity file");
+    sshKeyFile = new JTextField();
+    sshKeyFile.setName(Configuration.getParam("sshKeyFile").getName());
+    sshKeyFileBrowser = new JButton("Browse");
+    JPanel sshArgsPanel = new JPanel(new GridBagLayout());
+    JLabel sshArgsLabel = new JLabel("Arguments:");
+    sshArgsDefault =
+      new GroupedJRadioButton("Default", sshArgsGroup, sshArgsPanel);
+    sshArgsCustom =
+      new GroupedJRadioButton("Custom", sshArgsGroup, sshArgsPanel);
+    sshArguments = new JTextField();
+
+    JPanel gatewayPanel = new JPanel(new GridBagLayout());
+    gatewayPanel.add(sshUseGateway,
+                    new GridBagConstraints(0, 0,
+                                           REMAINDER, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, NONE,
+                                           new Insets(0, 0, 4, 0),
+                                           NONE, NONE));
+    indent = getButtonLabelInset(sshUseGateway);
+    gatewayPanel.add(sshUserLabel,
+                 new GridBagConstraints(0, 1,
+                                        1, 1,
+                                        LIGHT, LIGHT,
+                                        LINE_START, HORIZONTAL,
+                                        new Insets(0, indent, 4, 0),
+                                        NONE, NONE));
+    gatewayPanel.add(sshHostLabel,
+                 new GridBagConstraints(2, 1,
+                                        1, 1,
+                                        HEAVY, LIGHT,
+                                        LINE_START, HORIZONTAL,
+                                        new Insets(0, 0, 4, 0),
+                                        NONE, NONE));
+    gatewayPanel.add(sshPortLabel,
+                 new GridBagConstraints(3, 1,
+                                        1, 1,
+                                        LIGHT, LIGHT,
+                                        LINE_START, HORIZONTAL,
+                                        new Insets(0, 5, 4, 0),
+                                        NONE, NONE));
+    gatewayPanel.add(sshUser,
+                 new GridBagConstraints(0, 2,
+                                        1, 1,
+                                        LIGHT, LIGHT,
+                                        LINE_START, HORIZONTAL,
+                                        new Insets(0, indent, 0, 0),
+                                        NONE, NONE));
+    gatewayPanel.add(sshUserAtLabel,
+                 new GridBagConstraints(1, 2,
+                                        1, 1,
+                                        LIGHT, LIGHT,
+                                        LINE_START, HORIZONTAL,
+                                        new Insets(0, 2, 0, 2),
+                                        NONE, NONE));
+    gatewayPanel.add(sshHost,
+                 new GridBagConstraints(2, 2,
+                                        1, 1,
+                                        HEAVY, LIGHT,
+                                        LINE_START, HORIZONTAL,
+                                        new Insets(0, 0, 0, 0),
+                                        NONE, NONE));
+    gatewayPanel.add(sshPort,
+                 new GridBagConstraints(3, 2,
+                                        1, 1,
+                                        LIGHT, LIGHT,
+                                        LINE_START, HORIZONTAL,
+                                        new Insets(0, 5, 0, 0),
+                                        NONE, NONE));
+
+    JPanel clientPanel = new JPanel(new GridBagLayout());
+    clientPanel.add(sshUseExt,
+                    new GridBagConstraints(0, 0,
+                                           1, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, NONE,
+                                           new Insets(0, 0, 0, 0),
+                                           NONE, NONE));
+    clientPanel.add(sshClient,
+                    new GridBagConstraints(1, 0,
+                                           1, 1,
+                                           HEAVY, LIGHT,
+                                           LINE_START, HORIZONTAL,
+                                           new Insets(0, 5, 0, 0),
+                                           NONE, NONE));
+    clientPanel.add(sshClientBrowser,
+                    new GridBagConstraints(2, 0,
+                                           1, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, NONE,
+                                           new Insets(0, 5, 0, 0),
+                                           NONE, NONE));
+    sshArgsPanel.add(sshArgsLabel,
+                    new GridBagConstraints(0, 1,
+                                           1, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, NONE,
+                                           new Insets(0, 0, 0, 0),
+                                           NONE, NONE));
+    sshArgsPanel.add(sshArgsDefault,
+                    new GridBagConstraints(1, 1,
+                                           1, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, NONE,
+                                           new Insets(0, 5, 0, 0),
+                                           NONE, NONE));
+    sshArgsPanel.add(sshArgsCustom,
+                    new GridBagConstraints(2, 1,
+                                           1, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, NONE,
+                                           new Insets(0, 5, 0, 0),
+                                           NONE, NONE));
+    sshArgsPanel.add(sshArguments,
+                    new GridBagConstraints(3, 1,
+                                           1, 1,
+                                           HEAVY, LIGHT,
+                                           LINE_START, HORIZONTAL,
+                                           new Insets(0, 5, 0, 0),
+                                           NONE, NONE));
+    indent = getButtonLabelInset(sshUseExt);
+    clientPanel.add(sshArgsPanel,
+                    new GridBagConstraints(0, 1,
+                                           REMAINDER, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, HORIZONTAL,
+                                           new Insets(4, indent, 0, 0),
+                                           NONE, NONE));
+
+    JPanel opensshPanel = new JPanel(new GridBagLayout());
+    opensshPanel.setBorder(BorderFactory.createTitledBorder("Embedded SSH client configuration"));
+    opensshPanel.add(sshConfigLabel,
+                     new GridBagConstraints(0, 0,
+                                            1, 1,
+                                            LIGHT, LIGHT,
+                                            LINE_START, NONE,
+                                            new Insets(0, 0, 5, 0),
+                                            NONE, NONE));
+    opensshPanel.add(sshConfig,
+                     new GridBagConstraints(1, 0,
+                                            1, 1,
+                                            HEAVY, LIGHT,
+                                            LINE_START, HORIZONTAL,
+                                            new Insets(0, 5, 5, 0),
+                                            NONE, NONE));
+    opensshPanel.add(sshConfigBrowser,
+                     new GridBagConstraints(2, 0,
+                                            1, 1,
+                                            LIGHT, LIGHT,
+                                            LINE_START, VERTICAL,
+                                            new Insets(0, 5, 5, 0),
+                                            NONE, NONE));
+    opensshPanel.add(sshKeyFileLabel,
+                     new GridBagConstraints(0, 1,
+                                            1, 1,
+                                            LIGHT, LIGHT,
+                                            LINE_START, NONE,
+                                            new Insets(0, 0, 0, 0),
+                                            NONE, NONE));
+    opensshPanel.add(sshKeyFile,
+                     new GridBagConstraints(1, 1,
+                                            1, 1,
+                                            HEAVY, LIGHT,
+                                            LINE_START, HORIZONTAL,
+                                            new Insets(0, 5, 0, 0),
+                                            NONE, NONE));
+    opensshPanel.add(sshKeyFileBrowser,
+                     new GridBagConstraints(2, 1,
+                                            1, 1,
+                                            LIGHT, LIGHT,
+                                            LINE_START, VERTICAL,
+                                            new Insets(0, 5, 0, 0),
+                                            NONE, NONE));
+    tunnelPanel.add(gatewayPanel,
+                    new GridBagConstraints(0, 0,
+                                           REMAINDER, 1,
+                                           HEAVY, LIGHT,
+                                           LINE_START, HORIZONTAL,
+                                           new Insets(0, 0, 4, 0),
+                                           NONE, NONE));
+    tunnelPanel.add(clientPanel,
+                    new GridBagConstraints(0, 1,
+                                           REMAINDER, 1,
+                                           HEAVY, LIGHT,
+                                           LINE_START, HORIZONTAL,
+                                           new Insets(0, 0, 4, 0),
+                                           NONE, NONE));
+    tunnelPanel.add(opensshPanel,
+                    new GridBagConstraints(0, 2,
+                                           REMAINDER, 1,
+                                           HEAVY, LIGHT,
+                                           LINE_START, HORIZONTAL,
+                                           new Insets(0, 0, 0, 0),
+                                           NONE, NONE));
+
+    sshPanel.add(sshTunnel,
+                 new GridBagConstraints(0, 0,
+                                        REMAINDER, 1,
+                                        LIGHT, LIGHT,
+                                        LINE_START, NONE,
+                                        new Insets(0, 0, 4, 0),
+                                        NONE, NONE));
+    indent = getButtonLabelInset(sshTunnel);
+    sshPanel.add(tunnelPanel,
+                 new GridBagConstraints(0, 2,
+                                        REMAINDER, 1,
+                                        LIGHT, LIGHT,
+                                        LINE_START, HORIZONTAL,
+                                        new Insets(0, indent, 4, 0),
+                                        NONE, NONE));
+    sshPanel.add(Box.createRigidArea(new Dimension(5, 0)),
+                 new GridBagConstraints(0, RELATIVE,
+                                        REMAINDER, REMAINDER,
+                                        HEAVY, HEAVY,
+                                        LINE_START, BOTH,
+                                        new Insets(0, 0, 0, 0),
+                                        NONE, NONE));
 
     // load/save tab
     JPanel loadSavePanel = new JPanel(new GridBagLayout());
@@ -659,6 +904,7 @@
     tabPane.addTab("Input", inputPanel);
     tabPane.addTab("Screen", ScreenPanel);
     tabPane.addTab("Misc", MiscPanel);
+    tabPane.addTab("SSH", sshPanel);
     tabPane.addTab("Load / Save", loadSavePanel);
     tabPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
     // Resize the tabPane if necessary to prevent scrolling
@@ -701,8 +947,10 @@
     veryLowColour.setEnabled(!autoSelect.isSelected());
     compressLevel.setEnabled(customCompressLevel.isSelected());
     qualityLevel.setEnabled(noJpeg.isSelected());
-    sendLocalUsername.setEnabled(secVeNCrypt.isEnabled()&&
-      (secPlain.isSelected()||secIdent.isSelected()));
+    sendLocalUsername.setEnabled(secVeNCrypt.isEnabled() &&
+      (secPlain.isSelected() || secIdent.isSelected()));
+    sshArguments.setEnabled(sshTunnel.isSelected() &&
+      (sshUseExt.isSelected() && sshArgsCustom.isSelected()));
   }
 
   private void updatePreferences() {
@@ -782,6 +1030,19 @@
     if (!CSecurityTLS.x509crl.getValueStr().equals(""))
       UserPreferences.set("viewer", "x509crl",
               CSecurityTLS.x509crl.getValueStr());
+    UserPreferences.set("global", "Tunnel", sshTunnel.isSelected());
+    if (sshUseGateway.isSelected()) {
+      String via = sshUser.getText()+"@"+sshHost.getText()+":"+sshPort.getText();
+      UserPreferences.set("global", "Via", via);
+    }
+    if (sshUseExt.isSelected()) {
+      UserPreferences.set("global", "extSSH", sshUseExt.isSelected());
+      UserPreferences.set("global", "extSSHClient", sshClient.getText());
+      if (!sshArguments.getText().isEmpty())
+        UserPreferences.set("global", "extSSHArgs", sshArguments.getText());
+    }
+    UserPreferences.set("global", "SSHConfig", sshConfig.getText());
+    UserPreferences.set("global", "SSHKeyFile", sshKeyFile.getText());
   }
 
   private void restorePreferences() {
@@ -832,8 +1093,7 @@
     sendClipboard.setSelected(UserPreferences.getBool("global",
             "SendClipboard"));
     menuKey.setSelectedItem(UserPreferences.get("global", "MenuKey"));
-    desktopSize.setSelected(UserPreferences.get("global", "DesktopSize")
-            != null);
+    desktopSize.setSelected(!UserPreferences.get("global", "DesktopSize").isEmpty());
     if (desktopSize.isSelected()) {
       String desktopSizeString = UserPreferences.get("global", "DesktopSize");
       desktopWidth.setText(desktopSizeString.split("x")[0]);
@@ -874,6 +1134,70 @@
       secNone.setSelected(UserPreferences.getBool("viewer", "secNone", true));
     if (secVnc.isEnabled())
       secVnc.setSelected(UserPreferences.getBool("viewer", "secVnc", true));
+    sshTunnel.setSelected(UserPreferences.getBool("global", "Tunnel"));
+    sshUseGateway.setSelected(UserPreferences.get("global", "Via") != null);
+    if (sshUseGateway.isSelected())
+      cc.viewer.via.setParam(UserPreferences.get("global", "Via"));
+    sshUser.setText(Tunnel.getSshUser(cc));
+    sshHost.setText(Tunnel.getSshHost(cc));
+    sshPort.setText(Integer.toString(Tunnel.getSshPort(cc)));
+    sshUseExt.setSelected(UserPreferences.getBool("global", "extSSH"));
+    File f = new File(UserPreferences.get("global", "extSSHClient"));
+    if (f.exists() && f.canExecute())
+      sshClient.setText(f.getAbsolutePath());
+    sshArguments.setText(UserPreferences.get("global", "extSSHArgs"));
+    if (sshArguments.getText().isEmpty())
+      sshArgsDefault.setSelected(true);
+    else
+      sshArgsCustom.setSelected(true);
+    f = new File(UserPreferences.get("global", "SSHConfig"));
+    if (f.exists() && f.canRead())
+      sshConfig.setText(f.getAbsolutePath());
+    if (UserPreferences.get("global", "SSHKeyFile") != null) {
+      f = new File(UserPreferences.get("global", "SSHKeyFile"));
+      if (f.exists() && f.canRead())
+        sshKeyFile.setText(f.getAbsolutePath());
+    } else {
+      sshKeyFile.setText(Tunnel.getSshKeyFile(cc));
+    }
+    sshUseGateway.setEnabled(sshTunnel.isSelected());
+    sshUser.setEnabled(sshTunnel.isSelected() &&
+                       sshUseGateway.isEnabled() &&
+                       sshUseGateway.isSelected());
+    sshHost.setEnabled(sshTunnel.isSelected() &&
+                       sshUseGateway.isEnabled() &&
+                       sshUseGateway.isSelected());
+    sshPort.setEnabled(sshTunnel.isSelected() &&
+                       sshUseGateway.isEnabled() &&
+                       sshUseGateway.isSelected());
+    sshUseExt.setEnabled(sshTunnel.isSelected());
+    sshClient.setEnabled(sshTunnel.isSelected() &&
+                         sshUseExt.isEnabled());
+    sshClientBrowser.setEnabled(sshTunnel.isSelected() &&
+                                sshUseExt.isEnabled() &&
+                                sshUseExt.isSelected());
+    sshArgsDefault.setEnabled(sshTunnel.isSelected() &&
+                              sshUseExt.isEnabled() &&
+                              sshUseExt.isSelected());
+    sshArgsCustom.setEnabled(sshTunnel.isSelected() &&
+                             sshUseExt.isEnabled() &&
+                             sshUseExt.isSelected());
+    sshArguments.setEnabled(sshTunnel.isSelected() &&
+                            sshUseExt.isEnabled() &&
+                            sshUseExt.isSelected() &&
+                            sshArgsCustom.isSelected());
+    sshConfig.setEnabled(sshTunnel.isSelected() &&
+                         sshUseExt.isEnabled() &&
+                         !sshUseExt.isSelected());
+    sshConfigBrowser.setEnabled(sshTunnel.isSelected() &&
+                                sshUseExt.isEnabled() &&
+                                !sshUseExt.isSelected());
+    sshKeyFile.setEnabled(sshTunnel.isSelected() &&
+                          sshUseExt.isEnabled() &&
+                          !sshUseExt.isSelected());
+    sshKeyFileBrowser.setEnabled(sshTunnel.isSelected() &&
+                                 sshUseExt.isEnabled() &&
+                                 !sshUseExt.isSelected());
   }
 
   public void endDialog() {
@@ -890,15 +1214,15 @@
       JButton button = (JButton)s;
       if (button == okButton) {
         JTextField[] fields =
-          { x509ca, x509crl };
+          { x509ca, x509crl, sshClient, sshConfig, sshKeyFile };
         for (JTextField field : fields) {
           if (field.getText() != null && !field.getText().equals("")) {
             File f = new File(field.getText());
             if (!f.exists() || !f.canRead()) {
               String msg = new String("The file "+f.getAbsolutePath()+
                            " specified for option "+field.getName()+
-                           " does not exist or cannot be read.  Please "+
-                           "correct before proceeding.");
+                           " does not exist or cannot be read.  Please"+
+                           " correct before proceeding.");
               JOptionPane.showMessageDialog(this, msg, "WARNING",
                                             JOptionPane.WARNING_MESSAGE);
               return;
@@ -958,6 +1282,35 @@
         int ret = fc.showOpenDialog(this);
         if (ret == JFileChooser.APPROVE_OPTION)
           x509crl.setText(fc.getSelectedFile().toString());
+      } else if (button == sshClientBrowser) {
+        JFileChooser fc = new JFileChooser();
+        fc.setDialogTitle("Path to external SSH client");
+        fc.setApproveButtonText("OK");
+        fc.setFileHidingEnabled(false);
+        int ret = fc.showOpenDialog(this);
+        if (ret == JFileChooser.APPROVE_OPTION)
+          sshClient.setText(fc.getSelectedFile().toString());
+      } else if (button == sshConfigBrowser) {
+        JFileChooser fc = new JFileChooser();
+        fc.setDialogTitle("Path to OpenSSH client config file");
+        fc.setApproveButtonText("OK");
+        fc.setFileHidingEnabled(false);
+        int ret = fc.showOpenDialog(this);
+        if (ret == JFileChooser.APPROVE_OPTION)
+          sshConfig.setText(fc.getSelectedFile().toString());
+      } else if (button == sshKeyFileBrowser) {
+        JFileChooser fc = new JFileChooser();
+        fc.setDialogTitle("Path to SSH key file");
+        fc.setApproveButtonText("OK");
+        fc.setFileHidingEnabled(false);
+        int ret = fc.showOpenDialog(this);
+        if (ret == JFileChooser.APPROVE_OPTION)
+          sshKeyFile.setText(fc.getSelectedFile().toString());
+      }
+    } else if (s instanceof JRadioButton) {
+      JRadioButton button = (JRadioButton)s;
+      if (button == sshArgsCustom || button == sshArgsDefault) {
+        sshArguments.setEnabled(sshArgsCustom.isSelected());
       }
     }
   }
@@ -983,16 +1336,16 @@
         qualityLevel.setEnabled(enable);
       } else if (item == encX509) {
         x509ca.setEnabled(enable);
-        x509crl.setEnabled(enable);
         caButton.setEnabled(enable);
+        x509crl.setEnabled(enable);
         crlButton.setEnabled(enable);
       } else if (item == secVeNCrypt) {
         encNone.setEnabled(enable);
         encTLS.setEnabled(enable);
         encX509.setEnabled(enable);
         x509ca.setEnabled(enable && encX509.isSelected());
-        x509crl.setEnabled(enable && encX509.isSelected());
         caButton.setEnabled(enable && encX509.isSelected());
+        x509crl.setEnabled(enable && encX509.isSelected());
         crlButton.setEnabled(enable && encX509.isSelected());
         secIdent.setEnabled(enable);
         secPlain.setEnabled(enable);
@@ -1005,6 +1358,60 @@
       } else if (item == secIdent || item == secPlain) {
         sendLocalUsername.setEnabled(secIdent.isSelected() ||
                                      secPlain.isSelected());
+      } else if (item == sshTunnel) {
+        sshUseGateway.setEnabled(enable);
+        sshUser.setEnabled(enable &&
+                           sshUseGateway.isEnabled() &&
+                           sshUseGateway.isSelected());
+        sshHost.setEnabled(enable &&
+                           sshUseGateway.isEnabled() &&
+                           sshUseGateway.isSelected());
+        sshPort.setEnabled(enable &&
+                           sshUseGateway.isEnabled() &&
+                           sshUseGateway.isSelected());
+        sshUseExt.setEnabled(enable);
+        sshClient.setEnabled(enable &&
+                             sshUseExt.isEnabled() &&
+                             sshUseExt.isSelected());
+        sshClientBrowser.setEnabled(enable &&
+                                    sshUseExt.isEnabled() &&
+                                    sshUseExt.isSelected());
+        sshArgsDefault.setEnabled(enable &&
+                                  sshUseExt.isEnabled() &&
+                                  sshUseExt.isSelected());
+        sshArgsCustom.setEnabled(enable &&
+                                 sshUseExt.isEnabled() &&
+                                 sshUseExt.isSelected());
+        sshArguments.setEnabled(enable &&
+                                sshUseExt.isEnabled() &&
+                                sshUseExt.isSelected() &&
+                                sshArgsCustom.isSelected());
+        sshConfig.setEnabled(enable &&
+                             sshUseExt.isEnabled() &&
+                             !sshUseExt.isSelected());
+        sshConfigBrowser.setEnabled(enable &&
+                                    sshUseExt.isEnabled() &&
+                                    !sshUseExt.isSelected());
+        sshKeyFile.setEnabled(enable &&
+                              sshUseExt.isEnabled() &&
+                              !sshUseExt.isSelected());
+        sshKeyFileBrowser.setEnabled(enable &&
+                                     sshUseExt.isEnabled() &&
+                                     !sshUseExt.isSelected());
+      } else if (item == sshUseExt) {
+        sshClient.setEnabled(enable);
+        sshClientBrowser.setEnabled(enable);
+        sshArgsDefault.setEnabled(enable);
+        sshArgsCustom.setEnabled(enable);
+        sshArguments.setEnabled(enable && sshArgsCustom.isSelected());
+        sshConfig.setEnabled(!enable);
+        sshConfigBrowser.setEnabled(!enable);
+        sshKeyFile.setEnabled(!enable);
+        sshKeyFileBrowser.setEnabled(!enable);
+      } else if (item == sshUseGateway) {
+        sshUser.setEnabled(enable);
+        sshHost.setEnabled(enable);
+        sshPort.setEnabled(enable);
       }
     }
   }
diff --git a/java/com/tigervnc/vncviewer/PasswdDialog.java b/java/com/tigervnc/vncviewer/PasswdDialog.java
index 26a138d..fbaf991 100644
--- a/java/com/tigervnc/vncviewer/PasswdDialog.java
+++ b/java/com/tigervnc/vncviewer/PasswdDialog.java
@@ -120,6 +120,8 @@
     if (userEntry.isEnabled())
       if (userEntry.getText().equals(""))
         return false;
+      else if (!passwdEntry.isEnabled())
+        return true;
     if (passwdEntry.isEnabled())
       if (!passwdEntry.getText().equals(""))
         return true;
diff --git a/java/com/tigervnc/vncviewer/Tunnel.java b/java/com/tigervnc/vncviewer/Tunnel.java
new file mode 100644
index 0000000..90ab38e
--- /dev/null
+++ b/java/com/tigervnc/vncviewer/Tunnel.java
@@ -0,0 +1,355 @@
+/*
+ *  Copyright (C) 2012-2016 All Rights Reserved.
+ *  Copyright (C) 2000 Const Kaplinsky.  All Rights Reserved.
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ *  This is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This software is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this software; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ *  USA.
+ */
+
+/*
+ * tunnel.java - SSH tunneling support
+ */
+
+package com.tigervnc.vncviewer;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.*;
+
+import com.tigervnc.rdr.*;
+import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Exception;
+import com.tigervnc.network.*;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.ConfigRepository;
+import com.jcraft.jsch.Logger;
+import com.jcraft.jsch.OpenSSHConfig;
+import com.jcraft.jsch.Session;
+
+public class Tunnel {
+
+  private final static String DEFAULT_TUNNEL_TEMPLATE
+    = "-f -L %L:localhost:%R %H sleep 20";
+  private final static String DEFAULT_VIA_TEMPLATE
+    = "-f -L %L:%H:%R %G sleep 20";
+
+  public static void createTunnel(CConn cc, int localPort) throws Exception {
+    int remotePort;
+    String gatewayHost;
+    String remoteHost;
+
+    remotePort = cc.getServerPort();
+    if (cc.viewer.tunnel.getValue()) {
+      gatewayHost = cc.getServerName();
+      remoteHost = "localhost";
+    } else {
+      gatewayHost = getSshHost(cc);
+      remoteHost = cc.getServerName();
+    }
+
+    String pattern = cc.viewer.extSSHArgs.getValue();
+    if (pattern == null) {
+      if (cc.viewer.tunnel.getValue())
+        pattern = System.getProperty("VNC_TUNNEL_CMD");
+      else
+        pattern = System.getProperty("VNC_VIA_CMD");
+    }
+
+    if (cc.viewer.extSSH.getValue() || 
+        (pattern != null && pattern.length() > 0)) {
+      createTunnelExt(gatewayHost, remoteHost, remotePort, localPort, pattern, cc);
+    } else {
+      createTunnelJSch(gatewayHost, remoteHost, remotePort, localPort, cc);
+    }
+  }
+
+  private static class MyJSchLogger implements Logger {
+    public boolean isEnabled(int level){
+      return true;
+    }
+
+    public void log(int level, String msg){
+      switch (level) {
+      case Logger.INFO:
+        vlog.info(msg);
+        break;
+      case Logger.ERROR:
+        vlog.error(msg);
+        break;
+      default:
+        vlog.debug(msg);
+      }
+    }
+  }
+
+  public static String getSshHost(CConn cc) {
+    String sshHost = cc.viewer.via.getValue();
+    if (sshHost == null)
+      return cc.getServerName();
+    int end = sshHost.indexOf(":");
+    if (end < 0)
+      end = sshHost.length();
+    sshHost = sshHost.substring(sshHost.indexOf("@")+1, end);
+    return sshHost;
+  }
+
+  public static String getSshUser(CConn cc) {
+    String sshUser = (String)System.getProperties().get("user.name");
+    String via = cc.viewer.via.getValue();
+    if (via != null && via.indexOf("@") > 0)
+      sshUser = via.substring(0, via.indexOf("@"));
+    return sshUser;
+  }
+
+  public static int getSshPort(CConn cc) {
+    String sshPort = "22";
+    String via = cc.viewer.via.getValue();
+    if (via != null && via.indexOf(":") > 0)
+      sshPort = via.substring(via.indexOf(":")+1, via.length());
+    return Integer.parseInt(sshPort);
+  }
+
+  public static String getSshKeyFile(CConn cc) {
+    if (cc.viewer.sshKeyFile.getValue() != null)
+      return cc.viewer.sshKeyFile.getValue();
+    String[] ids = { "id_dsa", "id_rsa" };
+    for (String id : ids) {
+      File f = new File(FileUtils.getHomeDir()+".ssh/"+id);
+      if (f.exists() && f.canRead())
+        return(f.getAbsolutePath());
+    }
+    return null;
+  }
+
+  private static void createTunnelJSch(String gatewayHost, String remoteHost,
+                                       int remotePort, int localPort,
+                                       CConn cc) throws Exception {
+    JSch.setLogger(new MyJSchLogger());
+    JSch jsch=new JSch();
+
+    try {
+      // NOTE: jsch does not support all ciphers.  User may be
+      //       prompted to accept host key authenticy even if
+      //       the key is in the known_hosts file.
+      File knownHosts = new File(FileUtils.getHomeDir()+".ssh/known_hosts");
+      if (knownHosts.exists() && knownHosts.canRead())
+  	    jsch.setKnownHosts(knownHosts.getAbsolutePath());
+      ArrayList<File> privateKeys = new ArrayList<File>();
+      String sshKeyFile = cc.options.sshKeyFile.getText();
+      String sshKey = cc.viewer.sshKey.getValue();
+      if (sshKey != null) {
+        String sshKeyPass = cc.viewer.sshKeyPass.getValue();
+        byte[] keyPass = null, key;
+        if (sshKeyPass != null)
+          keyPass = sshKeyPass.getBytes();
+        sshKey = sshKey.replaceAll("\\\\n", "\n");
+        key = sshKey.getBytes();
+        jsch.addIdentity("TigerVNC", key, null, keyPass);
+      } else if (!sshKeyFile.equals("")) {
+        File f = new File(sshKeyFile);
+        if (!f.exists() || !f.canRead())
+          throw new Exception("Cannot access SSH key file "+ sshKeyFile);
+        privateKeys.add(f);
+      }
+      for (Iterator<File> i = privateKeys.iterator(); i.hasNext();) {
+        File privateKey = (File)i.next();
+        if (privateKey.exists() && privateKey.canRead())
+          if (cc.viewer.sshKeyPass.getValue() != null)
+  	        jsch.addIdentity(privateKey.getAbsolutePath(),
+                             cc.viewer.sshKeyPass.getValue());
+          else
+  	        jsch.addIdentity(privateKey.getAbsolutePath());
+      }
+  
+      String user = getSshUser(cc);
+      String label = new String("SSH Authentication");
+      PasswdDialog dlg =
+        new PasswdDialog(label, (user == null ? false : true), false);
+      dlg.userEntry.setText(user != null ? user : "");
+      File ssh_config = new File(cc.viewer.sshConfig.getValue());
+      if (ssh_config.exists() && ssh_config.canRead()) {
+        ConfigRepository repo =
+          OpenSSHConfig.parse(ssh_config.getAbsolutePath());
+        jsch.setConfigRepository(repo);
+      }
+      Session session=jsch.getSession(user, gatewayHost, getSshPort(cc));
+      session.setUserInfo(dlg);
+      // OpenSSHConfig doesn't recognize StrictHostKeyChecking
+      if (session.getConfig("StrictHostKeyChecking") == null)
+        session.setConfig("StrictHostKeyChecking", "ask");
+      session.connect();
+      session.setPortForwardingL(localPort, remoteHost, remotePort);
+    } catch (java.lang.Exception e) {
+      throw new Exception(e.getMessage()); 
+    }
+  }
+
+  private static class MyExtProcess implements Runnable {
+
+    private String cmd = null;
+    private Process pid = null;
+
+    private static class MyProcessLogger extends Thread {
+      private final BufferedReader err;
+  
+      public MyProcessLogger(Process p) {
+        InputStreamReader reader = 
+          new InputStreamReader(p.getErrorStream());
+        err = new BufferedReader(reader);
+      }
+  
+      @Override
+      public void run() {
+        try {
+          while (true) {
+            String msg = err.readLine();
+            if (msg != null)
+              vlog.info(msg);
+          }
+        } catch(java.io.IOException e) {
+          vlog.info(e.getMessage());
+        } finally {
+          try {
+            if (err != null)
+              err.close();
+          } catch (java.io.IOException e ) { }
+        }
+      }
+    }
+
+    private static class MyShutdownHook extends Thread {
+
+      private Process proc = null;
+
+      public MyShutdownHook(Process p) {
+        proc = p;
+      }
+  
+      @Override
+      public void run() {
+        try {
+          proc.exitValue();
+        } catch (IllegalThreadStateException e) {
+          try {
+            // wait for CConn to shutdown the socket
+            Thread.sleep(500);
+          } catch(InterruptedException ie) { }
+          proc.destroy();
+        }
+      }
+    }
+
+    public MyExtProcess(String command) {
+      cmd = command;  
+    }
+
+    public void run() {
+      try {
+        Runtime runtime = Runtime.getRuntime();
+        pid = runtime.exec(cmd);
+        runtime.addShutdownHook(new MyShutdownHook(pid));
+        new MyProcessLogger(pid).start();
+        pid.waitFor();
+      } catch(InterruptedException e) {
+        vlog.info(e.getMessage());
+      } catch(java.io.IOException e) {
+        vlog.info(e.getMessage());
+      }
+    }
+  }
+
+  private static void createTunnelExt(String gatewayHost, String remoteHost,
+                                      int remotePort, int localPort,
+                                      String pattern, CConn cc) throws Exception {
+    if (pattern == null || pattern.length() < 1) {
+      if (cc.viewer.tunnel.getValue())
+        pattern = DEFAULT_TUNNEL_TEMPLATE;
+      else
+        pattern = DEFAULT_VIA_TEMPLATE;
+    }
+    String cmd = fillCmdPattern(pattern, gatewayHost, remoteHost,
+                                remotePort, localPort, cc);
+    try {
+      Thread t = new Thread(new MyExtProcess(cmd));
+      t.start();
+      // wait for the ssh process to start
+      Thread.sleep(1000);
+    } catch (java.lang.Exception e) {
+      throw new Exception(e.getMessage());
+    }
+  }
+
+  private static String fillCmdPattern(String pattern, String gatewayHost,
+                                       String remoteHost, int remotePort,
+                                       int localPort, CConn cc) {
+    boolean H_found = false, G_found = false, R_found = false, L_found = false;
+    boolean P_found = false;
+    String cmd = cc.options.sshClient.getText() + " ";
+    pattern.replaceAll("^\\s+", "");
+
+    String user = getSshUser(cc);
+    int sshPort = getSshPort(cc);
+    gatewayHost = user + "@" + gatewayHost;
+
+    for (int i = 0; i < pattern.length(); i++) {
+      if (pattern.charAt(i) == '%') {
+        switch (pattern.charAt(++i)) {
+        case 'H':
+          cmd += (cc.viewer.tunnel.getValue() ? gatewayHost : remoteHost);
+  	      H_found = true;
+          continue;
+        case 'G':
+          cmd += gatewayHost;
+  	      G_found = true;
+  	      continue;
+        case 'R':
+          cmd += remotePort;
+  	      R_found = true;
+  	      continue;
+        case 'L':
+          cmd += localPort;
+  	      L_found = true;
+  	      continue;
+        case 'P':
+          cmd += sshPort;
+  	      P_found = true;
+  	      continue;
+        }
+      }
+      cmd += pattern.charAt(i);
+    }
+
+    if (pattern.length() > 1024)
+      throw new Exception("Tunneling command is too long.");
+
+    if (!H_found || !R_found || !L_found)
+      throw new Exception("%H, %R or %L absent in tunneling command template.");
+
+    if (!cc.viewer.tunnel.getValue() && !G_found)
+      throw new Exception("%G pattern absent in tunneling command template.");
+
+    vlog.info("SSH command line: "+cmd);
+    if (VncViewer.os.startsWith("windows"))
+      cmd.replaceAll("\\\\", "\\\\\\\\");
+    return cmd;
+  }
+
+  static LogWriter vlog = new LogWriter("Tunnel");
+}
diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java
index 3179971..fc9c7b5 100644
--- a/java/com/tigervnc/vncviewer/VncViewer.java
+++ b/java/com/tigervnc/vncviewer/VncViewer.java
@@ -168,13 +168,6 @@
         continue;
       }
 
-      if (argv[i].equalsIgnoreCase("-tunnel") || argv[i].equalsIgnoreCase("-via")) {
-        if (!tunnel.createTunnel(argv.length, argv, i))
-          exit(1);
-        if (argv[i].equalsIgnoreCase("-via")) i++;
-        continue;
-      }
-
       if (Configuration.setParam(argv[i]))
         continue;
 
@@ -614,30 +607,71 @@
                       "Produce a system beep when requested to by the server.",
                       true);
   StringParameter via
-  = new StringParameter("via",
+  = new StringParameter("Via",
     "Automatically create an encrypted TCP tunnel to "+
-    "machine gateway, then use that tunnel to connect "+
-    "to a VNC server running on host. By default, "+
-    "this option invokes SSH local port forwarding and "+
-    "assumes that the SSH client binary is located at "+
-    "/usr/bin/ssh. Note that when using the -via "+
-    "option, the host machine name should be specified "+
-    "from the point of view of the gateway machine. "+
-    "For example, \"localhost\" denotes the gateway, "+
-    "not the machine on which vncviewer was launched. "+
+    "the gateway machine, then connect to the VNC host "+
+    "through that tunnel. By default, this option invokes "+
+    "SSH local port forwarding using the embedded JSch "+
+    "client, however an external SSH client may be specified "+
+    "using the \"-extSSH\" parameter. Note that when using "+
+    "the -via option, the VNC host machine name should be "+
+    "specified from the point of view of the gateway machine, "+
+    "e.g. \"localhost\" denotes the gateway, "+
+    "not the machine on which the viewer was launched. "+
     "See the System Properties section below for "+
-    "information on configuring the -via option.",
+    "information on configuring the -Via option.", null);
+  BoolParameter tunnel
+  = new BoolParameter("Tunnel",
+    "The -Tunnel command is basically a shorthand for the "+
+    "-via command when the VNC server and SSH gateway are "+
+    "one and the same. -Tunnel creates an SSH connection "+
+    "to the server and forwards the VNC through the tunnel "+
+    "without the need to specify anything else.", false);
+  BoolParameter extSSH
+  = new BoolParameter("extSSH",
+    "By default, SSH tunneling uses the embedded JSch client "+
+    "for tunnel creation. This option causes the client to "+
+    "invoke an external SSH client application for all tunneling "+
+    "operations. By default, \"/usr/bin/ssh\" is used, however "+
+    "the path to the external application may be specified using "+
+    "the -SSHClient option.", false);
+  StringParameter extSSHClient
+  = new StringParameter("extSSHClient",
+    "Specifies the path to an external SSH client application "+
+    "that is to be used for tunneling operations when the -extSSH "+
+    "option is in effect.", "/usr/bin/ssh");
+  StringParameter extSSHArgs
+  = new StringParameter("extSSHArgs",
+    "Specifies the arguments string or command template to be used "+
+    "by the external SSH client application when the -extSSH option "+
+    "is in effect. The string will be processed according to the same "+
+    "pattern substitution rules as the VNC_TUNNEL_CMD and VNC_VIA_CMD "+
+    "system properties, and can be used to override those in a more "+
+    "command-line friendly way. If not specified, then the appropriate "+
+    "VNC_TUNNEL_CMD or VNC_VIA_CMD command template will be used.", null);
+  StringParameter sshConfig
+  = new StringParameter("SSHConfig",
+    "Specifies the path to an OpenSSH configuration file that to "+
+    "be parsed by the embedded JSch SSH client during tunneling "+
+    "operations.", FileUtils.getHomeDir()+".ssh/config");
+  StringParameter sshKey
+  = new StringParameter("SSHKey",
+    "When using the Via or Tunnel options with the embedded SSH client, "+
+    "this parameter specifies the text of the SSH private key to use when "+
+    "authenticating with the SSH server. You can use \\n within the string "+
+    "to specify a new line.", null);
+  StringParameter sshKeyFile
+  = new StringParameter("SSHKeyFile",
+    "When using the Via or Tunnel options with the embedded SSH client, "+
+    "this parameter specifies a file that contains an SSH private key "+
+    "(or keys) to use when authenticating with the SSH server. If not "+
+    "specified, ~/.ssh/id_dsa or ~/.ssh/id_rsa will be used (if they exist). "+
+    "Otherwise, the client will fallback to prompting for an SSH password.",
     null);
-
-  StringParameter tunnelMode
-  = new StringParameter("tunnel",
-    "Automatically create an encrypted TCP tunnel to "+
-    "remote gateway, then use that tunnel to connect "+
-    "to the specified VNC server port on the remote "+
-    "host. See the System Properties section below "+
-    "for information on configuring the -tunnel option.",
-    null);
-
+  StringParameter sshKeyPass
+  = new StringParameter("SSHKeyPass",
+    "When using the Via or Tunnel options with the embedded SSH client, "+
+    "this parameter specifies the passphrase for the SSH key.", null);
   BoolParameter customCompressLevel
   = new BoolParameter("CustomCompressLevel",
                       "Use custom compression level. "+
diff --git a/java/com/tigervnc/vncviewer/tunnel.java b/java/com/tigervnc/vncviewer/tunnel.java
deleted file mode 100644
index 6d55fec..0000000
--- a/java/com/tigervnc/vncviewer/tunnel.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- *  Copyright (C) 2012 Brian P. Hinz.  All Rights Reserved.
- *  Copyright (C) 2000 Const Kaplinsky.  All Rights Reserved.
- *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
- *
- *  This is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This software is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this software; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- *  USA.
- */
-
-/*
- * tunnel.java - SSH tunneling support
- */
-
-package com.tigervnc.vncviewer;
-
-import java.io.File;
-import java.lang.Character;
-import java.util.ArrayList;
-import java.util.Iterator;
-
-import com.tigervnc.rdr.*;
-import com.tigervnc.rfb.*;
-import com.tigervnc.network.*;
-
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.Session;
-
-public class tunnel
-{
-  private final static Integer SERVER_PORT_OFFSET = 5900;;
-  private final static String DEFAULT_SSH_CMD = "/usr/bin/ssh";
-  private final static String DEFAULT_TUNNEL_CMD
-    = DEFAULT_SSH_CMD+" -f -L %L:localhost:%R %H sleep 20";
-  private final static String DEFAULT_VIA_CMD
-    = DEFAULT_SSH_CMD+" -f -L %L:%H:%R %G sleep 20";
-
-  private final static int H = 17;
-  private final static int G = 16;
-  private final static int R = 27;
-  private final static int L = 21;
-
-  /* True if there was -tunnel or -via option in the command line. */
-  private static boolean tunnelSpecified = false;
-
-  /* True if it was -tunnel, not -via option. */
-  private static boolean tunnelOption = false;
-
-  /* "Hostname:display" pair in the command line will be substituted
-     by this fake argument when tunneling is used. */
-  private static String lastArgv;
-
-  private static String tunnelEndpoint;
-
-  public static Boolean
-  createTunnel(int pargc, String[] argv, int tunnelArgIndex)
-  {
-    char[] pattern;
-    char[] cmd = new char[1024];
-    int[] localPort = new int[1];
-    int[] remotePort = new int[1];
-    char[] localPortStr = new char[8];
-    char[] remotePortStr = new char[8];
-    StringBuilder gatewayHost = new StringBuilder("");
-    StringBuilder remoteHost = new StringBuilder("localhost");
-
-    tunnelSpecified = true;
-    if (argv[tunnelArgIndex].equalsIgnoreCase("-tunnel"))
-      tunnelOption = true;
-
-    pattern = getCmdPattern();
-    if (pattern == null)
-      return false;
-
-    localPort[0] = TcpSocket.findFreeTcpPort();
-    if (localPort[0] == 0)
-      return false;
-
-    if (tunnelOption) {
-      processTunnelArgs(remoteHost, remotePort, localPort,
-  		      pargc, argv, tunnelArgIndex);
-    } else {
-      processViaArgs(gatewayHost, remoteHost, remotePort, localPort,
-  		   pargc, argv, tunnelArgIndex);
-    }
-
-    localPortStr = Integer.toString(localPort[0]).toCharArray();
-    remotePortStr = Integer.toString(remotePort[0]).toCharArray();
-
-    if (!fillCmdPattern(cmd, pattern, gatewayHost.toString().toCharArray(),
-		remoteHost.toString().toCharArray(), remotePortStr, localPortStr))
-      return false;
-
-    if (!runCommand(new String(cmd)))
-      return false;
-
-    return true;
-  }
-
-  private static void
-  processTunnelArgs(StringBuilder remoteHost, int[] remotePort,
-                    int[] localPort, int pargc, String[] argv,
-                    int tunnelArgIndex)
-  {
-    String pdisplay;
-
-    if (tunnelArgIndex >= pargc - 1)
-      VncViewer.usage();
-
-    pdisplay = argv[pargc - 1].split(":")[1];
-    if (pdisplay == null || pdisplay == argv[pargc - 1])
-      VncViewer.usage();
-
-    if (pdisplay.matches("/[^0-9]/"))
-      VncViewer.usage();
-
-    remotePort[0] = Integer.parseInt(pdisplay);
-    if (remotePort[0] < 100)
-      remotePort[0] = remotePort[0] + SERVER_PORT_OFFSET;
-
-    lastArgv = new String("localhost::"+localPort[0]);
-
-    remoteHost.setLength(0);
-    remoteHost.insert(0, argv[pargc - 1].split(":")[0]);
-    argv[pargc - 1] = lastArgv;
-
-    //removeArgs(pargc, argv, tunnelArgIndex, 1);
-  }
-
-  private static void
-  processViaArgs(StringBuilder gatewayHost, StringBuilder remoteHost,
-  	       int[] remotePort, int[] localPort,
-  	       int pargc, String[] argv, int tunnelArgIndex)
-  {
-    String colonPos;
-    int len, portOffset;
-    int disp;
-
-    if (tunnelArgIndex >= pargc - 2)
-      VncViewer.usage();
-
-    colonPos = argv[pargc - 1].split(":", 2)[1];
-    if (colonPos == null) {
-      /* No colon -- use default port number */
-      remotePort[0] = SERVER_PORT_OFFSET;
-    } else {
-      len = colonPos.length();
-      portOffset = SERVER_PORT_OFFSET;
-      if (colonPos.startsWith(":")) {
-        /* Two colons -- interpret as a port number */
-        colonPos.replaceFirst(":", "");
-        len--;
-        portOffset = 0;
-      }
-      if (len == 0 || colonPos.matches("/[^0-9]/")) {
-        VncViewer.usage();
-      }
-      disp = Integer.parseInt(colonPos);
-      if (portOffset != 0 && disp >= 100)
-        portOffset = 0;
-      remotePort[0] = disp + portOffset;
-    }
-
-    lastArgv = "localhost::"+localPort[0];
-
-    gatewayHost.setLength(0);
-    gatewayHost.insert(0, argv[tunnelArgIndex + 1]);
-
-    if (!argv[pargc - 1].split(":", 2)[0].equals("")) {
-      remoteHost.setLength(0);
-      remoteHost.insert(0, argv[pargc - 1].split(":", 2)[0]);
-    }
-
-    argv[pargc - 1] = lastArgv;
-
-    //removeArgs(pargc, argv, tunnelArgIndex, 2);
-  }
-
-  private static char[]
-  getCmdPattern()
-  {
-    String pattern = "";
-
-    try {
-      if (tunnelOption) {
-        pattern = System.getProperty("VNC_TUNNEL_CMD");
-      } else {
-        pattern = System.getProperty("VNC_VIA_CMD");
-      }
-    } catch (java.lang.Exception e) {
-      vlog.info(e.toString());
-    }
-    if (pattern == null || pattern.equals(""))
-      pattern = (tunnelOption) ? DEFAULT_TUNNEL_CMD : DEFAULT_VIA_CMD;
-
-    return pattern.toCharArray();
-  }
-
-  /* Note: in fillCmdPattern() result points to a 1024-byte buffer */
-
-  private static boolean
-  fillCmdPattern(char[] result, char[] pattern,
-  	       char[] gatewayHost, char[] remoteHost,
-  	       char[] remotePort, char[] localPort)
-  {
-    int i, j;
-    boolean H_found = false, G_found = false, R_found = false, L_found = false;
-
-    for (i=0, j=0; i < pattern.length && j<1023; i++, j++) {
-      if (pattern[i] == '%') {
-        switch (pattern[++i]) {
-        case 'H':
-  	System.arraycopy(remoteHost, 0, result, j, remoteHost.length);
-  	j += remoteHost.length;
-  	H_found = true;
-        tunnelEndpoint = new String(remoteHost);
-  	continue;
-        case 'G':
-  	System.arraycopy(gatewayHost, 0, result, j, gatewayHost.length);
-  	j += gatewayHost.length;
-  	G_found = true;
-        tunnelEndpoint = new String(gatewayHost);
-  	continue;
-        case 'R':
-  	System.arraycopy(remotePort, 0, result, j, remotePort.length);
-  	j += remotePort.length;
-  	R_found = true;
-  	continue;
-        case 'L':
-  	System.arraycopy(localPort, 0, result, j, localPort.length);
-  	j += localPort.length;
-  	L_found = true;
-  	continue;
-        case '\0':
-  	i--;
-  	continue;
-        }
-      }
-      result[j] = pattern[i];
-    }
-
-    if (pattern.length > 1024) {
-      vlog.error("Tunneling command is too long.");
-      return false;
-    }
-
-    if (!H_found || !R_found || !L_found) {
-      vlog.error("%H, %R or %L absent in tunneling command.");
-      return false;
-    }
-    if (!tunnelOption && !G_found) {
-      vlog.error("%G pattern absent in tunneling command.");
-      return false;
-    }
-
-    return true;
-  }
-
-  private static Boolean
-  runCommand(String cmd)
-  {
-    try{
-      JSch jsch=new JSch();
-      String homeDir = new String("");
-      try {
-        homeDir = System.getProperty("user.home");
-      } catch(java.security.AccessControlException e) {
-        System.out.println("Cannot access user.home system property");
-      }
-      // NOTE: jsch does not support all ciphers.  User may be
-      //       prompted to accept host key authenticy even if
-      //       the key is in the known_hosts file.
-      File knownHosts = new File(homeDir+"/.ssh/known_hosts");
-      if (knownHosts.exists() && knownHosts.canRead())
-	      jsch.setKnownHosts(knownHosts.getAbsolutePath());
-      ArrayList<File> privateKeys = new ArrayList<File>();
-      privateKeys.add(new File(homeDir+"/.ssh/id_rsa"));
-      privateKeys.add(new File(homeDir+"/.ssh/id_dsa"));
-      for (Iterator<File> i = privateKeys.iterator(); i.hasNext();) {
-        File privateKey = (File)i.next();
-        if (privateKey.exists() && privateKey.canRead())
-	        jsch.addIdentity(privateKey.getAbsolutePath());
-      }
-      // username and passphrase will be given via UserInfo interface.
-      PasswdDialog dlg = new PasswdDialog(new String("SSH Authentication"), false, false);
-      dlg.promptPassword(new String("SSH Authentication"));
-
-      Session session=jsch.getSession(dlg.userEntry.getText(), tunnelEndpoint, 22);
-      session.setPassword(new String(dlg.passwdEntry.getPassword()));
-      session.connect();
-
-      String[] tokens = cmd.split("\\s");
-      for (int i = 0; i < tokens.length; i++) {
-        if (tokens[i].equals("-L")) {
-          String[] par = tokens[++i].split(":");
-          int localPort = Integer.parseInt(par[0].trim());
-          String remoteHost = par[1].trim();
-          int remotePort = Integer.parseInt(par[2].trim());
-          session.setPortForwardingL(localPort, remoteHost, remotePort);
-        } else if (tokens[i].equals("-R")) {
-          String[] par = tokens[++i].split(":");
-          int remotePort = Integer.parseInt(par[0].trim());
-          String localHost = par[1].trim();
-          int localPort = Integer.parseInt(par[2].trim());
-          session.setPortForwardingR(remotePort, localHost, localPort);
-        }
-      }
-    } catch (java.lang.Exception e) {
-      System.out.println(" Tunneling command failed: "+e.toString());
-      return false;
-    }
-    return true;
-  }
-
-  static LogWriter vlog = new LogWriter("tunnel");
-}
diff --git a/po/ru.po b/po/ru.po
index 89db1de..a35981e 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -1,780 +1,719 @@
-# Copyright (C) 2011 TigerVNC Team
+# Russian translation for tigervnc.
+# Copyright © 2016 the TigerVNC Team (msgids)
+# This file is distributed under the same license as the tigervnc package.
+# Constantin Kaplinsky <const@tightvnc.com>, 2011.
+# PuppyRus linux team <www.puppyrus.org>.
+# Pavel Maryanov <acid@jack.kiev.ua>, 2016.
 msgid ""
 msgstr ""
-"Project-Id-Version: TigerVNC 0.0.91\n"
+"Project-Id-Version: tigervnc 1.5.90\n"
 "Report-Msgid-Bugs-To: tigervnc-devel@googlegroups.com\n"
-"POT-Creation-Date: 2014-09-22 11:15+0000\n"
-"PO-Revision-Date: 2011-11-24 13:31+0100\n"
-"Last-Translator: Constantin Kaplinsky <const@tightvnc.com>\n"
-"Language-Team: Russian\n"
-"Language: \n"
+"POT-Creation-Date: 2015-11-26 11:33+0000\n"
+"PO-Revision-Date: 2016-04-07 16:08+0300\n"
+"Last-Translator: Pavel Maryanov <acid@jack.kiev.ua>\n"
+"Language-Team: Russian <gnu@d07.ru>\n"
+"Language: ru_UA\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Poedit 1.8.7.1\n"
 
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:194
+#: vncviewer/CConn.cxx:111
 #, c-format
-msgid "(server default %s)"
-msgstr ""
+msgid "connected to host %s port %d"
+msgstr "подключен к компьютеру %s, порт %d"
 
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:227
-msgid "About"
-msgstr "О программе"
-
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:103
-#, fuzzy
-msgid "About TigerVNC Viewer"
-msgstr "О VNC Viewer"
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1086
-#, fuzzy
-msgid "About TigerVNC viewer..."
-msgstr "О VNC Viewer..."
-
-#: /home/ossman/devel/tigervnc/vncviewer/ServerDialog.cxx:86
-msgid "About..."
-msgstr "О программе..."
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:704
-msgid "Accept clipboard from server"
-msgstr "Принимать буфер обмена с сервера"
-
-#: /home/ossman/devel/tigervnc/vncviewer/DesktopWindow.cxx:327
-msgid "Adjusting window size to avoid accidental full screen request"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:563
-msgid "Allow JPEG compression:"
-msgstr "Разрешить сжатие JPEG:"
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1070
-msgid "Alt"
-msgstr "Alt"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:648
-msgid "Authentication"
-msgstr "Аутентификация"
-
-#: /home/ossman/devel/tigervnc/vncviewer/UserDialog.cxx:89
-msgid "Authentication cancelled"
-msgstr "Аутентификация отменена"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:437
-msgid "Auto select"
-msgstr "Автоматический выбор"
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:651
-#, c-format
-msgid "Bad Name/Value pair on line: %d in file: %s"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/Win32PixelBuffer.cxx:92
-msgid "BitBlt failed"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/ServerDialog.cxx:91
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:221
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:83
-msgid "Cancel"
-msgstr "Отмена"
-
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:116
-msgid "CleanupSignalHandler called"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:222
-msgid "Close"
-msgstr "Закрыть"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:499
-msgid "Color level"
-msgstr "Уровень цветности"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:421
-#, fuzzy
-msgid "Compression"
-msgstr "Нестандартный уровень сжатия:"
-
-#: /home/ossman/devel/tigervnc/vncviewer/ServerDialog.cxx:96
-msgid "Connect"
-msgstr "Подключ."
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1085
-msgid "Connection info..."
-msgstr "Информация о соединении..."
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:362
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:404
-#, c-format
-msgid ""
-"Could not convert the parameter-name %s to wchar_t* when reading from the "
-"Registry, the buffersize is to small."
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:302
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:340
-#, c-format
-msgid ""
-"Could not convert the parameter-name %s to wchar_t* when writing to the "
-"Registry, the buffersize is to small."
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:318
-#, c-format
-msgid ""
-"Could not convert the parameter-value %s to wchar_t* when writing to the "
-"Registry, the buffersize is to small."
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:381
-#, c-format
-msgid ""
-"Could not convert the parameter-value for %s to utf8 char* when reading from "
-"the Registry, the buffer dest is to small."
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:256
-#, c-format
-msgid "Could not create VNC home directory: %s."
-msgstr "Не удается создать домашний каталог VNC: %s."
-
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:251
-msgid "Could not create VNC home directory: can't obtain home directory path."
-msgstr ""
-"Не удается создать домашний каталог VNC: не удается получить путь к "
-"домашнему каталогу."
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:310
-#, c-format
-msgid "Could not encode the parameter-value %s when writing to the Registry."
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:613
-#, c-format
-msgid ""
-"Could not read the line(%d) in the configuration file,the buffersize is to "
-"small."
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/Win32PixelBuffer.cxx:80
-msgid "CreateCompatibleDC failed"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1068
-msgid "Ctrl"
-msgstr "Ctrl"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:550
-msgid "Custom compression level:"
-msgstr "Нестандартный уровень сжатия:"
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:283
-msgid ""
-"Decoding: The size of the buffer dest is to small, it needs to be 1 byte "
-"bigger."
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:172
+#: vncviewer/CConn.cxx:173
 #, c-format
 msgid "Desktop name: %.80s"
-msgstr ""
+msgstr "Имя компьютера: %.80s"
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1088
-msgid "Dismiss menu"
-msgstr "Закрыть меню"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:778
-msgid "Enable full-screen mode over all monitors"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:469
-msgid "Enabling continuous updates"
-msgstr "Включение регулярного обновления"
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:200
-#, c-format
-msgid ""
-"Encoding backslash: The size of the buffer dest is to small, it needs to be "
-"more than %d bytes bigger."
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:214
-#, c-format
-msgid ""
-"Encoding escape sequence: The size of the buffer dest is to small, it needs "
-"to be more than %d bytes bigger."
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:234
-#, c-format
-msgid ""
-"Encoding normal character: The size of the buffer dest is to small, it needs "
-"to be more than %d bytes bigger."
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:595
-msgid "Encryption"
-msgstr "Шифрование"
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:503
-#, c-format
-msgid "Error(%d) closing key:  Software\\TigerVNC\\vncviewer"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:455
-#, c-format
-msgid "Error(%d) closing key: Software\\TigerVNC\\vncviewer"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:434
-#, c-format
-msgid "Error(%d) creating key: Software\\TigerVNC\\vncviewer"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:471
-#, c-format
-msgid "Error(%d) opening key: Software\\TigerVNC\\vncviewer"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:373
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:415
-#, c-format
-msgid "Error(%d) reading %s from Registry."
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:348
-#, c-format
-msgid "Error(%d) writing %d(REG_DWORD) to Registry."
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:326
-#, c-format
-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
-msgid "Exit viewer"
-msgstr "Выход"
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:588
-#, fuzzy
-msgid "Failed to read configuration file, can't obtain home directory path."
-msgstr ""
-"Не удается создать домашний каталог VNC: не удается получить путь к "
-"домашнему каталогу."
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:602
-#, c-format
-msgid "Failed to read configuration file, can't open %s"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:621
-#, c-format
-msgid "Failed to read line %d in file %s"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:528
-#, fuzzy
-msgid "Failed to write configuration file, can't obtain home directory path."
-msgstr ""
-"Не удается создать домашний каталог VNC: не удается получить путь к "
-"домашнему каталогу."
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:542
-#, c-format
-msgid "Failed to write configuration file, can't open %s"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/DesktopWindow.cxx:526
-#: /home/ossman/devel/tigervnc/vncviewer/DesktopWindow.cxx:538
-#: /home/ossman/devel/tigervnc/vncviewer/DesktopWindow.cxx:551
-msgid "Failure grabbing keyboard"
-msgstr "Ошибка при перехвате для клавиатуры"
-
-#: /home/ossman/devel/tigervnc/vncviewer/DesktopWindow.cxx:563
-msgid "Failure grabbing mouse"
-msgstr "Ошибка при перехвате для мыши"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:512
-msgid "Full (all available colors)"
-msgstr "Многоцветный режим (все доступные цвета)"
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1059
-msgid "Full screen"
-msgstr "Полноэкранный режим"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:770
-msgid "Full-screen mode"
-msgstr "Полноэкранный режим"
-
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:230
-msgid "Hide"
-msgstr "Скрыть"
-
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:238
-msgid "Hide Others"
-msgstr "Скрыть прочее"
-
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:177
+#: vncviewer/CConn.cxx:178
 #, c-format
 msgid "Host: %.80s port: %d"
-msgstr ""
+msgstr "Компьютер: %.80s порт: %d"
 
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:690
-#, fuzzy
-msgid "Input"
-msgstr "Ввод:"
-
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:510
-msgid "Internal FLTK error. Exiting."
-msgstr "Внутренняя ошибка FLTK. Выполняется выход."
-
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:384
-msgid "Invalid SetColourMapEntries from server!"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/DesktopWindow.cxx:106
-msgid "Invalid geometry specified!"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:708
+#: vncviewer/CConn.cxx:183
 #, c-format
-msgid "Invalid parameter name on line: %d in file: %s"
-msgstr ""
+msgid "Size: %d x %d"
+msgstr "Размер: %d x %d"
 
-#: /home/ossman/devel/tigervnc/vncviewer/DesktopWindow.cxx:797
-msgid "Invalid screen layout computed for resize request!"
-msgstr ""
+#: vncviewer/CConn.cxx:191
+#, c-format
+msgid "Pixel format: %s"
+msgstr "Формат пикселей: %s"
 
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:204
-#, fuzzy, c-format
+#: vncviewer/CConn.cxx:198
+#, c-format
+msgid "(server default %s)"
+msgstr "(сервер по умолчанию %s)"
+
+#: vncviewer/CConn.cxx:203
+#, c-format
+msgid "Requested encoding: %s"
+msgstr "Запрошено кодирование: %s"
+
+#: vncviewer/CConn.cxx:208
+#, c-format
 msgid "Last used encoding: %s"
-msgstr "Использование кодирования %s"
+msgstr "Используется кодирование: %s"
 
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:631
-#, c-format
-msgid ""
-"Line 1 in file %s\n"
-"must contain the TigerVNC configuration file identifier string:\n"
-"\"%s\""
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:209
+#: vncviewer/CConn.cxx:213
 #, c-format
 msgid "Line speed estimate: %d kbit/s"
-msgstr ""
+msgstr "Скорость соединения: %d кбит/с"
 
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:478
-#, c-format
-msgid "Listening on port %d\n"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/ServerDialog.cxx:69
-msgid "Load..."
-msgstr "Загрузка..."
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:526
-msgid "Low (64 colors)"
-msgstr "Низкое качество цвета (64 цвета)"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:519
-msgid "Medium (256 colors)"
-msgstr "Среднее качество цвета (256 цветов)"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:725
-msgid "Menu key"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:791
-msgid "Misc."
-msgstr "Разное"
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1026
-#, c-format
-msgid "Multiple characters given for key code %d (0x%04x): '%s'"
-msgstr "Несколько символов для кода ключа %d (0x%04x): '%s'"
-
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:218
-msgid "No"
-msgstr "Нет"
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:688
-#, c-format
-msgid "No scan code for extended virtual key 0x%02x"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:690
-#, c-format
-msgid "No scan code for virtual key 0x%02x"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:701
-#, c-format
-msgid "No symbol for extended virtual key 0x%02x"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:765
-#, c-format
-msgid "No symbol for key code %d (in the current state)"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:739
-#, c-format
-msgid "No symbol for key code 0x%02x (in the current state)"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:703
-#, c-format
-msgid "No symbol for virtual key 0x%02x"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:606
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:659
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:727
-msgid "None"
-msgstr "Нет"
-
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:220
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:88
-msgid "OK"
-msgstr "OK"
-
-#: /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/ServerDialog.cxx:64
-msgid "Options..."
-msgstr "Параметры..."
-
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:463
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:464
-msgid "Parameters -listen and -via are incompatible"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:722
-msgid "Pass system keys directly to server (full screen)"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/UserDialog.cxx:87
-#: /home/ossman/devel/tigervnc/vncviewer/UserDialog.cxx:102
-msgid "Password:"
-msgstr "Пароль:"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:625
-msgid "Path to X509 CA certificate"
-msgstr "Путь к сертификату X509 CA"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:632
-msgid "Path to X509 CRL file"
-msgstr "Путь к файлу X509 CRL"
-
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:188
-#, fuzzy, c-format
-msgid "Pixel format: %s"
-msgstr "Использование формата точек %s"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:449
-msgid "Preferred encoding"
-msgstr "Предпочтительное кодирование"
-
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:214
+#: vncviewer/CConn.cxx:218
 #, c-format
 msgid "Protocol version: %d.%d"
-msgstr ""
+msgstr "Версия протокола: %d.%d"
 
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:233
-msgid "Quit"
-msgstr "Выход"
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1082
-msgid "Refresh screen"
-msgstr "Обновить экран"
-
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:199
-#, fuzzy, c-format
-msgid "Requested encoding: %s"
-msgstr "Предпочтительное кодирование"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:749
-msgid "Resize remote session on connect"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:762
-msgid "Resize remote session to the local window"
-msgstr "Изменить размер удалённой сессии до местного окна"
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1062
-msgid "Resize window to session"
-msgstr "Изменить размер окна сессии"
-
-#: /home/ossman/devel/tigervnc/vncviewer/ServerDialog.cxx:74
-msgid "Save As..."
-msgstr "Сохранить как..."
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:741
-msgid "Screen"
-msgstr "Экран"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:580
-msgid "Security"
-msgstr "Безопасность"
-
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:219
+#: vncviewer/CConn.cxx:223
 #, c-format
 msgid "Security method: %s"
-msgstr ""
+msgstr "Метод защиты: %s"
 
-#: /home/ossman/devel/tigervnc/vncviewer/Win32PixelBuffer.cxx:83
-msgid "SelectObject failed"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1075
-#, c-format
-msgid "Send %s"
-msgstr "Послать %s"
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1080
-msgid "Send Ctrl-Alt-Del"
-msgstr "Послать Ctrl-Alt-Del"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:710
-msgid "Send clipboard to server"
-msgstr "Посылать буфер обмена на сервер"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:716
-#, fuzzy
-msgid "Send primary selection and cut buffer as clipboard"
-msgstr "Посылать \"primary selection\" и \"cut buffer\" как буфер обмена"
-
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:237
-msgid "Services"
-msgstr "Службы"
-
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:319
+#: vncviewer/CConn.cxx:329
 #, c-format
 msgid "SetDesktopSize failed: %d"
 msgstr "Ошибка SetDesktopSize: %d"
 
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:799
-msgid "Shared (don't disconnect other viewers)"
-msgstr "Разделяемый режим (не отключать других клиентов)"
+#: vncviewer/CConn.cxx:398
+msgid "Invalid SetColourMapEntries from server!"
+msgstr "С сервера получен недопустмый SetColourMapEntries"
 
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:239
-msgid "Show All"
-msgstr "Показать все"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:805
-msgid "Show dot when no cursor"
-msgstr "Показывать точку при отсутствии курсора"
-
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:182
+#. TRANSLATORS: Refers to a VNC protocol encoding type
+#: vncviewer/CConn.cxx:444 vncviewer/CConn.cxx:451
 #, c-format
-msgid "Size: %d x %d"
-msgstr ""
+msgid "Unknown encoding %d"
+msgstr "Неизвестное кодирование %d"
 
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:665
-msgid "Standard VNC (insecure without encryption)"
-msgstr "Стандартный VNC (без защиты и шифрования)"
+#: vncviewer/CConn.cxx:445 vncviewer/CConn.cxx:452
+msgid "Unknown encoding"
+msgstr "Неизвестное кодирование"
 
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:618
-msgid "TLS with X509 certificates"
-msgstr "TLS с сертификатами X509"
+#: vncviewer/CConn.cxx:484
+msgid "Enabling continuous updates"
+msgstr "Включение непрерывного обновления"
 
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:612
+#: vncviewer/CConn.cxx:554
+#, c-format
+msgid "Throughput %d kbit/s - changing to quality %d"
+msgstr "Пропускная способность %d кбит/с. Установлено качество %d"
+
+#: vncviewer/CConn.cxx:576
+#, c-format
+msgid "Throughput %d kbit/s - full color is now %s"
+msgstr "Пропускная способность %d кбит/с. Глубина цвета %s"
+
+#: vncviewer/CConn.cxx:578
+msgid "disabled"
+msgstr "отключено"
+
+#: vncviewer/CConn.cxx:578
+msgid "enabled"
+msgstr "включено"
+
+#: vncviewer/CConn.cxx:588
+#, c-format
+msgid "Using %s encoding"
+msgstr "Используется кодирование %s"
+
+#: vncviewer/CConn.cxx:635
+#, c-format
+msgid "Using pixel format %s"
+msgstr "Используется формат пикселей %s"
+
+#: vncviewer/DesktopWindow.cxx:106
+msgid "Invalid geometry specified!"
+msgstr "Указан недопустимый размер экрана."
+
+#: vncviewer/DesktopWindow.cxx:309
+msgid "Adjusting window size to avoid accidental full screen request"
+msgstr "Зафиксировать размер окна, чтобы исключить переключения на полный экран"
+
+#: vncviewer/DesktopWindow.cxx:491 vncviewer/DesktopWindow.cxx:497
+#: vncviewer/DesktopWindow.cxx:510
+msgid "Failure grabbing keyboard"
+msgstr "Не удалось перехватить клавиатуру"
+
+#: vncviewer/DesktopWindow.cxx:522
+msgid "Failure grabbing mouse"
+msgstr "Не удалось перехватить мышь"
+
+#: vncviewer/DesktopWindow.cxx:752
+msgid "Invalid screen layout computed for resize request!"
+msgstr "Для запроса на изменение рамера рассчитан недопустимый макет экрана."
+
+#: vncviewer/FLTKPixelBuffer.cxx:33 vncviewer/OSXPixelBuffer.cxx:48
+#: vncviewer/X11PixelBuffer.cxx:113
+msgid "Not enough memory for framebuffer"
+msgstr "Недостаточно памяти для framebuffer"
+
+#: vncviewer/OSXPixelBuffer.cxx:52
+msgid "Could not create framebuffer device"
+msgstr "Не удалось создать устройство framebuffer"
+
+#: vncviewer/OSXPixelBuffer.cxx:58
+msgid "Could not create framebuffer bitmap"
+msgstr "Не удалось создать framebuffer bitmap"
+
+#: vncviewer/OptionsDialog.cxx:57
+msgid "VNC Viewer: Connection Options"
+msgstr "VNC Viewer: параметры соединения"
+
+#: vncviewer/OptionsDialog.cxx:83 vncviewer/ServerDialog.cxx:91
+#: vncviewer/vncviewer.cxx:268
+msgid "Cancel"
+msgstr "Отмена"
+
+#: vncviewer/OptionsDialog.cxx:88 vncviewer/vncviewer.cxx:267
+msgid "OK"
+msgstr "ОК"
+
+#: vncviewer/OptionsDialog.cxx:413
+msgid "Compression"
+msgstr "Сжатие"
+
+#: vncviewer/OptionsDialog.cxx:429
+msgid "Auto select"
+msgstr "Автоматический выбор"
+
+#: vncviewer/OptionsDialog.cxx:441
+msgid "Preferred encoding"
+msgstr "Вид кодирования"
+
+#: vncviewer/OptionsDialog.cxx:489
+msgid "Color level"
+msgstr "Глубина цвета"
+
+#: vncviewer/OptionsDialog.cxx:500
+msgid "Full (all available colors)"
+msgstr "Полная (все цвета)"
+
+#: vncviewer/OptionsDialog.cxx:507
+msgid "Medium (256 colors)"
+msgstr "Средняя (256 цветов)"
+
+#: vncviewer/OptionsDialog.cxx:514
+msgid "Low (64 colors)"
+msgstr "Низкая (64 цвета)"
+
+#: vncviewer/OptionsDialog.cxx:521
+msgid "Very low (8 colors)"
+msgstr "Минимум цвета (8 цветов)"
+
+#: vncviewer/OptionsDialog.cxx:538
+msgid "Custom compression level:"
+msgstr "Задать уровень сжатия:"
+
+#: vncviewer/OptionsDialog.cxx:544
+msgid "level (1=fast, 6=best [4-6 are rarely useful])"
+msgstr "уровень (1=мин, 6=макс. [4-6 используются редко])"
+
+#: vncviewer/OptionsDialog.cxx:551
+msgid "Allow JPEG compression:"
+msgstr "Разрешить сжатие JPEG:"
+
+#: vncviewer/OptionsDialog.cxx:557
+msgid "quality (0=poor, 9=best)"
+msgstr "качество (0=наихудшее, 9=наилучшее)"
+
+#: vncviewer/OptionsDialog.cxx:568
+msgid "Security"
+msgstr "Безопасность"
+
+#: vncviewer/OptionsDialog.cxx:583
+msgid "Encryption"
+msgstr "Шифрование"
+
+#: vncviewer/OptionsDialog.cxx:594 vncviewer/OptionsDialog.cxx:647
+#: vncviewer/OptionsDialog.cxx:715
+msgid "None"
+msgstr "Нет"
+
+#: vncviewer/OptionsDialog.cxx:600
 msgid "TLS with anonymous certificates"
 msgstr "TLS с анонимными сертификатами"
 
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:448
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:497
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:561
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:701
+#: vncviewer/OptionsDialog.cxx:606
+msgid "TLS with X509 certificates"
+msgstr "TLS с сертификатами X509"
+
+#: vncviewer/OptionsDialog.cxx:613
+msgid "Path to X509 CA certificate"
+msgstr "Путь к сертификату X509 CA"
+
+#: vncviewer/OptionsDialog.cxx:620
+msgid "Path to X509 CRL file"
+msgstr "Путь к файлу X509 CRL"
+
+#: vncviewer/OptionsDialog.cxx:636
+msgid "Authentication"
+msgstr "Авторизация"
+
+#: vncviewer/OptionsDialog.cxx:653
+msgid "Standard VNC (insecure without encryption)"
+msgstr "Стандартный VNC (без защиты и шифрования)"
+
+#: vncviewer/OptionsDialog.cxx:659
+msgid "Username and password (insecure without encryption)"
+msgstr "Имя пользователя и пароль (без защиты и шифрования)"
+
+#: vncviewer/OptionsDialog.cxx:678
+msgid "Input"
+msgstr "Ввод"
+
+#: vncviewer/OptionsDialog.cxx:686
+msgid "View only (ignore mouse and keyboard)"
+msgstr "Только просмотр (не перехватывать мышь и клавиатуру)"
+
+#: vncviewer/OptionsDialog.cxx:692
+msgid "Accept clipboard from server"
+msgstr "Принимать буфер обмена с сервера"
+
+#: vncviewer/OptionsDialog.cxx:698
+msgid "Send clipboard to server"
+msgstr "Отправлять буфер обмена на сервер"
+
+#: vncviewer/OptionsDialog.cxx:704
+msgid "Send primary selection and cut buffer as clipboard"
+msgstr "Отправлять выделение в терминале в буфер обмена сервера"
+
+#: vncviewer/OptionsDialog.cxx:710
+msgid "Pass system keys directly to server (full screen)"
+msgstr "Отправлять сочетания клавиш (для полного экрана)"
+
+#: vncviewer/OptionsDialog.cxx:713
+msgid "Menu key"
+msgstr "Вызов меню:"
+
+#: vncviewer/OptionsDialog.cxx:729
+msgid "Screen"
+msgstr "Экран"
+
+#: vncviewer/OptionsDialog.cxx:737
+msgid "Resize remote session on connect"
+msgstr "Изменить размер удалённого экрана"
+
+#: vncviewer/OptionsDialog.cxx:750
+msgid "Resize remote session to the local window"
+msgstr "Изменить размер удалённого сеанса до локального окна"
+
+#: vncviewer/OptionsDialog.cxx:756
+msgid "Full-screen mode"
+msgstr "Полноэкранный режим"
+
+#: vncviewer/OptionsDialog.cxx:762
+msgid "Enable full-screen mode over all monitors"
+msgstr "Расширить режим полного экрана на все мониторы"
+
+#: vncviewer/OptionsDialog.cxx:771
+msgid "Misc."
+msgstr "Разное"
+
+#: vncviewer/OptionsDialog.cxx:779
+msgid "Shared (don't disconnect other viewers)"
+msgstr "Совместная работа (не отключать других клиентов)"
+
+#: vncviewer/OptionsDialog.cxx:785
+msgid "Show dot when no cursor"
+msgstr "Показывать точку при отсутствии курсора"
+
+#: vncviewer/ServerDialog.cxx:42
+msgid "VNC Viewer: Connection Details"
+msgstr "VNC Viewer: информация о соединении"
+
+#: vncviewer/ServerDialog.cxx:49 vncviewer/ServerDialog.cxx:54
+msgid "VNC server:"
+msgstr "Сервер VNC:"
+
+#: vncviewer/ServerDialog.cxx:64
+msgid "Options..."
+msgstr "Параметры"
+
+#: vncviewer/ServerDialog.cxx:69
+msgid "Load..."
+msgstr "Загрузить"
+
+#: vncviewer/ServerDialog.cxx:74
+msgid "Save As..."
+msgstr "Сохранить"
+
+#: vncviewer/ServerDialog.cxx:86
+msgid "About..."
+msgstr "О программе"
+
+#: vncviewer/ServerDialog.cxx:96
+msgid "Connect"
+msgstr "Подключ."
+
+#: vncviewer/UserDialog.cxx:74
+msgid "Opening password file failed"
+msgstr "Не удалось открыть файл с паролем"
+
+#: vncviewer/UserDialog.cxx:86 vncviewer/UserDialog.cxx:96
+msgid "VNC authentication"
+msgstr "Авторизация VNC"
+
+#: vncviewer/UserDialog.cxx:87 vncviewer/UserDialog.cxx:102
+msgid "Password:"
+msgstr "Пароль:"
+
+#: vncviewer/UserDialog.cxx:89
+msgid "Authentication cancelled"
+msgstr "Авторизация отменена"
+
+#: vncviewer/UserDialog.cxx:99
+msgid "Username:"
+msgstr "Имя пользователя:"
+
+#: vncviewer/Viewport.cxx:433
 #, c-format
-msgid "The parameterArray contains a object of a invalid type at line %d."
-msgstr ""
+msgid "Unable to create platform specific framebuffer: %s"
+msgstr "Не удаётся создать framebuffer: %s"
 
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:664
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:680
+#: vncviewer/Viewport.cxx:434
+msgid "Using platform independent framebuffer"
+msgstr "Используется универсальный framebuffer"
+
+#: vncviewer/Viewport.cxx:668
 #, c-format
-msgid "The value of the parameter %s on line %d in file %s is invalid."
-msgstr ""
+msgid "No scan code for extended virtual key 0x%02x"
+msgstr "Нет скан-кода для дополнительной виртуальной клавиши 0x%02x"
 
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:539
+#: vncviewer/Viewport.cxx:670
 #, c-format
-msgid "Throughput %d kbit/s - changing to quality %d"
-msgstr "Пропускная способность %d Кбит/с - изменяется на качество %d"
+msgid "No scan code for virtual key 0x%02x"
+msgstr "Нет скан-кода для виртуальной клавиши 0x%02x"
 
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:561
+#: vncviewer/Viewport.cxx:687
 #, c-format
-msgid "Throughput %d kbit/s - full color is now %s"
-msgstr "Пропускная способность %d Кбит/с - теперь полноцветный режим %s"
+msgid "No symbol for extended virtual key 0x%02x"
+msgstr "Нет символа для расширенной виртуальной клавиши 0x%02x"
 
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:210
-#, fuzzy
-msgid "TigerVNC Viewer"
-msgstr "О VNC Viewer"
+#: vncviewer/Viewport.cxx:689
+#, c-format
+msgid "No symbol for virtual key 0x%02x"
+msgstr "Нет символа для виртуальной клавиши 0x%02x"
 
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:80
-#, fuzzy, c-format
+#: vncviewer/Viewport.cxx:727
+#, c-format
+msgid "No symbol for key code 0x%02x (in the current state)"
+msgstr "Нет символа для кода клавиши 0x%02x (в текущем состоянии)"
+
+#: vncviewer/Viewport.cxx:753
+#, c-format
+msgid "No symbol for key code %d (in the current state)"
+msgstr "Нет символа для кода клавиши %d (в текущем состоянии)"
+
+#: vncviewer/Viewport.cxx:790
+msgctxt "ContextMenu|"
+msgid "E&xit viewer"
+msgstr "В&ыход"
+
+#: vncviewer/Viewport.cxx:793
+msgctxt "ContextMenu|"
+msgid "&Full screen"
+msgstr "&Полный экран"
+
+#: vncviewer/Viewport.cxx:796
+msgctxt "ContextMenu|"
+msgid "Minimi&ze"
+msgstr "&Свернуть"
+
+#: vncviewer/Viewport.cxx:798
+msgctxt "ContextMenu|"
+msgid "Resize &window to session"
+msgstr "Изменить размер окна"
+
+#: vncviewer/Viewport.cxx:803
+msgctxt "ContextMenu|"
+msgid "&Ctrl"
+msgstr "&CTRL"
+
+#: vncviewer/Viewport.cxx:806
+msgctxt "ContextMenu|"
+msgid "&Alt"
+msgstr "&ALT"
+
+#: vncviewer/Viewport.cxx:812
+#, c-format
+msgctxt "ContextMenu|"
+msgid "Send %s"
+msgstr "Отправить %s"
+
+#: vncviewer/Viewport.cxx:818
+msgctxt "ContextMenu|"
+msgid "Send Ctrl-Alt-&Del"
+msgstr "Отправить CTRL-ALT-&DEL"
+
+#: vncviewer/Viewport.cxx:821
+msgctxt "ContextMenu|"
+msgid "&Refresh screen"
+msgstr "&Обновить экран"
+
+#: vncviewer/Viewport.cxx:824
+msgctxt "ContextMenu|"
+msgid "&Options..."
+msgstr "&Параметры"
+
+#: vncviewer/Viewport.cxx:826
+msgctxt "ContextMenu|"
+msgid "Connection &info..."
+msgstr "Сведения о соединении"
+
+#: vncviewer/Viewport.cxx:828
+msgctxt "ContextMenu|"
+msgid "About &TigerVNC viewer..."
+msgstr "О &TigerVNC viewer"
+
+#: vncviewer/Viewport.cxx:831
+msgctxt "ContextMenu|"
+msgid "Dismiss &menu"
+msgstr "Закрыть &меню"
+
+#: vncviewer/Viewport.cxx:915
+msgid "VNC connection info"
+msgstr "Сведения о соединении VNC"
+
+#: vncviewer/Win32PixelBuffer.cxx:62
+msgid "unable to create DIB section"
+msgstr "не удаётся создать выбор DIB"
+
+#: vncviewer/Win32PixelBuffer.cxx:79
+msgid "CreateCompatibleDC failed"
+msgstr "Ошибка CreateCompatibleDC"
+
+#: vncviewer/Win32PixelBuffer.cxx:82
+msgid "SelectObject failed"
+msgstr "Ошибка SelectObject"
+
+#: vncviewer/Win32PixelBuffer.cxx:91
+msgid "BitBlt failed"
+msgstr "Ошибка BitBlt"
+
+#. TRANSLATORS: "pixmap" is an X11 concept and may not be suitable
+#. to translate.
+#: vncviewer/X11PixelBuffer.cxx:61
+msgid "Display lacks pixmap format for default depth"
+msgstr "Неправильный формат pixmap для выбранной глубины цвета"
+
+#. TRANSLATORS: "pixmap" is an X11 concept and may not be suitable
+#. to translate.
+#: vncviewer/X11PixelBuffer.cxx:72
+msgid "Couldn't find suitable pixmap format"
+msgstr "Не удалось найти допустимый формат pixmap"
+
+#: vncviewer/X11PixelBuffer.cxx:81
+msgid "Only true colour displays supported"
+msgstr "Поддерживаются только полноцветные экраны"
+
+#: vncviewer/X11PixelBuffer.cxx:83
+#, c-format
+msgid "Using default colormap and visual, TrueColor, depth %d."
+msgstr "Используется стандартная цветовая карта и визуальное оформление, TrueColor, глубина %d."
+
+#: vncviewer/X11PixelBuffer.cxx:109
+msgid "Could not create framebuffer image"
+msgstr "Не удалось создать изображение в framebuffer"
+
+#: vncviewer/parameters.cxx:279 vncviewer/parameters.cxx:313
+#, c-format
+msgid "The name of the parameter %s was too large to write to the registry"
+msgstr "Название параметра %s слишком длинное для записи в реестр"
+
+#: vncviewer/parameters.cxx:285 vncviewer/parameters.cxx:292
+#, c-format
+msgid "The parameter %s was too large to write to the registry"
+msgstr "Параметр %s слишком длинный для записи в реестр"
+
+#: vncviewer/parameters.cxx:298 vncviewer/parameters.cxx:319
+#, c-format
+msgid "Failed to write parameter %s of type %s to the registry: %ld"
+msgstr "Не удалось записать параметр %s типа %s в реестр: %ld"
+
+#: vncviewer/parameters.cxx:334 vncviewer/parameters.cxx:373
+#, c-format
+msgid "The name of the parameter %s was too large to read from the registry"
+msgstr "Название параметра %s слишком длинное для чтения из реестра"
+
+#: vncviewer/parameters.cxx:343 vncviewer/parameters.cxx:382
+#, c-format
+msgid "Failed to read parameter %s from the registry: %ld"
+msgstr "Не удалось прочитать параметр %s из реестра: %ld"
+
+#: vncviewer/parameters.cxx:352
+#, c-format
+msgid "The parameter %s was too large to read from the registry"
+msgstr "Параметр %s слишком длинный для чтения из реестра"
+
+#: vncviewer/parameters.cxx:402
+#, c-format
+msgid "Failed to create registry key: %ld"
+msgstr "Не удалось создать ключ реестра: %ld"
+
+#: vncviewer/parameters.cxx:416 vncviewer/parameters.cxx:465
+#: vncviewer/parameters.cxx:527 vncviewer/parameters.cxx:658
+#, c-format
+msgid "Unknown parameter type for parameter %s"
+msgstr "Неизвестный тип для параметра %s"
+
+#: vncviewer/parameters.cxx:423 vncviewer/parameters.cxx:472
+#, c-format
+msgid "Failed to close registry key: %ld"
+msgstr "Не удалось закрыть ключ реестра: %ld"
+
+#: vncviewer/parameters.cxx:439
+#, c-format
+msgid "Failed to open registry key: %ld"
+msgstr "Не удалось открыть ключ реестра: %ld"
+
+#: vncviewer/parameters.cxx:496
+msgid "Failed to write configuration file, can't obtain home directory path."
+msgstr "Не удалось создать домашний каталог VNC: не удаётся получить путь к домашнему каталогу."
+
+#: vncviewer/parameters.cxx:509
+#, c-format
+msgid "Failed to write configuration file, can't open %s: %s"
+msgstr "Не удалось записать файл конфигурации: не удаётся открыть %s: %s"
+
+#: vncviewer/parameters.cxx:552
+msgid "Failed to read configuration file, can't obtain home directory path."
+msgstr "Не удалось прочитать файл конфигурации: не удаётся получить путь к домашнему каталогу."
+
+#: vncviewer/parameters.cxx:565
+#, c-format
+msgid "Failed to read configuration file, can't open %s: %s"
+msgstr "Не удалось прочитать файл конфигурации: не удаётся открыть %s: %s"
+
+#: vncviewer/parameters.cxx:578 vncviewer/parameters.cxx:583
+#: vncviewer/parameters.cxx:608 vncviewer/parameters.cxx:621
+#: vncviewer/parameters.cxx:637
+#, c-format
+msgid "Failed to read line %d in file %s: %s"
+msgstr "Не удалось прочитать строку  %d из файла %s: %s"
+
+#: vncviewer/parameters.cxx:584
+msgid "Line too long"
+msgstr "Строка слишком длинная"
+
+#: vncviewer/parameters.cxx:591
+#, c-format
+msgid "Configuration file %s is in an invalid format"
+msgstr "Недопустимый формат файла конфигурации %s"
+
+#: vncviewer/parameters.cxx:609
+msgid "Invalid format"
+msgstr "Недопустимый формат"
+
+#: vncviewer/parameters.cxx:622 vncviewer/parameters.cxx:638
+msgid "Invalid format or too large value"
+msgstr "Недопустимый формат или слишком большое значение"
+
+#: vncviewer/parameters.cxx:665
+#, c-format
+msgid "Unknown parameter %s on line %d in file %s"
+msgstr "Неизвестный параметр %s в строке %d файла %s"
+
+#: vncviewer/vncviewer.cxx:100
+#, c-format
 msgid ""
 "TigerVNC Viewer %d-bit v%s\n"
 "Built on: %s\n"
 "Copyright (C) 1999-%d TigerVNC Team and many others (see README.txt)\n"
 "See http://www.tigervnc.org for information on TigerVNC."
 msgstr ""
-"TigerVNC Viewer %d-bit версия %s (%s)\n"
-"%s\n"
-"Copyright (C) 1999-2011 TigerVNC Team and many others (see README.txt)\n"
-"Ищите информацию о TigerVNC на сайте http://www.tigervnc.org"
+"TigerVNC Viewer (%d-разрядная версия) %s\n"
+"Сборка от: %s\n"
+"© 1999-%d, TigerVNC Team и многие другие (см. README.txt).\n"
+"Информацию о TigerVNC на сайте http://www.tigervnc.org"
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1004
+#: vncviewer/vncviewer.cxx:127
+msgid "About TigerVNC Viewer"
+msgstr "О TigerVNC viewer"
+
+#: vncviewer/vncviewer.cxx:144 vncviewer/vncviewer.cxx:156
 #, c-format
-msgid "Unknown FLTK key code %d (0x%04x)"
-msgstr "Неизвестный код ключа FLTK %d (0x%04x)"
+msgid "Error starting new TigerVNC Viewer: %s"
+msgstr "Не удалось запустить новый TigerVNC Viewer: %s"
 
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:878
+#: vncviewer/vncviewer.cxx:165
 #, c-format
-msgid "Unknown decimal separator: '%s'"
-msgstr "Неизвестный десятичный разделитель: '%s'"
+msgid "Termination signal %d has been received. TigerVNC Viewer will now exit."
+msgstr "Получен сигнал завершения работы %d. TigerVNC Viewer будет закрыт."
 
-#: /home/ossman/devel/tigervnc/vncviewer/parameters.cxx:271
-#, fuzzy, c-format
-msgid "Unknown escape sequence at character %d"
-msgstr "Неизвестный десятичный разделитель: '%s'"
+#: vncviewer/vncviewer.cxx:257
+msgid "TigerVNC Viewer"
+msgstr "TigerVNC Viewer"
 
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:430
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:437
-#, fuzzy
-msgid "Unknown rect encoding"
-msgstr "Использование кодирования %s"
+#: vncviewer/vncviewer.cxx:265
+msgid "No"
+msgstr "Нет"
 
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:429
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:436
-#, c-format
-msgid "Unknown rect encoding %d"
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:671
-msgid "Username and password (insecure without encryption)"
-msgstr "Имя пользователя и пароль (без защиты и шифрования)"
-
-#: /home/ossman/devel/tigervnc/vncviewer/UserDialog.cxx:99
-msgid "Username:"
-msgstr "Имя пользователя:"
-
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:573
-#, c-format
-msgid "Using %s encoding"
-msgstr "Использование кодирования %s"
-
-#: /home/ossman/devel/tigervnc/vncviewer/X11PixelBuffer.cxx:80
-#, c-format
-msgid "Using default colormap and visual, %sdepth %d."
-msgstr ""
-
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:620
-#, c-format
-msgid "Using pixel format %s"
-msgstr "Использование формата точек %s"
-
-#: /home/ossman/devel/tigervnc/vncviewer/ServerDialog.cxx:42
-msgid "VNC Viewer: Connection Details"
-msgstr "VNC Viewer: детали соединения"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:57
-msgid "VNC Viewer: Connection Options"
-msgstr "VNC Viewer: параметры соединения"
-
-#: /home/ossman/devel/tigervnc/vncviewer/UserDialog.cxx:86
-#: /home/ossman/devel/tigervnc/vncviewer/UserDialog.cxx:96
-msgid "VNC authentication"
-msgstr "Аутентификация VNC"
-
-#: /home/ossman/devel/tigervnc/vncviewer/Viewport.cxx:1176
-msgid "VNC connection info"
-msgstr "Информация о соединении VNC"
-
-#: /home/ossman/devel/tigervnc/vncviewer/ServerDialog.cxx:49
-#: /home/ossman/devel/tigervnc/vncviewer/ServerDialog.cxx:54
-msgid "VNC server:"
-msgstr "Сервер VNC:"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:533
-msgid "Very low (8 colors)"
-msgstr "Минимум цвета (8 цветов)"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:698
-#, fuzzy
-msgid "View only (ignore mouse and keyboard)"
-msgstr "Только просмотр (игнорировать мышь и клавиатуру)"
-
-#: /home/ossman/devel/tigervnc/vncviewer/vncviewer.cxx:219
+#: vncviewer/vncviewer.cxx:266
 msgid "Yes"
 msgstr "Да"
 
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:111
+#: vncviewer/vncviewer.cxx:269
+msgid "Close"
+msgstr "Закрыть"
+
+#: vncviewer/vncviewer.cxx:274
+msgid "About"
+msgstr "О программе"
+
+#: vncviewer/vncviewer.cxx:277
+msgid "Hide"
+msgstr "Скрыть"
+
+#: vncviewer/vncviewer.cxx:280
+msgid "Quit"
+msgstr "Выход"
+
+#: vncviewer/vncviewer.cxx:284
+msgid "Services"
+msgstr "Службы"
+
+#: vncviewer/vncviewer.cxx:285
+msgid "Hide Others"
+msgstr "Скрыть прочее"
+
+#: vncviewer/vncviewer.cxx:286
+msgid "Show All"
+msgstr "Показать все"
+
+#: vncviewer/vncviewer.cxx:295
+msgctxt "SysMenu|"
+msgid "&File"
+msgstr "&Файл"
+
+#: vncviewer/vncviewer.cxx:298
+msgctxt "SysMenu|File|"
+msgid "&New Connection"
+msgstr "&Новое соединение"
+
+#: vncviewer/vncviewer.cxx:310
+msgid "Could not create VNC home directory: can't obtain home directory path."
+msgstr "Не удалось создать домашний каталог VNC: не удаётся получить путь к домашнему каталогу."
+
+#: vncviewer/vncviewer.cxx:315
 #, c-format
-msgid "connected to host %s port %d"
-msgstr "подключен к хосту %s, порт %d"
+msgid "Could not create VNC home directory: %s."
+msgstr "Не удалось создать домашний каталог VNC: %s."
 
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:563
-msgid "disabled"
-msgstr "отключено"
+#. TRANSLATORS: "Parameters" are command line arguments, or settings
+#. from a file or the Windows registry.
+#: vncviewer/vncviewer.cxx:520 vncviewer/vncviewer.cxx:521
+msgid "Parameters -listen and -via are incompatible"
+msgstr "Параметры -listen и -via несовместимы"
 
-#: /home/ossman/devel/tigervnc/vncviewer/CConn.cxx:563
-msgid "enabled"
-msgstr "включено"
+#: vncviewer/vncviewer.cxx:536
+#, c-format
+msgid "Listening on port %d"
+msgstr "Прослушивается порт %d"
 
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:556
-msgid "level (1=fast, 6=best [4-6 are rarely useful])"
-msgstr ""
-"уровень (1=высокая скорость, 6=высокое качество [4-6 используются редко])"
-
-#: /home/ossman/devel/tigervnc/vncviewer/OptionsDialog.cxx:569
-msgid "quality (0=poor, 9=best)"
-msgstr "качество (0=наихудшее, 9=наилучшее)"
-
-#: /home/ossman/devel/tigervnc/vncviewer/Win32PixelBuffer.cxx:63
-msgid "unable to create DIB section"
-msgstr ""
-
-#~ msgid ""
-#~ "Desktop name: %.80s\n"
-#~ "Host: %.80s port: %d\n"
-#~ "Size: %d x %d\n"
-#~ "Pixel format: %s\n"
-#~ "(server default %s)\n"
-#~ "Requested encoding: %s\n"
-#~ "Last used encoding: %s\n"
-#~ "Line speed estimate: %d kbit/s\n"
-#~ "Protocol version: %d.%d\n"
-#~ "Security method: %s\n"
-#~ msgstr ""
-#~ "Имя сеанса: %.80s\n"
-#~ "Машина: %.80s порт: %d\n"
-#~ "Размер: %d x %d\n"
-#~ "Формат цвета: %s\n"
-#~ "(значение сервера %s)\n"
-#~ "Запрошенная схема кодирования: %s\n"
-#~ "Последняя схема кодирования: %s\n"
-#~ "Скорость линии связи: %d кбит/с\n"
-#~ "Версия протокола: %d.%d\n"
-#~ "Схема безопасности: %s\n"
+#: vncviewer/vncviewer.cxx:601
+msgid "Internal FLTK error. Exiting."
+msgstr "Внутренняя ошибка FLTK. Выход."
diff --git a/unix/vncconfig/vncconfig.cxx b/unix/vncconfig/vncconfig.cxx
index bffdfbe..2d6adf5 100644
--- a/unix/vncconfig/vncconfig.cxx
+++ b/unix/vncconfig/vncconfig.cxx
@@ -51,6 +51,8 @@
 StringParameter displayname("display", "The X display", "");
 BoolParameter noWindow("nowin", "Don't display a window", 0);
 BoolParameter iconic("iconic", "Start with window iconified", 0);
+BoolParameter setPrimary("SetPrimary", "Set the PRIMARY as well "
+                         "as the CLIPBOARD selection", true);
 BoolParameter sendPrimary("SendPrimary", "Send the PRIMARY as well as the "
                           "CLIPBOARD selection", true);
 IntParameter pollTime("poll",
@@ -87,6 +89,8 @@
   VncConfigWindow(Display* dpy)
     : TXWindow(dpy, 300, 100), cutText(0), cutTextLen(0),
       acceptClipboard(dpy, "Accept clipboard from viewers", this, false, this),
+      setPrimaryCB(dpy, "Also set primary selection",
+                      this, false, this),
       sendClipboard(dpy, "Send clipboard to viewers", this, false, this),
       sendPrimaryCB(dpy, "Send primary selection to viewers", this,false,this),
       pollTimer(this),
@@ -98,6 +102,10 @@
     acceptClipboard.move(xPad, y);
     acceptClipboard.checked(getBoolParam(dpy, ACCEPT_CUT_TEXT));
     y += acceptClipboard.height();
+    setPrimaryCB.move(xPad + 10, y);
+    setPrimaryCB.checked(setPrimary);
+    setPrimaryCB.disabled(!acceptClipboard.checked());
+    y += setPrimaryCB.height();
     sendClipboard.move(xPad, y);
     sendClipboard.checked(getBoolParam(dpy, SEND_CUT_TEXT));
     y += sendClipboard.height();
@@ -136,7 +144,9 @@
                      cutTextLen<9?cutTextLen:8, cutText,
                      cutTextLen<9?"":"...");
           XStoreBytes(dpy, cutText, cutTextLen);
-          ownSelection(XA_PRIMARY, cutEv->time);
+          if (setPrimaryCB.checked()) {
+            ownSelection(XA_PRIMARY, cutEv->time);
+          }
           ownSelection(xaCLIPBOARD, cutEv->time);
           delete [] selection[0];
           delete [] selection[1];
@@ -238,6 +248,7 @@
     if (checkbox == &acceptClipboard) {
       XVncExtSetParam(dpy, (acceptClipboard.checked()
                             ? ACCEPT_CUT_TEXT "=1" : ACCEPT_CUT_TEXT "=0"));
+      setPrimaryCB.disabled(!acceptClipboard.checked());
     } else if (checkbox == &sendClipboard) {
       XVncExtSetParam(dpy, (sendClipboard.checked()
                             ? SEND_CUT_TEXT "=1" : SEND_CUT_TEXT "=0"));
@@ -269,7 +280,7 @@
   int cutTextLen;
   char* selection[2];
   int selectionLen[2];
-  TXCheckbox acceptClipboard, sendClipboard, sendPrimaryCB;
+  TXCheckbox acceptClipboard, setPrimaryCB, sendClipboard, sendPrimaryCB;
   rfb::Timer pollTimer;
 
   QueryConnectDialog* queryConnectDialog;
diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx
index 262dd2c..140b1d6 100644
--- a/vncviewer/CConn.cxx
+++ b/vncviewer/CConn.cxx
@@ -439,7 +439,8 @@
 
   // RFB doesn't have separate selection and clipboard concepts, so we
   // dump the data into both variants.
-  Fl::copy(buffer, ret, 0);
+  if (setPrimary)
+    Fl::copy(buffer, ret, 0);
   Fl::copy(buffer, ret, 1);
 
   delete [] buffer;
diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx
index f112a52..2f9df9c 100644
--- a/vncviewer/OptionsDialog.cxx
+++ b/vncviewer/OptionsDialog.cxx
@@ -263,6 +263,9 @@
 
   viewOnlyCheckbox->value(viewOnly);
   acceptClipboardCheckbox->value(acceptClipboard);
+#if !defined(WIN32) && !defined(__APPLE__)
+  setPrimaryCheckbox->value(setPrimary);
+#endif
   sendClipboardCheckbox->value(sendClipboard);
 #if !defined(WIN32) && !defined(__APPLE__)
   sendPrimaryCheckbox->value(sendPrimary);
@@ -373,6 +376,9 @@
   /* Input */
   viewOnly.setParam(viewOnlyCheckbox->value());
   acceptClipboard.setParam(acceptClipboardCheckbox->value());
+#if !defined(WIN32) && !defined(__APPLE__)
+  setPrimary.setParam(setPrimaryCheckbox->value());
+#endif
   sendClipboard.setParam(sendClipboardCheckbox->value());
 #if !defined(WIN32) && !defined(__APPLE__)
   sendPrimary.setParam(sendPrimaryCheckbox->value());
@@ -696,6 +702,14 @@
                                                          _("Accept clipboard from server")));
   ty += CHECK_HEIGHT + TIGHT_MARGIN;
 
+#if !defined(WIN32) && !defined(__APPLE__)
+  setPrimaryCheckbox = new Fl_Check_Button(LBLRIGHT(tx + INDENT, ty,
+                                                    CHECK_MIN_WIDTH,
+                                                    CHECK_HEIGHT,
+                                                    _("Also set primary selection")));
+  ty += CHECK_HEIGHT + TIGHT_MARGIN;
+#endif
+
   sendClipboardCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                        CHECK_MIN_WIDTH,
                                                        CHECK_HEIGHT,
@@ -706,7 +720,7 @@
   sendPrimaryCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                      CHECK_MIN_WIDTH,
                                                      CHECK_HEIGHT,
-                                                     _("Send primary selection and cut buffer as clipboard")));
+                                                     _("Send primary selection as clipboard")));
   ty += CHECK_HEIGHT + TIGHT_MARGIN;
 #endif
 
diff --git a/vncviewer/OptionsDialog.h b/vncviewer/OptionsDialog.h
index 39bd7af..6984c72 100644
--- a/vncviewer/OptionsDialog.h
+++ b/vncviewer/OptionsDialog.h
@@ -106,6 +106,9 @@
   /* Input */
   Fl_Check_Button *viewOnlyCheckbox;
   Fl_Check_Button *acceptClipboardCheckbox;
+#if !defined(WIN32) && !defined(__APPLE__)
+  Fl_Check_Button *setPrimaryCheckbox;
+#endif
   Fl_Check_Button *sendClipboardCheckbox;
 #if !defined(WIN32) && !defined(__APPLE__)
   Fl_Check_Button *sendPrimaryCheckbox;
diff --git a/vncviewer/parameters.cxx b/vncviewer/parameters.cxx
index 9a9b9fd..ad82f27 100644
--- a/vncviewer/parameters.cxx
+++ b/vncviewer/parameters.cxx
@@ -120,11 +120,14 @@
 BoolParameter acceptClipboard("AcceptClipboard",
                               "Accept clipboard changes from the server",
                               true);
+BoolParameter setPrimary("SetPrimary",
+                         "Set the primary selection as well as the "
+                         "clipboard selection", true);
 BoolParameter sendClipboard("SendClipboard",
                             "Send clipboard changes to the server", true);
 #if !defined(WIN32) && !defined(__APPLE__)
 BoolParameter sendPrimary("SendPrimary",
-                          "Send the primary selection and cut buffer to the "
+                          "Send the primary selection to the "
                           "server as well as the clipboard selection",
                           true);
 #endif
diff --git a/vncviewer/parameters.h b/vncviewer/parameters.h
index e4378c8..682b6d6 100644
--- a/vncviewer/parameters.h
+++ b/vncviewer/parameters.h
@@ -51,6 +51,7 @@
 extern rfb::BoolParameter shared;
 
 extern rfb::BoolParameter acceptClipboard;
+extern rfb::BoolParameter setPrimary;
 extern rfb::BoolParameter sendClipboard;
 #if !defined(WIN32) && !defined(__APPLE__)
 extern rfb::BoolParameter sendPrimary;
diff --git a/vncviewer/vncviewer.man b/vncviewer/vncviewer.man
index 5dd3b45..9ee616d 100644
--- a/vncviewer/vncviewer.man
+++ b/vncviewer/vncviewer.man
@@ -153,13 +153,18 @@
 Accept clipboard changes from the server. Default is on.
 .
 .TP
+.B \-SetPrimary
+Set the primary selection as well as the clipboard selection.
+Default is on.
+.
+.TP
 .B \-SendClipboard
 Send clipboard changes to the server. Default is on.
 .
 .TP
 .B \-SendPrimary
-Send the primary selection and cut buffer to the server as well as the
-clipboard selection. Default is on.
+Send the primary selection to the server as well as the clipboard
+selection. Default is on.
 .
 .TP
 .B \-Maximize