summaryrefslogtreecommitdiffstats
path: root/webclients/java-applet/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'webclients/java-applet/ssl')
-rw-r--r--webclients/java-applet/ssl/Makefile.am2
-rw-r--r--webclients/java-applet/ssl/README338
-rw-r--r--webclients/java-applet/ssl/SignedUltraViewerSSL.jarbin0 -> 113117 bytes
-rw-r--r--webclients/java-applet/ssl/SignedVncViewer.jarbin0 -> 89208 bytes
-rw-r--r--webclients/java-applet/ssl/UltraViewerSSL.jarbin0 -> 110040 bytes
-rw-r--r--webclients/java-applet/ssl/VncViewer.jarbin0 -> 86228 bytes
-rw-r--r--webclients/java-applet/ssl/index.vnc26
-rwxr-xr-xwebclients/java-applet/ssl/onetimekey65
-rw-r--r--webclients/java-applet/ssl/proxy.vnc73
-rwxr-xr-xwebclients/java-applet/ssl/ss_vncviewer3676
-rw-r--r--webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch111
-rw-r--r--webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch2600
-rw-r--r--webclients/java-applet/ssl/ultra.vnc28
-rw-r--r--webclients/java-applet/ssl/ultraproxy.vnc28
-rw-r--r--webclients/java-applet/ssl/ultrasigned.vnc28
-rw-r--r--webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch5494
16 files changed, 12469 insertions, 0 deletions
diff --git a/webclients/java-applet/ssl/Makefile.am b/webclients/java-applet/ssl/Makefile.am
new file mode 100644
index 0000000..fd1c201
--- /dev/null
+++ b/webclients/java-applet/ssl/Makefile.am
@@ -0,0 +1,2 @@
+EXTRA_DIST=VncViewer.jar index.vnc SignedVncViewer.jar proxy.vnc README ss_vncviewer onetimekey UltraViewerSSL.jar SignedUltraViewerSSL.jar ultra.vnc ultrasigned.vnc ultraproxy.vnc
+
diff --git a/webclients/java-applet/ssl/README b/webclients/java-applet/ssl/README
new file mode 100644
index 0000000..b244cf1
--- /dev/null
+++ b/webclients/java-applet/ssl/README
@@ -0,0 +1,338 @@
+This directory contains a patched Java applet VNC viewer that is SSL
+enabled.
+
+The patches in the *.patch files are relative to the source tarball:
+
+ tightvnc-1.3dev7_javasrc.tar.gz
+
+currently (4/06) available here:
+
+ http://prdownloads.sourceforge.net/vnc-tight/tightvnc-1.3dev7_javasrc.tar.gz?download
+
+It also includes some simple patches to:
+
+ - fix richcursor colors
+
+ - make the Java Applet cursor (not the cursor drawn to the canvas
+ framebuffer) invisible when it is inside the canvas.
+
+ - allow Tab (and some other) keystrokes to be sent to the vnc
+ server instead of doing widget traversal.
+
+
+This SSL applet should work with any VNC viewer that has an SSL tunnel in
+front of it. It has been tested on x11vnc and using the stunnel tunnel
+to other VNC servers.
+
+By default this Vnc Viewer will only do SSL. To do unencrypted traffic
+see the "DisableSSL" applet parameter (e.g. set it to Yes in index.vnc).
+
+Proxies: they are a general problem with java socket applets (a socket
+connection does not go through the proxy). See the info in the proxy.vnc
+file for a workaround. It uses SignedVncViewer.jar which is simply
+a signed version of VncViewer.jar. The basic idea is the user clicks
+"Yes" to trust the applet and then it can connect directly to the proxy
+and issue a CONNECT request.
+
+This applet has been tested on versions 1.4.2 and 1.5.0 of the Sun
+Java plugin. It may not work on older releases or different vendor VM's.
+Send full Java Console output for failures.
+
+---------------------------------------------------------------
+Tips:
+
+When doing single-port proxy connections (e.g. both VNC and HTTPS
+thru port 5900) it helps to move through the 'do you trust this site'
+dialogs quickly. x11vnc has to wait to see if the traffic is VNC or
+HTTP and this can cause timeouts if you don't move thru them quickly.
+
+You may have to restart your browser completely if it gets into a
+weird state. For one case we saw the JVM requesting VncViewer.class
+even when no such file exists.
+
+
+---------------------------------------------------------------
+Extras:
+
+ss_vncviewer (not Java):
+
+ Wrapper script for native VNC viewer to connect to x11vnc in
+ SSL mode. Script launches stunnel(8) and then connects to it
+ via localhost which in turn is then redirected to x11vnc via an
+ SSL tunnel. stunnel(8) must be installed and available in PATH.
+
+
+Running Java SSL VncViewer from the command line:
+
+ From this directory:
+
+ java -cp ./VncViewer.jar VncViewer HOST <thehost> PORT <theport>
+
+ substitute <thehost> and <theport> with the actual values.
+ You can add any other parameters, e.g.: ignoreProxy yes
+
+---------------------------------------------------------------
+UltraVNC:
+
+The UltraVNC java viewer has also been patched to support SSL. Various
+bugs in the UltraVNC java viewer were also fixed. This viewer can be
+useful because is support UltraVNC filetransfer, and so it works on
+Unix, etc.
+
+UltraViewerSSL.jar
+SignedUltraViewerSSL.jar
+ultra.vnc
+ultraproxy.vnc
+ultravnc-102-JavaViewer-ssl-etc.patch
+
+---------------------------------------------------------------
+Applet Parameters:
+
+Some additional applet parameters can be set via the URL, e.g.
+
+ http://host:5800/?param=value
+ http://host:5800/ultra.vnc?param=value
+ https://host:5900/ultra.vnc?param=value
+
+etc. If running java from command line as show above, it comes
+in as java ... VncViewer param value ...
+
+There is a limitation with libvncserver that param and value can
+only be alphanumeric, underscore, "+" (for space), or "."
+
+We have added some applet parameters to the stock VNC java
+viewers. Here are the applet parameters:
+
+Both TightVNC and UltraVNC Java viewers:
+
+ HOST
+ string, default: none.
+ The Hostname to connect to.
+
+ PORT
+ number, default: 0
+ The VNC server port to connect to.
+
+ Open New Window
+ yes/no, default: no
+ Run applet in separate frame.
+
+ Show Controls
+ yes/no, default: yes
+ Show Controls button panel.
+
+ Show Offline Desktop
+ yes/no, default: no
+ Do we continue showing desktop on remote disconnect?
+
+ Defer screen updates
+ number, default: 20
+ Milliseconds delay
+
+ Defer cursor updates
+ number, default: 10
+ Milliseconds delay
+
+ Defer update requests
+ number, default: 50
+ Milliseconds delay
+
+ PASSWORD
+ string, default: none
+ VNC session password in plain text.
+
+ ENCPASSWORD
+ string, default: none
+ VNC session password in encrypted in DES with KNOWN FIXED
+ key. It is a hex string. This is like the ~/.vnc/passwd format.
+
+
+ The following are added by x11vnc and/or ssvnc project
+
+ VNCSERVERPORT
+ number, default: 0
+ Like PORT, but if there is a firewall this is the Actual VNC
+ server port. PORT might be a redir port on the firewall.
+
+ DisableSSL
+ yes/no, default: no
+ Do unencrypted connection, no SSL.
+
+ httpsPort
+ number, default: none
+ When checking for proxy, use this at the url port number.
+
+ CONNECT
+ string, default: none
+ Sets to host:port for the CONNECT line to a Web proxy.
+ The Web proxy should connect us to it.
+
+ GET
+ yes/no, default: no
+ Set to do a special HTTP GET (/request.https.vnc.connection)
+ to the vnc server that will cause it to switch to VNC instead.
+ This is to speedup/make more robust, the single port HTTPS and VNC
+ mode of x11vnc (e.g. both services thru port 5900, etc)
+
+ urlPrefix
+ string, default: none
+ set to a string that will be prefixed to all URL's when contacting
+ the VNC server. Idea is a special proxy will use this to indicate
+ internal hostname, etc.
+
+ oneTimeKey
+ string, default: none
+ set a special hex "key" to correspond to an SSL X.509 cert+key.
+ See the 'onetimekey' helper script. Can also be PROMPT to prompt
+ the user to paste the hex key string in.
+
+ This provides a Client-Side cert+key that the client will use to
+ authenticate itself by SSL To the VNC Server.
+
+ This is to try to work around the problem that the Java applet
+ cannot keep an SSL keystore on disk, etc. E.g. if they log
+ into an HTTPS website via password they are authenticated and
+ encrypted, then the website can safely put oneTimeKey=... on the
+ URL. The Vncviewer authenticates the VNC server with this key.
+
+ Note that there is currently a problem in that if x11vnc requires
+ Client Certificates the user cannot download the index.vnc HTML
+ and VncViewer.jar from the same x11vnc. Those need to come from
+ a different x11vnc or from a web server.
+
+ Note that the HTTPS website can also put the VNC Password
+ (e.g. a temporary/one-time one) in the parameter PASSWORD.
+ The Java Applet will automatically supply this VNC password
+ instead of prompting.
+
+ serverCert
+ string, default: none
+ set a special hex "cert" to correspond to an SSL X.509 cert
+ See the 'onetimekey -certonly' helper script.
+
+ This provides a Server-Side cert that the client will authenticate
+ the VNC Server against by SSL.
+
+ This is to try to work around the problem that the Java applet
+ cannot keep an SSL keystore on disk, etc. E.g. if they log
+ into an HTTPS website via password they are authenticated and
+ encrypted, then the website can safely put serverCert=... on the
+ URL.
+
+ Of course the VNC Server is sending this string to the Java
+ Applet, so this is only reasonable security if the VNC Viewer
+ already trusts the HTTPS retrieval of the URL + serverCert param
+ that it gets. This should be done over HTTPS not HTTP.
+
+ proxyHost
+ string, default: none
+ Do not try to guess the proxy's hostname, use the value in
+ proxyHost. Does not imply forceProxy (below.)
+
+ proxyPort
+ string, default: none
+ Do not try to guess the proxy's port number, use the value in
+ proxyPort. Does not imply forceProxy (below.)
+
+ forceProxy
+ yes/no, default: no
+ Assume there is a proxy and force its use.
+
+ If a string other than "yes" or "no" is given, it implies "yes"
+ and uses the string for proxyHost and proxyPort (see above).
+ In this case the string must be of the form "hostname+port".
+ Note that it is "+" and not ":" before the port number.
+
+ ignoreProxy
+ yes/no, default: no
+ Don't check for a proxy, assume there is none.
+
+ trustAllVncCerts
+ yes/no, default: no
+ Automatically trust any cert received from the VNC server
+ (obviously this could be dangerous and lead to man in the
+ middle attack). Do not ask the user to verify any of these
+ certs from the VNC server.
+
+ trustUrlVncCert
+ yes/no, default: no
+ Automatically trust any cert that the web browsers has accepted.
+ E.g. the user said "Yes" or "Continue" to a web browser dialog
+ regarding a certificate. If we get the same cert (chain) from
+ the VNC server we trust it without prompting the user.
+
+ debugCerts
+ yes/no, default: no
+ Print out every cert in the Server, TrustUrl, TrustAll chains.
+
+
+TightVNC Java viewer only:
+
+ Offer Relogin
+ yes/no, default: yes
+ "Offer Relogin" set to "No" disables "Login again"
+
+ SocketFactory
+ string, default: none
+ set Java Socket class factory.
+
+UltraVNC Java viewer only:
+
+ None.
+
+ The following are added by x11vnc and/or ssvnc project
+
+ ftpDropDown
+ string, default: none
+ Sets the file transfer "drives" dropdown to the "." separated
+ list. Use "+" for space. The default is
+
+ My+Documents.Desktop.Home
+
+ for 3 entries in the dropdown in addition to the "drives"
+ (e.g. C:\) These items should be expanded properly by the VNC
+ Server. x11vnc will prepend $HOME to them, which is normally
+ what one wants. To include a "/" use "_2F_". Another example:
+
+ Home.Desktop.bin_2F_linux
+
+ If an item is prefixed with "TOP_" then the item is inserted at
+ the top of the drop down rather than being appended to the end.
+ E.g. to try to initially load the user homedir instead of /:
+
+ TOP_Home.My+Documents.Desktop
+
+ If ftpDropDown is set to the empty string, "", then no special
+ locations, [Desktop] etc., are placed in the drop down. Only the
+ ultravnc "drives" will appear.
+
+ ftpOnly
+ yes/no, default: no
+ The VNC viewer only shows the filetransfer panel, no desktop
+ is displayed.
+
+ graftFtp
+ yes/no, default: no
+ As ftpOnly, the VNC viewer only shows the filetransfer panel,
+ no desktop is displayed, however it is "grafted" onto an existing
+ SSVNC unix vncviewer. The special SSVNC vncviewer merges the two
+ channels.
+
+ dsmActive
+ yes/no, default: no
+ Special usage mode with the SSVNC unix vncviewer. The UltraVNC
+ DSM encryption is active. Foolishly, UltraVNC DSM encryption
+ *MODIFIES* the VNC protocol when active (it is not a pure tunnel).
+ This option indicates to modify the VNC protocol to make this work.
+ Usually only used with graftFtp and SSVNC unix vncviewer.
+
+ delayAuthPanel
+ yes/no, default: no
+ This is another special usage mode with the SSVNC unix vncviewer.
+ A login panel is delayed (not shown at startup.) Could be useful
+ for non SSVNC usage too.
+
+ ignoreMSLogonCheck
+ yes/no, default: no
+ Similar to delayAuthPanel, do not put up a popup asking for
+ Windows username, etc.
diff --git a/webclients/java-applet/ssl/SignedUltraViewerSSL.jar b/webclients/java-applet/ssl/SignedUltraViewerSSL.jar
new file mode 100644
index 0000000..6c18737
--- /dev/null
+++ b/webclients/java-applet/ssl/SignedUltraViewerSSL.jar
Binary files differ
diff --git a/webclients/java-applet/ssl/SignedVncViewer.jar b/webclients/java-applet/ssl/SignedVncViewer.jar
new file mode 100644
index 0000000..95c0b0b
--- /dev/null
+++ b/webclients/java-applet/ssl/SignedVncViewer.jar
Binary files differ
diff --git a/webclients/java-applet/ssl/UltraViewerSSL.jar b/webclients/java-applet/ssl/UltraViewerSSL.jar
new file mode 100644
index 0000000..45259fd
--- /dev/null
+++ b/webclients/java-applet/ssl/UltraViewerSSL.jar
Binary files differ
diff --git a/webclients/java-applet/ssl/VncViewer.jar b/webclients/java-applet/ssl/VncViewer.jar
new file mode 100644
index 0000000..9453c6f
--- /dev/null
+++ b/webclients/java-applet/ssl/VncViewer.jar
Binary files differ
diff --git a/webclients/java-applet/ssl/index.vnc b/webclients/java-applet/ssl/index.vnc
new file mode 100644
index 0000000..ec520dc
--- /dev/null
+++ b/webclients/java-applet/ssl/index.vnc
@@ -0,0 +1,26 @@
+<!--
+ index.vnc - default HTML page for TightVNC Java viewer applet, to be
+ used with Xvnc. On any file ending in .vnc, the HTTP server embedded in
+ Xvnc will substitute the following variables when preceded by a dollar:
+ USER, DESKTOP, DISPLAY, APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT,
+ PARAMS. Use two dollar signs ($$) to get a dollar sign in the generated
+ HTML page.
+
+ NOTE: the $PARAMS variable is not supported by the standard VNC, so
+ make sure you have TightVNC on the server side, if you're using this
+ variable.
+-->
+
+<HTML>
+<TITLE>
+$USER's $DESKTOP desktop ($DISPLAY)
+</TITLE>
+<APPLET CODE=VncViewer.class ARCHIVE=VncViewer.jar
+ WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
+<param name=PORT value=$PORT>
+<param name="Open New Window" value=yes>
+$PARAMS
+</APPLET>
+<BR>
+<A href="http://www.karlrunge.com/x11vnc">x11vnc site</A>
+</HTML>
diff --git a/webclients/java-applet/ssl/onetimekey b/webclients/java-applet/ssl/onetimekey
new file mode 100755
index 0000000..bf57c8f
--- /dev/null
+++ b/webclients/java-applet/ssl/onetimekey
@@ -0,0 +1,65 @@
+#!/bin/sh
+#
+# usage: onetimekey path/to/mycert.pem
+# onetimekey -certonly path/to/mycert.pem
+#
+# Takes an openssl cert+key pem file and turns into a long string
+# for the x11vnc SSL VNC Java Viewer.
+#
+# The Java applet URL parameter can be oneTimeKey=<str> where str is
+# the output of this program, or can be oneTimeKey=PROMPT in which
+# case the applet will ask you to paste in the string.
+#
+# The problem trying to be solved here is it is difficult to get
+# the Java applet to have or use a keystore with the key saved
+# in it. Also, as the name implies, an HTTPS server can create
+# a one time key to send to the applet (the user has already
+# logged in via password to the HTTPS server).
+#
+# Note oneTimeKey is to provide a CLIENT Certificate for the viewer
+# to authenticate itself to the VNC Server.
+#
+# There is also the serverCert=<str> Applet parameter. This is
+# a cert to authenticate the VNC server against. To create that
+# string with this tool specify -certonly as the first argument.
+
+certonly=""
+if [ "X$1" = "X-certonly" ]; then
+ shift
+ certonly=1
+fi
+
+in=$1
+der=/tmp/1time$$.der
+touch $der
+chmod 600 $der
+
+openssl pkcs8 -topk8 -nocrypt -in "$in" -out "$der" -outform der
+
+pbinhex=/tmp/pbinhex.$$
+cat > $pbinhex <<END
+#!/usr/bin/perl
+
+\$str = '';
+while (1) {
+ \$c = getc(STDIN);
+ last if \$c eq '';
+ \$str .= sprintf("%02x", unpack("C", \$c));
+}
+
+print "\$str\n";
+END
+
+chmod 700 $pbinhex
+
+str1=`$pbinhex < "$der"`
+rm -f "$der"
+
+n=`grep -n 'BEGIN CERTIFICATE' $in | awk -F: '{print $1}' | head -1`
+str2=`tail +$n $in | $pbinhex`
+if [ "X$certonly" = "X1" ]; then
+ echo "$str2"
+else
+ echo "$str1,$str2"
+fi
+rm -f $pbinhex
diff --git a/webclients/java-applet/ssl/proxy.vnc b/webclients/java-applet/ssl/proxy.vnc
new file mode 100644
index 0000000..6d3ab3d
--- /dev/null
+++ b/webclients/java-applet/ssl/proxy.vnc
@@ -0,0 +1,73 @@
+<!--
+ index.vnc - default HTML page for TightVNC Java viewer applet, to be
+ used with Xvnc. On any file ending in .vnc, the HTTP server embedded in
+ Xvnc will substitute the following variables when preceded by a dollar:
+ USER, DESKTOP, DISPLAY, APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT,
+ PARAMS. Use two dollar signs ($$) to get a dollar sign in the generated
+ HTML page.
+
+ NOTE: the $PARAMS variable is not supported by the standard VNC, so
+ make sure you have TightVNC on the server side, if you're using this
+ variable.
+-->
+
+<!--
+The idea behind using the signed applet in SignedVncViewer.jar for
+firewall proxies:
+
+Java socket applets and http proxies do not get along well.
+
+Java security allows the applet to connect back via a socket to the
+originating host, but the browser/plugin Proxy settings are not used for
+socket connections (only http and the like). So the socket connection
+fails in the proxy environment.
+
+The applet is not allowed to open a socket connection to the proxy (since
+that would let it connect to just about any host, e.g. CONNECT method).
+
+This is indpendent of SSL but of course fails for that socket connection
+as well. I.e. this is a problem for non-SSL VNC Viewers as well.
+
+Solution? Sign the applet and have the user click on "Yes" that they
+fully trust the applet. Then the applet can connect to any host via
+sockets, in particular the proxy. It next issues the request
+
+ CONNECT host:port HTTP/1.1
+ Host: host:port
+
+and if the proxy supports the CONNECT method we are finally connected to
+the VNC server.
+
+For SSL connections, SSL is layered on top of this socket. However note
+this scheme will work for non-SSL applet proxy tunnelling as well.
+
+It should be able to get non-SSL VNC connections to work via GET
+command but that has not been done yet.
+
+Note that some proxies only allow CONNECT to only these the ports 443
+(HTTPS) and 563 (SNEWS). So you would have to run the VNC server on
+those ports.
+
+SignedVncViewer.jar is just a signed version of VncViewer.jar
+
+The URL to use for this file: https://host:port/proxy.vnc
+
+Note VNCSERVERPORT, we assume $PARAMS will have the correct PORT setting
+(e.g. 563), not the one libvncserver puts in....
+
+-->
+
+
+<HTML>
+<TITLE>
+$USER's $DESKTOP desktop ($DISPLAY)
+</TITLE>
+<APPLET CODE=VncViewer.class ARCHIVE=SignedVncViewer.jar
+ WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
+<param name=VNCSERVERPORT value=$PORT>
+<param name="Open New Window" value=yes>
+$PARAMS
+</APPLET>
+<BR>
+<A href="http://www.karlrunge.com/x11vnc">x11vnc site</A>
+</HTML>
diff --git a/webclients/java-applet/ssl/ss_vncviewer b/webclients/java-applet/ssl/ss_vncviewer
new file mode 100755
index 0000000..7e793ff
--- /dev/null
+++ b/webclients/java-applet/ssl/ss_vncviewer
@@ -0,0 +1,3676 @@
+#!/bin/sh
+#
+# ss_vncviewer: wrapper for vncviewer to use an stunnel SSL tunnel
+# or an SSH tunnel.
+#
+# Copyright (c) 2006-2009 by Karl J. Runge <runge@karlrunge.com>
+#
+# ss_vncviewer 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.
+#
+# ss_vncviewer 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 ss_vncviewer; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
+# or see <http://www.gnu.org/licenses/>.
+#
+#
+# You must have stunnel(8) installed on the system and in your PATH
+# (however, see the -ssh option below, in which case you will need ssh(1)
+# installed) Note: stunnel is usually installed in an "sbin" subdirectory.
+#
+# You should have "x11vnc -ssl ..." or "x11vnc -stunnel ..."
+# already running as the VNC server on the remote machine.
+# (or use stunnel on the server side for any other VNC server)
+#
+#
+# Usage: ss_vncviewer [cert-args] host:display <vncviewer-args>
+#
+# e.g.: ss_vncviewer snoopy:0
+# ss_vncviewer snoopy:0 -encodings "copyrect tight zrle hextile"
+#
+# [cert-args] can be:
+#
+# -verify /path/to/cacert.pem
+# -mycert /path/to/mycert.pem
+# -crl /path/to/my_crl.pem (or directory)
+# -proxy host:port
+#
+# -verify specifies a CA cert PEM file (or a self-signed one) for
+# authenticating the VNC server.
+#
+# -mycert specifies this client's cert+key PEM file for the VNC server to
+# authenticate this client.
+#
+# -proxy try host:port as a Web proxy to use the CONNECT method
+# to reach the VNC server (e.g. your firewall requires a proxy).
+#
+# For the "double proxy" case use -proxy host1:port1,host2:port2
+# (the first CONNECT is done through host1:port1 to host2:port2
+# and then a 2nd CONNECT to the destination VNC server.)
+#
+# Use socks://host:port, socks4://host:port, or socks5://host,port
+# to force usage of a SOCKS proxy. Also repeater://host:port and
+# sslrepeater://host:port.
+#
+# -showcert Only fetch the certificate using the 'openssl s_client'
+# command (openssl(1) must in installed). On ssvnc 1.0.27 and
+# later the bundled command 'ultravnc_dsm_helper' is used.
+#
+# See http://www.karlrunge.com/x11vnc/faq.html#faq-ssl-ca for details on
+# SSL certificates with VNC.
+#
+# A few other args (not related to SSL and certs):
+#
+# -2nd Run the vncviewer a 2nd time if the first connections fails.
+#
+# -ssh Use ssh instead of stunnel SSL. ssh(1) must be installed and you
+# must be able to log into the remote machine via ssh.
+#
+# In this case "host:display" may be of the form "user@host:display"
+# where "user@host" is used for the ssh login (see ssh(1) manpage).
+#
+# If -proxy is supplied it can be of the forms: "gwhost" "gwhost:port"
+# "user@gwhost" or "user@gwhost:port". "gwhost" is an incoming ssh
+# gateway machine (the VNC server is not running there), an ssh -L
+# redir is used to "host" in "host:display" from "gwhost". Any "user@"
+# part must be in the -proxy string (not in "host:display").
+#
+# Under -proxy use "gwhost:port" if connecting to any ssh port
+# other than the default (22). (even for the non-gateway case,
+# -proxy must be used to specify a non-standard ssh port)
+#
+# A "double ssh" can be specified via a -proxy string with the two
+# hosts separated by a comma:
+#
+# [user1@]host1[:port1],[user2@]host2[:port2]
+#
+# in which case a ssh to host1 and thru it via a -L redir a 2nd
+# ssh is established to host2.
+#
+# Examples:
+#
+# ss_vncviewer -ssh bob@bobs-home.net:0
+# ss_vncviewer -ssh -sshcmd 'x11vnc -localhost' bob@bobs-home.net:0
+#
+# ss_vncviewer -ssh -proxy fred@mygate.com:2022 mymachine:0
+# ss_vncviewer -ssh -proxy bob@bobs-home.net:2222 localhost:0
+#
+# ss_vncviewer -ssh -proxy fred@gw-host,fred@peecee localhost:0
+#
+# -sshcmd cmd Run "cmd" via ssh instead of the default "sleep 15"
+# e.g. -sshcmd 'x11vnc -display :0 -localhost -rfbport 5900'
+#
+# -sshargs "args" pass "args" to the ssh process, e.g. -L/-R port redirs.
+#
+# -sshssl Tunnel the SSL connection thru a SSH connection. The tunnel as
+# under -ssh is set up and the SSL connection goes thru it. Use
+# this if you want to have and end-to-end SSL connection but must
+# go thru a SSH gateway host (e.g. not the vnc server). Or use
+# this if you need to tunnel additional services via -R and -L
+# (see -sshargs above).
+#
+# ss_vncviewer -sshssl -proxy fred@mygate.com mymachine:0
+#
+# -listen (or -reverse) set up a reverse connection.
+#
+# -alpha turn on cursor alphablending hack if you are using the
+# enhanced tightvnc vncviewer.
+#
+# -grab turn on XGrabServer hack if you are using the enhanced tightvnc
+# vncviewer (e.g. for fullscreen mode in some windowmanagers like
+# fvwm that do not otherwise work in fullscreen mode)
+#
+#
+# set VNCVIEWERCMD to whatever vncviewer command you want to use.
+#
+VNCIPCMD=${VNCVIEWERCMD:-vncip}
+VNCVIEWERCMD=${VNCVIEWERCMD:-vncviewer}
+if [ "X$SSVNC_TURBOVNC" != "X" ]; then
+ if echo "$VNCVIEWERCMD" | grep '\.turbovnc' > /dev/null; then
+ :
+ else
+ if type "$VNCVIEWERCMD.turbovnc" > /dev/null 2>/dev/null; then
+ VNCVIEWERCMD="$VNCVIEWERCMD.turbovnc"
+ fi
+ fi
+fi
+#
+# Same for STUNNEL, e.g. set it to /path/to/stunnel or stunnel4, etc.
+#
+
+# turn on verbose debugging output
+if [ "X$SS_DEBUG" != "X" -a "X$SS_DEBUG" != "X0" ]; then
+ set -xv
+fi
+
+PATH=$PATH:/usr/sbin:/usr/local/sbin:/dist/sbin; export PATH
+
+localhost="localhost"
+if uname | grep Darwin >/dev/null; then
+ localhost="127.0.0.1"
+fi
+
+# work out which stunnel to use (debian installs as stunnel4)
+stunnel_set_here=""
+if [ "X$STUNNEL" = "X" ]; then
+ check_stunnel=1
+ if [ "X$SSVNC_BASEDIRNAME" != "X" ]; then
+ if [ -x "$SSVNC_BASEDIRNAME/stunnel" ]; then
+ type stunnel > /dev/null 2>&1
+ if [ $? = 0 ]; then
+ # found ours
+ STUNNEL=stunnel
+ check_stunnel=0
+ fi
+ fi
+ fi
+ if [ "X$check_stunnel" = "X1" ]; then
+ type stunnel4 > /dev/null 2>&1
+ if [ $? = 0 ]; then
+ STUNNEL=stunnel4
+ else
+ STUNNEL=stunnel
+ fi
+ fi
+ stunnel_set_here=1
+fi
+
+help() {
+ tail -n +2 "$0" | sed -e '/^$/ q'
+}
+
+secondtry=""
+gotalpha=""
+use_ssh=""
+use_sshssl=""
+direct_connect=""
+ssh_sleep=15
+
+# sleep longer in -listen mode:
+if echo "$*" | grep '.*-listen' > /dev/null; then
+ ssh_sleep=1800
+fi
+
+
+ssh_cmd=""
+# env override of ssh_cmd:
+if [ "X$SS_VNCVIEWER_SSH_CMD" != "X" ]; then
+ ssh_cmd="$SS_VNCVIEWER_SSH_CMD"
+fi
+
+ssh_args=""
+showcert=""
+reverse=""
+
+ciphers=""
+anondh="ALL:RC4+RSA:+SSLv2:@STRENGTH"
+anondh_set=""
+stunnel_debug="6"
+if [ "X$SS_DEBUG" != "X" -o "X$SSVNC_VENCRYPT_DEBUG" != "X" -o "X$SSVNC_STUNNEL_DEBUG" != "X" ]; then
+ stunnel_debug="7"
+fi
+
+if [ "X$1" = "X-viewerflavor" ]; then
+ # special case, try to guess which viewer:
+ #
+ if echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then
+ echo "unknown"
+ exit 0
+ fi
+ if echo "$VNCVIEWERCMD" | grep -i chicken.of > /dev/null; then
+ echo "cotvnc"
+ exit 0
+ fi
+ if echo "$VNCVIEWERCMD" | grep -i ultra > /dev/null; then
+ echo "ultravnc"
+ exit 0
+ fi
+ # OK, run it for help output...
+ str=`$VNCVIEWERCMD -h 2>&1 | head -n 5`
+ if echo "$str" | grep -i 'TightVNC.viewer' > /dev/null; then
+ echo "tightvnc"
+ elif echo "$str" | grep -i 'VNC viewer version 3' > /dev/null; then
+ echo "realvnc3"
+ elif echo "$str" | grep -i 'VNC viewer .*Edition 4' > /dev/null; then
+ echo "realvnc4"
+ elif echo "$str" | grep -i 'RealVNC.Ltd' > /dev/null; then
+ echo "realvnc4"
+ else
+ echo "unknown"
+ fi
+ exit 0
+fi
+if [ "X$1" = "X-viewerhelp" ]; then
+ $VNCVIEWERCMD -h 2>&1
+ exit 0
+fi
+
+# grab our cmdline options:
+while [ "X$1" != "X" ]
+do
+ case $1 in
+ "-verify") shift; verify="$1"
+ ;;
+ "-mycert") shift; mycert="$1"
+ ;;
+ "-crl") shift; crl="$1"
+ ;;
+ "-proxy") shift; proxy="$1"
+ ;;
+ "-ssh") use_ssh=1
+ ;;
+ "-sshssl") use_ssh=1
+ use_sshssl=1
+ ;;
+ "-sshcmd") shift; ssh_cmd="$1"
+ ;;
+ "-sshargs") shift; ssh_args="$1"
+ ;;
+ "-anondh") ciphers="ciphers=$anondh"
+ ULTRAVNC_DSM_HELPER_SHOWCERT_ADH=1
+ export ULTRAVNC_DSM_HELPER_SHOWCERT_ADH
+ anondh_set=1
+ ;;
+ "-ciphers") shift; ciphers="ciphers=$1"
+ ;;
+ "-alpha") gotalpha=1
+ ;;
+ "-showcert") showcert=1
+ ;;
+ "-listen") reverse=1
+ ;;
+ "-reverse") reverse=1
+ ;;
+ "-2nd") secondtry=1
+ ;;
+ "-grab") VNCVIEWER_GRAB_SERVER=1; export VNCVIEWER_GRAB_SERVER
+ ;;
+ "-x11cursor") VNCVIEWER_X11CURSOR=1; export VNCVIEWER_X11CURSOR
+ ;;
+ "-rawlocal") VNCVIEWER_RAWLOCAL=1; export VNCVIEWER_RAWLOCAL
+ ;;
+ "-scale") shift; SSVNC_SCALE="$1"; export SSVNC_SCALE
+ ;;
+ "-onelisten") SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE
+ ;;
+ "-sendclipboard") VNCVIEWER_SEND_CLIPBOARD=1; export VNCVIEWER_SEND_CLIPBOARD
+ ;;
+ "-sendalways") VNCVIEWER_SEND_ALWAYS=1; export VNCVIEWER_SEND_ALWAYS
+ ;;
+ "-recvtext") shift; VNCVIEWER_RECV_TEXT="$1"; export VNCVIEWER_RECV_TEXT
+ ;;
+ "-escape") shift; VNCVIEWER_ESCAPE="$1"; export VNCVIEWER_ESCAPE
+ ;;
+ "-ssvnc_encodings") shift; VNCVIEWER_ENCODINGS="$1"; export VNCVIEWER_ENCODINGS
+ ;;
+ "-ssvnc_extra_opts") shift; VNCVIEWERCMD_EXTRA_OPTS="$1"; export VNCVIEWERCMD_EXTRA_OPTS
+ ;;
+ "-rfbversion") shift; VNCVIEWER_RFBVERSION="$1"; export VNCVIEWER_RFBVERSION
+ ;;
+ "-nobell") VNCVIEWER_NOBELL=1; export VNCVIEWER_NOBELL
+ ;;
+ "-popupfix") VNCVIEWER_POPUP_FIX=1; export VNCVIEWER_POPUP_FIX
+ ;;
+ "-realvnc4") VNCVIEWER_IS_REALVNC4=1; export VNCVIEWER_IS_REALVNC4
+ ;;
+ "-h"*) help; exit 0
+ ;;
+ "--h"*) help; exit 0
+ ;;
+ *) break
+ ;;
+ esac
+ shift
+done
+
+# maxconn is something we added to stunnel, this disables it:
+if [ "X$SS_VNCVIEWER_NO_MAXCONN" != "X" ]; then
+ STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'`
+elif echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then
+ STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'`
+elif [ "X$reverse" != "X" ]; then
+ STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'`
+else
+ # new way (our patches). other than the above, we set these:
+ if [ "X$SKIP_STUNNEL_ONCE" = "X" ]; then
+ STUNNEL_ONCE=1; export STUNNEL_ONCE
+ fi
+ if [ "X$SKIP_STUNNEL_MAX_CLIENTS" = "X" ]; then
+ STUNNEL_MAX_CLIENTS=1; export STUNNEL_MAX_CLIENTS
+ fi
+fi
+# always set this one:
+if [ "X$SKIP_STUNNEL_NO_SYSLOG" = "X" ]; then
+ STUNNEL_NO_SYSLOG=1; export STUNNEL_NO_SYSLOG
+fi
+
+# this is the -t ssh option (gives better keyboard response thru SSH tunnel)
+targ="-t"
+if [ "X$SS_VNCVIEWER_NO_T" != "X" ]; then
+ targ=""
+fi
+
+# set the alpha blending env. hack:
+if [ "X$gotalpha" = "X1" ]; then
+ VNCVIEWER_ALPHABLEND=1
+ export VNCVIEWER_ALPHABLEND
+else
+ NO_ALPHABLEND=1
+ export NO_ALPHABLEND
+fi
+
+if [ "X$reverse" != "X" ]; then
+ ssh_sleep=1800
+ if [ "X$proxy" != "X" ]; then
+ # check proxy usage under reverse connection:
+ if [ "X$use_ssh" = "X" -a "X$use_sshssl" = "X" ]; then
+ echo ""
+ if echo "$proxy" | egrep -i "(repeater|vencrypt)://" > /dev/null; then
+ :
+ else
+ echo "*Warning*: SSL -listen and a Web proxy does not make sense."
+ sleep 2
+ fi
+ elif echo "$proxy" | grep "," > /dev/null; then
+ :
+ else
+ echo ""
+ echo "*Warning*: -listen and a single proxy/gateway does not make sense."
+ sleep 2
+ fi
+
+ # we now try to PPROXY_LOOP_THYSELF, set this var to disable that.
+ #SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE
+ fi
+fi
+if [ "X$ssh_cmd" = "X" ]; then
+ # if no remote ssh cmd, sleep a bit:
+ ssh_cmd="sleep $ssh_sleep"
+fi
+
+# this should be a host:display:
+#
+orig="$1"
+shift
+
+dL="-L"
+if uname -sr | egrep 'SunOS 5\.[5-8]' > /dev/null; then
+ dL="-h"
+fi
+
+have_uvnc_dsm_helper_showcert=""
+if [ "X$showcert" = "X1" -a "X$SSVNC_USE_S_CLIENT" = "X" -a "X$reverse" = "X" ]; then
+ if type ultravnc_dsm_helper >/dev/null 2>&1; then
+ if ultravnc_dsm_helper -help 2>&1 | grep -w showcert >/dev/null; then
+ have_uvnc_dsm_helper_showcert=1
+ fi
+ fi
+fi
+have_uvnc_dsm_helper_ipv6=""
+if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then
+ if type ultravnc_dsm_helper >/dev/null 2>&1; then
+ if ultravnc_dsm_helper -help 2>&1 | grep -iw ipv6 >/dev/null; then
+ have_uvnc_dsm_helper_ipv6=1
+ fi
+ fi
+fi
+
+rchk() {
+ # a kludge to set $RANDOM if we are not bash:
+ if [ "X$BASH_VERSION" = "X" ]; then
+ RANDOM=`date +%S``sh -c 'echo $$'``ps -elf 2>&1 | sum 2>&1 | awk '{print $1}'`
+ fi
+}
+rchk
+
+# a portable, but not absolutely safe, tmp file creator
+mytmp() {
+ tf=$1
+ if type mktemp > /dev/null 2>&1; then
+ # if we have mktemp(1), use it:
+ tf2="$tf.XXXXXX"
+ tf2=`mktemp "$tf2"`
+ if [ "X$tf2" != "X" -a -f "$tf2" ]; then
+ if [ "X$DEBUG_MKTEMP" != "X" ]; then
+ echo "mytmp-mktemp: $tf2" 1>&2
+ fi
+ echo "$tf2"
+ return
+ fi
+ fi
+ # fallback to multiple cmds:
+ rm -rf "$tf" || exit 1
+ if [ -d "$tf" ]; then
+ echo "tmp file $tf still exists as a directory."
+ exit 1
+ elif [ $dL "$tf" ]; then
+ echo "tmp file $tf still exists as a symlink."
+ exit 1
+ elif [ -f "$tf" ]; then
+ echo "tmp file $tf still exists."
+ exit 1
+ fi
+ touch "$tf" || exit 1
+ chmod 600 "$tf" || exit 1
+ rchk
+ if [ "X$DEBUG_MKTEMP" != "X" ]; then
+ echo "mytmp-touch: $tf" 1>&2
+ fi
+ echo "$tf"
+}
+
+# set up special case of ultravnc single click III mode:
+if echo "$proxy" | egrep "^sslrepeater://" > /dev/null; then
+ pstr=`echo "$proxy" | sed -e 's,sslrepeater://,,'`
+ pstr1=`echo "$pstr" | sed -e 's/+.*$//'`
+ pstr2=`echo "$pstr" | sed -e 's/^[^+]*+//'`
+ SSVNC_REPEATER="SCIII=$pstr2"; export SSVNC_REPEATER
+ orig=$pstr1
+ echo
+ echo "reset: SSVNC_REPEATER=$SSVNC_REPEATER orig=$orig proxy=''"
+ proxy=""
+fi
+if echo "$proxy" | egrep "vencrypt://" > /dev/null; then
+ vtmp="/tmp/ss_handshake${RANDOM}.$$.txt"
+ vtmp=`mytmp "$vtmp"`
+ SSVNC_PREDIGESTED_HANDSHAKE="$vtmp"
+ export SSVNC_PREDIGESTED_HANDSHAKE
+ if [ "X$SSVNC_USE_OURS" = "X" ]; then
+ NEED_VENCRYPT_VIEWER_BRIDGE=1
+ fi
+fi
+if [ "X$SSVNC_USE_OURS" = "X" ]; then
+ VNCVIEWERCMD_EXTRA_OPTS=""
+fi
+
+
+# check -ssh and -mycert/-verify conflict:
+if [ "X$use_ssh" = "X1" -a "X$use_sshssl" = "X" ]; then
+ if [ "X$mycert" != "X" -o "X$verify" != "X" ]; then
+ echo "-mycert and -verify cannot be used in -ssh mode"
+ exit 1
+ fi
+fi
+
+# direct mode Vnc:// means show no warnings.
+# direct mode vnc:// will show warnings.
+if echo "$orig" | grep '^V[Nn][Cc]://' > /dev/null; then
+ SSVNC_NO_ENC_WARN=1
+ export SSVNC_NO_ENC_WARN
+ orig=`echo "$orig" | sed -e 's/^...:/vnc:/'`
+fi
+
+# interprest the pseudo URL proto:// strings:
+if echo "$orig" | grep '^vnc://' > /dev/null; then
+ orig=`echo "$orig" | sed -e 's,vnc://,,'`
+ verify=""
+ mycert=""
+ crl=""
+ use_ssh=""
+ use_sshssl=""
+ direct_connect=1
+elif echo "$orig" | grep '^vncs://' > /dev/null; then
+ orig=`echo "$orig" | sed -e 's,vncs://,,'`
+elif echo "$orig" | grep '^vncssl://' > /dev/null; then
+ orig=`echo "$orig" | sed -e 's,vncssl://,,'`
+elif echo "$orig" | grep '^vnc+ssl://' > /dev/null; then
+ orig=`echo "$orig" | sed -e 's,vnc.ssl://,,'`
+elif echo "$orig" | grep '^vncssh://' > /dev/null; then
+ orig=`echo "$orig" | sed -e 's,vncssh://,,'`
+ use_ssh=1
+elif echo "$orig" | grep '^vnc+ssh://' > /dev/null; then
+ orig=`echo "$orig" | sed -e 's,vnc.ssh://,,'`
+ use_ssh=1
+fi
+
+if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then
+ verify=""
+ mycert=""
+ crl=""
+ use_ssh=""
+ use_sshssl=""
+ direct_connect=1
+ if echo "$SSVNC_ULTRA_DSM" | grep 'noultra:' > /dev/null; then
+ SSVNC_NO_ULTRA_DSM=1; export SSVNC_NO_ULTRA_DSM
+ fi
+fi
+
+# rsh mode is an internal/secret thing only I use.
+rsh=""
+if echo "$orig" | grep '^rsh://' > /dev/null; then
+ use_ssh=1
+ rsh=1
+ orig=`echo "$orig" | sed -e 's,rsh://,,'`
+elif echo "$orig" | grep '^rsh:' > /dev/null; then
+ use_ssh=1
+ rsh=1
+ orig=`echo "$orig" | sed -e 's,rsh:,,'`
+fi
+
+# play around with host:display port:
+if echo "$orig" | grep ':[0-9][0-9]*$' > /dev/null; then
+ :
+else
+ # add or assume :0 if no ':'
+ if [ "X$reverse" = "X" ]; then
+ orig="$orig:0"
+ elif [ "X$orig" = "X" ]; then
+ orig=":0"
+ fi
+fi
+
+# extract host and disp number:
+
+# try to see if it is ipv6 address:
+ipv6=0
+if echo "$orig" | grep '\[' > /dev/null; then
+ # ipv6 [fe80::219:dbff:fee5:3f92%eth1]:5900
+ host=`echo "$orig" | sed -e 's/\].*$//' -e 's/\[//'`
+ disp=`echo "$orig" | sed -e 's/^.*\]://'`
+ ipv6=1
+elif echo "$orig" | grep ':..*:' > /dev/null; then
+ # ipv6 fe80::219:dbff:fee5:3f92%eth1:5900
+ host=`echo "$orig" | sed -e 's/:[^:]*$//'`
+ disp=`echo "$orig" | sed -e 's/^.*://'`
+ ipv6=1
+else
+ # regular host:port
+ host=`echo "$orig" | awk -F: '{print $1}'`
+ disp=`echo "$orig" | awk -F: '{print $2}'`
+fi
+
+if [ "X$reverse" != "X" -a "X$STUNNEL_LISTEN" = "X" -a "X$host" != "X" ]; then
+ STUNNEL_LISTEN=$host
+ echo "set STUNNEL_LISTEN=$STUNNEL_LISTEN"
+fi
+
+if [ "X$host" = "X" ]; then
+ host=$localhost
+fi
+
+if [ "X$SSVNC_IPV6" = "X0" ]; then
+ # disable checking for it.
+ ipv6=0
+#elif [ "X$reverse" != "X" -a "X$ipv6" = "X1" ]; then
+# ipv6=0
+elif [ "X$ipv6" = "X1" ]; then
+ :
+elif echo "$host" | grep '^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' > /dev/null; then
+ :
+else
+ # regular hostname, can't be sure...
+ gout=""
+ if type getent > /dev/null 2>/dev/null; then
+ gout=`getent hosts "$host" 2>/dev/null`
+ fi
+ if echo "$gout" | grep ':.*:' > /dev/null; then
+ if echo "$gout" | grep '^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' > /dev/null; then
+ :
+ else
+ echo "ipv6: "`echo "$gout" | grep ':.*:' | head -n 1`
+ ipv6=1
+ fi
+ fi
+ if [ "X$ipv6" = "X0" ]; then
+ hout=""
+ if type host > /dev/null 2>/dev/null; then
+ host "$host" >/dev/null 2>&1
+ host "$host" >/dev/null 2>&1
+ hout=`host "$host" 2>/dev/null`
+ fi
+ if echo "$hout" | grep -i 'has ipv6 address' > /dev/null; then
+ if echo "$hout" | grep -i 'has address' > /dev/null; then
+ :
+ else
+ echo "ipv6: "`echo "$hout" | grep -i 'has ipv6 address' | head -n 1`
+ ipv6=1
+ fi
+ fi
+ fi
+ if [ "X$ipv6" = "X0" ]; then
+ dout=""
+ if type dig > /dev/null 2>/dev/null; then
+ dout=`dig -t any "$host" 2>/dev/null`
+ fi
+ if echo "$dout" | grep -i "^$host" | grep '[ ]AAAA[ ]' > /dev/null; then
+ if echo "$dout" | grep -i "^$host" | grep '[ ]A[ ]' > /dev/null; then
+ :
+ else
+ echo "ipv6: "`echo "$dout" | grep -i '[ ]AAAA[ ]' | head -n 1`
+ ipv6=1
+ fi
+ fi
+ fi
+ if [ "X$ipv6" = "X0" ]; then
+ sout=`env LOOKUP="$host" \
+ perl -e ' eval {use Socket}; exit 0 if $@;
+ eval {use Socket6}; exit 0 if $@;
+ @res = getaddrinfo($ENV{LOOKUP}, "daytime", AF_UNSPEC, SOCK_STREAM);
+ $ipv4 = 0;
+ $ipv6 = 0;
+ $ip6 = "";
+ while (scalar(@res) >= 5) {
+ ($family, $socktype, $proto, $saddr, $canon, @res) = @res;
+ $ipv4 = 1 if $family == AF_INET;
+ $ipv6 = 1 if $family == AF_INET6;
+ if ($family == AF_INET6 && $ip6 eq "") {
+ my ($host, $port) = getnameinfo($saddr, NI_NUMERICHOST | NI_NUMERICSERV);
+ $ip6 = $host;
+ }
+ }
+ if (! $ipv4 && $ipv6) {
+ print "AF_INET6_ONLY: $ENV{LOOKUP}: $ip6\n";
+ }
+ exit 0;
+ ' 2>/dev/null`
+ if echo "$sout" | grep AF_INET6_ONLY > /dev/null; then
+ echo "$sout"
+ ipv6=1
+ fi
+ fi
+fi
+if [ "X$ipv6" = "X1" ]; then
+ echo "ipv6: addr=$host disp=$disp"
+fi
+if [ "X$disp" = "X" ]; then
+ port="" # probably -listen mode.
+elif [ $disp -lt 0 ]; then
+ # negative means use |n| without question:
+ port=`expr 0 - $disp`
+elif [ $disp -lt 200 ]; then
+ # less than 200 means 5900+n
+ if [ "X$reverse" = "X" ]; then
+ port=`expr $disp + 5900`
+ else
+ port=`expr $disp + 5500`
+ fi
+else
+ # otherwise use the number directly, e.g. 443, 2345
+ port=$disp
+fi
+
+if [ "X$ipv6" = "X1" -a "X$direct_connect" = "X1" ]; then
+ if [ "X$proxy" = "X" -a "X$reverse" = "X" ]; then
+ if [ "X$SSVNC_ULTRA_DSM" != "X" -a "X$have_uvnc_dsm_helper_ipv6" = "X1" ]; then
+ :
+ elif [ "X$SSVNC_NO_IPV6_PROXY" != "X" ]; then
+ :
+ elif [ "X$SSVNC_NO_IPV6_PROXY_DIRECT" != "X" ]; then
+ :
+ elif [ "X$SSVNC_USE_OURS" = "X1" ]; then
+ # requires 1.0.27 and later ssvncviewer binary
+ :
+ else
+ proxy="ipv6://$host:$port"
+ echo "direct connect: set proxy=$proxy"
+ fi
+ fi
+fi
+
+# (possibly) tell the vncviewer to only listen on lo:
+if [ "X$reverse" != "X" ]; then
+ if [ "X$direct_connect" = "X" -o "X$proxy" != "X" -o "X$STUNNEL_LISTEN" != "X" ]; then
+ VNCVIEWER_LISTEN_LOCALHOST=1
+ export VNCVIEWER_LISTEN_LOCALHOST
+ fi
+fi
+
+# try to find an open listening port via netstat(1):
+inuse=""
+if uname | grep Linux > /dev/null; then
+ inuse=`netstat -ant | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $4}' | sed 's/^.*://'`
+elif uname | grep SunOS > /dev/null; then
+ inuse=`netstat -an -f inet -P tcp | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $1}' | sed 's/^.*\.//'`
+elif uname | egrep -i 'bsd|darwin' > /dev/null; then
+ inuse=`netstat -ant -f inet | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $4}' | sed 's/^.*\.//'`
+# add others...
+fi
+
+# this is a crude attempt for unique ports tags, etc.
+date_sec=`date +%S`
+
+# these are special cases of no vnc, e.g. sleep or xmessage.
+# these are for using ssvnc as a general port redirector.
+if echo "$VNCVIEWERCMD" | grep '^sleep[ ][ ]*[0-9][0-9]*' > /dev/null; then
+ if [ "X$SS_VNCVIEWER_LISTEN_PORT" = "X" ]; then
+ p=`echo "$VNCVIEWERCMD" | awk '{print $3}'`
+ if [ "X$p" != "X" ]; then
+ SS_VNCVIEWER_LISTEN_PORT=$p
+ fi
+ fi
+ p2=`echo "$VNCVIEWERCMD" | awk '{print $2}'`
+ VNCVIEWERCMD="eval sleep $p2; echo Local "
+elif echo "$VNCVIEWERCMD" | grep '^xmessage[ ][ ]*[0-9][0-9]*' > /dev/null; then
+ if [ "X$SS_VNCVIEWER_LISTEN_PORT" = "X" ]; then
+ p=`echo "$VNCVIEWERCMD" | awk '{print $2}'`
+ SS_VNCVIEWER_LISTEN_PORT=$p
+ fi
+fi
+
+# utility to find a free port to listen on.
+findfree() {
+ try0=$1
+ try=$try0
+ use0=""
+
+ if [ "X$SS_VNCVIEWER_LISTEN_PORT" != "X" ]; then
+ echo "$SS_VNCVIEWER_LISTEN_PORT"
+ return
+ fi
+ if [ $try -ge 6000 ]; then
+ fmax=`expr $try + 1000`
+ else
+ fmax=6000
+ fi
+
+ while [ $try -lt $fmax ]
+ do
+ if [ "X$inuse" = "X" ]; then
+ break
+ fi
+ if echo "$inuse" | grep -w $try > /dev/null; then
+ :
+ else
+ use0=$try
+ break
+ fi
+ try=`expr $try + 1`
+ done
+ if [ "X$use0" = "X" ]; then
+ use0=`expr $date_sec + $try0`
+ fi
+
+ echo $use0
+}
+
+# utility for exiting; kills some helper processes,
+# removes files, etc.
+final() {
+ echo ""
+ if [ "X$tmp_cfg" != "X" ]; then
+ rm -f $tmp_cfg
+ fi
+ if [ "X$SS_VNCVIEWER_RM" != "X" ]; then
+ rm -f $SS_VNCVIEWER_RM 2>/dev/null
+ fi
+ if [ "X$tcert" != "X" ]; then
+ rm -f $tcert
+ fi
+ if [ "X$pssh" != "X" ]; then
+ echo "Terminating background ssh process"
+ echo kill -TERM "$pssh"
+ kill -TERM "$pssh" 2>/dev/null
+ sleep 1
+ kill -KILL "$pssh" 2>/dev/null
+ pssh=""
+ fi
+ if [ "X$stunnel_pid" != "X" ]; then
+ echo "Terminating background stunnel process"
+ echo kill -TERM "$stunnel_pid"
+ kill -TERM "$stunnel_pid" 2>/dev/null
+ sleep 1
+ kill -KILL "$stunnel_pid" 2>/dev/null
+ stunnel_pid=""
+ fi
+ if [ "X$dsm_pid" != "X" ]; then
+ echo "Terminating background ultravnc_dsm_helper process"
+ echo kill -TERM "$dsm_pid"
+ kill -TERM "$dsm_pid" 2>/dev/null
+ sleep 1
+ kill -KILL "$dsm_pid" 2>/dev/null
+ stunnel_pid=""
+ fi
+ if [ "X$tail_pid" != "X" ]; then
+ kill -TERM $tail_pid
+ fi
+ if [ "X$tail_pid2" != "X" ]; then
+ kill -TERM $tail_pid2
+ fi
+}
+
+if [ "X$reverse" = "X" ]; then
+ # normal connections try 5930-5999:
+ if [ "X$showcert" = "X" ]; then
+ use=`findfree 5930`
+ else
+ # move away from normal place for (possibly many) -showcert
+ pstart=`date +%S`
+ pstart=`expr 6130 + $pstart + $pstart`
+ use=`findfree $pstart`
+ fi
+ if [ $use -ge 5900 ]; then
+ N=`expr $use - 5900`
+ else
+ N=$use
+ fi
+else
+ # reverse connections:
+ p2=`expr $port + 30`
+ use=`findfree $p2`
+ if [ $use -ge 5500 ]; then
+ N=`expr $use - 5500`
+ else
+ N=$use
+ fi
+fi
+
+# this is for my special use of ss_vncip -> vncip viewer.
+if echo "$0" | grep vncip > /dev/null; then
+ VNCVIEWERCMD="$VNCIPCMD"
+fi
+
+if echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then
+ :
+elif [ "X$VNCVIEWERCMD_EXTRA_OPTS" != "X" ]; then
+ VNCVIEWERCMD="$VNCVIEWERCMD $VNCVIEWERCMD_EXTRA_OPTS"
+fi
+
+# trick for the undocumented rsh://host:port method.
+rsh_setup() {
+ if echo "$ssh_host" | grep '@' > /dev/null; then
+ ul=`echo "$ssh_host" | awk -F@ '{print $1}'`
+ ul="-l $ul"
+ ssh_host=`echo "$ssh_host" | awk -F@ '{print $2}'`
+ else
+ ul=""
+ fi
+ ssh_cmd=`echo "$ssh_cmd" | sed -e 's/ -localhost/ /g'`
+}
+
+# trick for the undocumented rsh://host:port method.
+rsh_viewer() {
+ trap "final" 0 2 15
+ if [ "X$PORT" = "X" ]; then
+ exit 1
+ elif [ $PORT -ge 5900 ]; then
+ vdpy=`expr $PORT - 5900`
+ else
+ vdpy=":$PORT"
+ fi
+ stty sane
+ echo "$VNCVIEWERCMD" "$@" $ssh_host:$vdpy
+ echo ""
+ $VNCVIEWERCMD "$@" $ssh_host:$vdpy
+ if [ $? != 0 ]; then
+ sleep 2
+ $VNCVIEWERCMD "$@" $ssh_host:$vdpy
+ fi
+}
+
+check_perl() {
+ if type "$1" > /dev/null 2>&1; then
+ :
+ elif [ ! -x "$1" ]; then
+ echo ""
+ echo "*******************************************************"
+ echo "** Problem finding the Perl command '$1': **"
+ echo ""
+ type "perl"
+ echo ""
+ echo "** Perhaps you need to install the Perl package. **"
+ echo "*******************************************************"
+ echo ""
+ sleep 5
+ fi
+}
+
+# this is the PPROXY tool. used only here for now...
+pcode() {
+ tf=$1
+ PPROXY_PROXY=$proxy; export PPROXY_PROXY
+ PPROXY_DEST="$host:$port"; export PPROXY_DEST
+ check_perl /usr/bin/perl
+
+ cod='#!/usr/bin/perl
+
+# A hack to glue stunnel to a Web or SOCKS proxy, UltraVNC repeater for
+# client connections.
+# Also acts as a VeNCrypt bridge (by redirecting to stunnel.)
+
+use IO::Socket::INET;
+
+my $have_inet6 = "";
+eval "use IO::Socket::INET6;";
+$have_inet6 = 1 if $@ eq "";
+
+#my $have_sock6 = "";
+#eval "use Socket; use Socket6;";
+#$have_sock6 = 1 if $@ eq "";
+
+if (exists $ENV{PPROXY_LOOP_THYSELF}) {
+ # used for reverse vnc, run a repeating outer loop.
+ print STDERR "PPROXY_LOOP: $ENV{PPROXY_LOOP_THYSELF}\n";
+ my $rm = $ENV{PPROXY_REMOVE};
+ my $lp = $ENV{PPROXY_LOOP_THYSELF};
+ delete $ENV{PPROXY_REMOVE};
+ delete $ENV{PPROXY_LOOP_THYSELF};
+ $ENV{PPROXY_LOOP_THYSELF_MASTER} = $$;
+ my $pid = $$;
+ my $dbg = 0;
+ my $c = 0;
+ use POSIX ":sys_wait_h";
+ while (1) {
+ $pid = fork();
+ last if ! defined $pid;
+ if ($pid eq "0") {
+ last;
+ }
+ $c++;
+ print STDERR "\nPPROXY_LOOP: pid=$$ child=$pid count=$c\n";
+ while (1) {
+ waitpid(-1, WNOHANG);
+ fsleep(0.25);
+ if (! kill 0, $pid) {
+ print STDERR "PPROXY_LOOP: child=$pid gone.\n";
+ last;
+ }
+ print STDERR "PPROXY_LOOP: child=$pid alive.\n" if $dbg;
+ if (! -f $lp) {
+ print STDERR "PPROXY_LOOP: flag file $lp gone, killing $pid\n";
+ kill TERM, $pid;
+ fsleep(0.1);
+ wait;
+ last;
+ }
+ print STDERR "PPROXY_LOOP: file exists $lp\n" if $dbg;
+ }
+ last if ! -f $lp;
+ fsleep(0.25);
+ }
+ if ($pid ne "0") {
+ unlink($0) if $rm;
+ exit 0;
+ }
+}
+
+if (exists $ENV{PPROXY_SLEEP} && $ENV{PPROXY_SLEEP} > 0) {
+ print STDERR "PPROXY_PID: $$\n";
+ sleep $ENV{PPROXY_SLEEP};
+}
+
+foreach my $var (qw(
+ PPROXY_DEST
+ PPROXY_KILLPID
+ PPROXY_LISTEN
+ PPROXY_PROXY
+ PPROXY_REMOVE
+ PPROXY_REPEATER
+ PPROXY_REVERSE
+ PPROXY_SLEEP
+ PPROXY_SOCKS
+ PPROXY_VENCRYPT
+ PPROXY_VENCRYPT_VIEWER_BRIDGE
+ )) {
+ if (0 || $ENV{SS_DEBUG} || $ENV{SSVNC_VENCRYPT_DEBUG}) {
+ print STDERR "$var: $ENV{$var}\n";
+ }
+}
+
+if ($ENV{PPROXY_SOCKS} ne "" && $ENV{PPROXY_PROXY} !~ m,^socks5?://,i) {
+ if ($ENV{PPROXY_SOCKS} eq "5") {
+ $ENV{PPROXY_PROXY} = "socks5://$ENV{PPROXY_PROXY}";
+ } else {
+ $ENV{PPROXY_PROXY} = "socks://$ENV{PPROXY_PROXY}";
+ }
+}
+
+my $rfbSecTypeAnonTls = 18;
+my $rfbSecTypeVencrypt = 19;
+
+my $rfbVencryptPlain = 256;
+my $rfbVencryptTlsNone = 257;
+my $rfbVencryptTlsVnc = 258;
+my $rfbVencryptTlsPlain = 259;
+my $rfbVencryptX509None = 260;
+my $rfbVencryptX509Vnc = 261;
+my $rfbVencryptX509Plain = 262;
+
+my $handshake_file = "";
+if (exists $ENV{SSVNC_PREDIGESTED_HANDSHAKE}) {
+ $handshake_file = $ENV{SSVNC_PREDIGESTED_HANDSHAKE};
+}
+
+my $have_gettimeofday = 0;
+eval "use Time::HiRes;";
+if ($@ eq "") {
+ $have_gettimeofday = 1;
+}
+sub gettime {
+ my $t = "0.0";
+ if ($have_gettimeofday) {
+ $t = Time::HiRes::gettimeofday();
+ }
+ return $t;
+}
+
+my $listen_handle = "";
+my $sock = "";
+my $parent = $$;
+
+my $initial_data = "";
+
+if ($ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}) {
+ my ($from, $to) = split(/,/, $ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE});
+ do_vencrypt_viewer_bridge($from, $to);
+ exit 0;
+}
+
+my ($first, $second, $third) = split(/,/, $ENV{PPROXY_PROXY}, 3);
+my ($mode_1st, $mode_2nd, $mode_3rd) = ("", "", "");
+
+($first, $mode_1st) = url_parse($first);
+
+my ($proxy_host, $proxy_port) = ($first, "");
+if ($proxy_host =~ /^(.*):(\d+)$/) {
+ $proxy_host = $1;
+ $proxy_port = $2;
+}
+my $connect = $ENV{PPROXY_DEST};
+
+if ($second ne "") {
+ ($second, $mode_2nd) = url_parse($second);
+}
+
+if ($third ne "") {
+ ($third, $mode_3rd) = url_parse($third);
+}
+
+
+print STDERR "\n";
+print STDERR "PPROXY v0.4: a tool for Web, SOCKS, and UltraVNC proxies and for\n";
+print STDERR "PPROXY v0.4: IPv6 and VNC VeNCrypt bridging.\n";
+print STDERR "proxy_host: $proxy_host\n";
+print STDERR "proxy_port: $proxy_port\n";
+print STDERR "proxy_connect: $connect\n";
+print STDERR "pproxy_params: $ENV{PPROXY_PROXY}\n";
+print STDERR "pproxy_listen: $ENV{PPROXY_LISTEN}\n";
+print STDERR "pproxy_reverse: $ENV{PPROXY_REVERSE}\n";
+print STDERR "io_socket_inet6: $have_inet6\n";
+print STDERR "\n";
+if (! $have_inet6) {
+ print STDERR "PPROXY: To enable IPv6 connections, install the IO::Socket::INET6 perl module.\n\n";
+}
+
+if (1) {
+ print STDERR "pproxy 1st: $first\t- $mode_1st\n";
+ print STDERR "pproxy 2nd: $second\t- $mode_2nd\n";
+ print STDERR "pproxy 3rd: $third\t- $mode_3rd\n";
+ print STDERR "\n";
+}
+
+sub pdie {
+ my $msg = shift;
+ kill_proxy_pids();
+ die "$msg";
+}
+
+if ($ENV{PPROXY_REVERSE} ne "") {
+ my ($rhost, $rport) = ($ENV{PPROXY_REVERSE}, "");
+ if ($rhost =~ /^(.*):(\d+)$/) {
+ $rhost = $1;
+ $rport = $2;
+ }
+ $rport = 5900 unless $rport;
+ my $emsg = "";
+ $listen_handle = IO::Socket::INET->new(
+ PeerAddr => $rhost,
+ PeerPort => $rport,
+ Proto => "tcp"
+ );
+ $emsg = $!;
+ if (! $listen_handle && $have_inet6) {
+ eval {$listen_handle = IO::Socket::INET6->new(
+ PeerAddr => $rhost,
+ PeerPort => $rport,
+ Proto => "tcp"
+ );};
+ $emsg .= " / $!";
+ }
+ if (! $listen_handle) {
+ pdie "pproxy: $emsg -- PPROXY_REVERSE\n";
+ }
+ print STDERR "PPROXY_REVERSE: connected to $rhost $rport\n";
+
+} elsif ($ENV{PPROXY_LISTEN} ne "") {
+ my $listen_sock = "";
+ my $maxtry = 12;
+ my $sleep = 5;
+ my $p2 = "";
+ my $emsg = "";
+ for (my $i=0; $i < $maxtry; $i++) {
+ my ($if, $p) = ("", $ENV{PPROXY_LISTEN});
+ if ($p =~ /^(.*):(\d+)$/) {
+ $if = $1;
+ $p = $2;
+ }
+ $p2 = "*:$p";
+ if ($if eq "") {
+ $if = "localhost";
+ }
+ print STDERR "pproxy interface: $if\n";
+
+ $emsg = "";
+ if (($if eq "INADDR_ANY6" || $if eq "::") && $have_inet6) {
+ eval {$listen_sock = IO::Socket::INET6->new(
+ Listen => 2,
+ ReuseAddr => 1,
+ Domain => AF_INET6,
+ LocalAddr => "::",
+ LocalPort => $p,
+ Proto => "tcp"
+ );};
+ $p2 = ":::$p";
+ } elsif ($if =~ /^INADDR_ANY/) {
+ $listen_sock = IO::Socket::INET->new(
+ Listen => 2,
+ ReuseAddr => 1,
+ LocalPort => $p,
+ Proto => "tcp"
+ );
+ } elsif (($if eq "INADDR_LOOPBACK6" || $if eq "::1") && $have_inet6) {
+ $p2 = "::1:$p";
+ eval {$listen_sock = IO::Socket::INET6->new(
+ Listen => 2,
+ ReuseAddr => 1,
+ Domain => AF_INET6,
+ LocalAddr => "::1",
+ LocalPort => $p,
+ Proto => "tcp"
+ );};
+ $p2 = "::1:$p";
+ } else {
+ $p2 = "$if:$p";
+ $listen_sock = IO::Socket::INET->new(
+ Listen => 2,
+ ReuseAddr => 1,
+ LocalAddr => $if,
+ LocalPort => $p,
+ Proto => "tcp"
+ );
+ $emsg = $!;
+
+ if (! $listen_sock && $have_inet6) {
+ print STDERR "PPROXY_LISTEN: retry with INET6\n";
+ eval {$listen_sock = IO::Socket::INET6->new(
+ Listen => 2,
+ ReuseAddr => 1,
+ Domain => AF_INET6,
+ LocalAddr => $if,
+ LocalPort => $p,
+ Proto => "tcp"
+ );};
+ $emsg .= " / $!";
+ }
+ }
+ if (! $listen_sock) {
+ if ($i < $maxtry - 1) {
+ warn "pproxy: $emsg $!\n";
+ warn "Could not listen on port $p2, retrying in $sleep seconds... (Ctrl-C to quit)\n";
+ sleep $sleep;
+ }
+ } else {
+ last;
+ }
+ }
+ if (! $listen_sock) {
+ pdie "pproxy: $emsg -- PPROXY_LISTEN\n";
+ }
+ print STDERR "pproxy: listening on $p2\n";
+ my $ip;
+ ($listen_handle, $ip) = $listen_sock->accept();
+ my $err = $!;
+ close $listen_sock;
+ if (! $listen_handle) {
+ pdie "pproxy: $err\n";
+ }
+
+ if ($ENV{PPROXY_LOOP_THYSELF_MASTER}) {
+ my $sml = $ENV{SSVNC_MULTIPLE_LISTEN};
+ if ($sml ne "" && $sml ne "0") {
+ setpgrp(0, 0);
+ if (fork()) {
+ close $viewer_sock;
+ wait;
+ exit 0;
+ }
+ if (fork()) {
+ close $viewer_sock;
+ exit 0;
+ }
+ setpgrp(0, 0);
+ $parent = $$;
+ }
+ }
+}
+
+$sock = IO::Socket::INET->new(
+ PeerAddr => $proxy_host,
+ PeerPort => $proxy_port,
+ Proto => "tcp"
+);
+
+my $err = "";
+
+if (! $sock && $have_inet6) {
+ $err = $!;
+
+ print STDERR "pproxy: $!\n";
+
+ eval {$sock = IO::Socket::INET6->new(
+ PeerAddr => $proxy_host,
+ PeerPort => $proxy_port,
+ Proto => "tcp"
+ );};
+ $err .= " / $!";
+}
+
+if (! $sock && ($proxy_host =~ /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i || $proxy_host =~ /^::ffff:([\da-f]+:[\da-f]+)$/i)) {
+ print STDERR "pproxy: $!\n";
+ my $ipv4_addr = $1;
+ if ($ipv4_addr =~ /:/) {
+ my ($a, $b) = split(/:/, $ipv4_addr);
+ $a = hex($a);
+ $b = hex($b);
+ $ipv4_addr = sprintf("%d.", ($a & 0xff00) >> 8);
+ $ipv4_addr .= sprintf("%d.", ($a & 0x00ff));
+ $ipv4_addr .= sprintf("%d.", ($b & 0xff00) >> 8);
+ $ipv4_addr .= sprintf("%d", ($b & 0x00ff));
+ }
+
+ print STDERR "pproxy: re-trying with ipv4 addr: $ipv4_addr\n";
+
+ eval {$sock = IO::Socket::INET->new(
+ PeerAddr => $ipv4_addr,
+ PeerPort => $proxy_port,
+ Proto => "tcp"
+ );};
+ $err .= " / $!";
+}
+
+if (! $sock) {
+ unlink($0) if $ENV{PPROXY_REMOVE};
+ pdie "pproxy: $err\n";
+}
+
+unlink($0) if $ENV{PPROXY_REMOVE};
+
+if ($ENV{PPROXY_PROXY} =~ /^vencrypt:/ && $ENV{PPROXY_VENCRYPT_REVERSE}) {
+ print STDERR "\nPPROXY: vencrypt+reverse: swapping listen socket with connect socket.\n";
+ my $tmp_swap = $sock;
+ $sock = $listen_handle;
+ $listen_handle = $tmp_swap;
+}
+
+$cur_proxy = $first;
+setmode($mode_1st);
+
+if ($second ne "") {
+ connection($second, 1);
+
+ setmode($mode_2nd);
+ $cur_proxy = $second;
+
+ if ($third ne "") {
+ connection($third, 2);
+ setmode($mode_3rd);
+ $cur_proxy = $third;
+ connection($connect, 3);
+ } else {
+ connection($connect, 2);
+ }
+} else {
+ connection($connect, 1);
+}
+
+sub kill_proxy_pids() {
+ if ($ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}) {
+ return;
+ }
+ if ($ENV{PPROXY_KILLPID}) {
+ foreach my $p (split(/,/, $ENV{PPROXY_KILLPID})) {
+ if ($p =~ /^(\+|-)/) {
+ $p = $parent + $p;
+ }
+ print STDERR "kill TERM, $p (PPROXY_KILLPID)\n";
+ kill "TERM", $p;
+ }
+ }
+}
+
+sub xfer {
+ my($in, $out) = @_;
+ $RIN = $WIN = $EIN = "";
+ $ROUT = "";
+ vec($RIN, fileno($in), 1) = 1;
+ vec($WIN, fileno($in), 1) = 1;
+ $EIN = $RIN | $WIN;
+
+ while (1) {
+ my $nf = 0;
+ while (! $nf) {
+ $nf = select($ROUT=$RIN, undef, undef, undef);
+ }
+ my $len = sysread($in, $buf, 8192);
+ if (! defined($len)) {
+ next if $! =~ /^Interrupted/;
+ print STDERR "pproxy[$$]: $!\n";
+ last;
+ } elsif ($len == 0) {
+ print STDERR "pproxy[$$]: Input is EOF.\n";
+ last;
+ }
+ my $offset = 0;
+ my $quit = 0;
+ while ($len) {
+ my $written = syswrite($out, $buf, $len, $offset);
+ if (! defined $written) {
+ print STDERR "pproxy[$$]: Output is EOF. $!\n";
+ $quit = 1;
+ last;
+ }
+ $len -= $written;
+ $offset += $written;
+ }
+ last if $quit;
+ }
+ close($out);
+ close($in);
+ print STDERR "pproxy[$$]: finished xfer.\n";
+}
+
+sub handler {
+ print STDERR "pproxy[$$]: got SIGTERM.\n";
+ close $listen_handle if $listen_handle;
+ close $sock if $sock;
+ exit;
+}
+
+sub xfer_both {
+ $child = fork;
+
+ if (! defined $child) {
+ kill_proxy_pids();
+ exit 1;
+ }
+
+ $SIG{TERM} = "handler";
+
+ if ($child) {
+ if ($listen_handle) {
+ print STDERR "pproxy parent[$$] listen_handle -> socket\n";
+ xfer($listen_handle, $sock);
+ } else {
+ print STDERR "pproxy parent[$$] STDIN -> socket\n";
+ xfer(STDIN, $sock);
+ }
+ select(undef, undef, undef, 0.25);
+ if (kill 0, $child) {
+ select(undef, undef, undef, 0.9);
+ if (kill 0, $child) {
+ print STDERR "pproxy[$$]: kill TERM child $child\n";
+ kill "TERM", $child;
+ } else {
+ print STDERR "pproxy[$$]: child $child gone.\n";
+ }
+ }
+ } else {
+ select(undef, undef, undef, 0.05);
+ if ($listen_handle) {
+ print STDERR "pproxy child [$$] socket -> listen_handle\n";
+ if ($initial_data ne "") {
+ my $len = length $initial_data;
+ print STDERR "pproxy child [$$] sending initial_data, length $len\n\n";
+ syswrite($listen_handle, $initial_data, $len);
+ } else {
+ print STDERR "\n";
+ }
+ xfer($sock, $listen_handle);
+ } else {
+ print STDERR "pproxy child [$$] socket -> STDOUT\n";
+ if ($initial_data ne "") {
+ my $len = length $initial_data;
+ print STDERR "pproxy child [$$] sending initial_data, length $len\n\n";
+ syswrite(STDOUT, $initial_data, $len);
+ } else {
+ print STDERR "\n";
+ }
+ xfer($sock, STDOUT);
+ }
+ select(undef, undef, undef, 0.25);
+ if (kill 0, $parent) {
+ select(undef, undef, undef, 0.8);
+ if (kill 0, $parent) {
+ print STDERR "pproxy[$$]: kill TERM parent $parent\n";
+ kill "TERM", $parent;
+ } else {
+ print STDERR "pproxy[$$]: parent $parent gone.\n";
+ }
+ }
+ }
+
+ kill_proxy_pids();
+}
+
+xfer_both();
+
+exit;
+
+sub fsleep {
+ select(undef, undef, undef, shift);
+}
+
+sub url_parse {
+ my $hostport = shift;
+ my $mode = "http";
+ if ($hostport =~ m,^socks4?://(\S*)$,i) {
+ $mode = "socks4";
+ $hostport = $1;
+ } elsif ($hostport =~ m,^socks5://(\S*)$,i) {
+ $mode = "socks5";
+ $hostport = $1;
+ } elsif ($hostport =~ m,^https?://(\S*)$,i) {
+ $mode = "http";
+ $hostport = $1;
+ } elsif ($hostport =~ m,^ipv6://(\S*)$,i) {
+ $mode = "ipv6";
+ $hostport = $1;
+ } elsif ($hostport =~ m,^repeater://(\S*)\+(\S*)$,i) {
+ # ultravnc repeater proxy.
+ $hostport = $1;
+ $mode = "repeater:$2";
+ if ($hostport !~ /:\d+$/) {
+ $hostport .= ":5900";
+ }
+ } elsif ($hostport =~ m,^vencrypt://(\S*)$,i) {
+ # vencrypt handshake.
+ $hostport = $1;
+ my $m = "connect";
+ if ($hostpost =~ /^(\S+)\+(\S+)$/) {
+ $hostport = $1;
+ $mode = $2;
+ }
+ $mode = "vencrypt:$m";
+ if ($hostport !~ /:\d+$/) {
+ $hostport .= ":5900";
+ }
+ }
+ return ($hostport, $mode);
+}
+
+sub setmode {
+ my $mode = shift;
+ $ENV{PPROXY_REPEATER} = "";
+ $ENV{PPROXY_VENCRYPT} = "";
+ if ($mode =~ /^socks/) {
+ if ($mode =~ /^socks5/) {
+ $ENV{PPROXY_SOCKS} = 5;
+ } else {
+ $ENV{PPROXY_SOCKS} = 1;
+ }
+ } elsif ($mode =~ /^ipv6/i) {
+ $ENV{PPROXY_SOCKS} = 0;
+ } elsif ($mode =~ /^repeater:(.*)/) {
+ $ENV{PPROXY_REPEATER} = $1;
+ $ENV{PPROXY_SOCKS} = "";
+ } elsif ($mode =~ /^vencrypt:(.*)/) {
+ $ENV{PPROXY_VENCRYPT} = $1;
+ $ENV{PPROXY_SOCKS} = "";
+ } else {
+ $ENV{PPROXY_SOCKS} = "";
+ }
+}
+
+sub connection {
+ my ($CONNECT, $w) = @_;
+
+ my $con = "";
+ my $msg = "";
+
+ if ($ENV{PPROXY_SOCKS} eq "5") {
+ # SOCKS5
+ my ($h, $p) = ($CONNECT, "");
+ if ($h =~ /^(.*):(\d+)$/) {
+ $h = $1;
+ $p = $2;
+ }
+ $con .= pack("C", 0x05);
+ $con .= pack("C", 0x01);
+ $con .= pack("C", 0x00);
+
+ $msg = "SOCKS5 via $cur_proxy to $h:$p\n\n";
+ print STDERR "proxy_request$w: $msg";
+
+ syswrite($sock, $con, length($con));
+
+ my ($n1, $n2, $n3, $n4, $n5, $n6);
+ my ($r1, $r2, $r3, $r4, $r5, $r6);
+ my ($s1, $s2, $s3, $s4, $s5, $s6);
+
+ $n1 = sysread($sock, $r1, 1);
+ $n2 = sysread($sock, $r2, 1);
+
+ $s1 = unpack("C", $r1);
+ $s2 = unpack("C", $r2);
+ if ($s1 != 0x05 || $s2 != 0x00) {
+ print STDERR "SOCKS5 fail s1=$s1 s2=$s2 n1=$n1 n2=$n2\n";
+ close $sock;
+ exit(1);
+ }
+
+ $con = "";
+ $con .= pack("C", 0x05);
+ $con .= pack("C", 0x01);
+ $con .= pack("C", 0x00);
+ $con .= pack("C", 0x03);
+ $con .= pack("C", length($h));
+ $con .= $h;
+ $con .= pack("C", $p >> 8);
+ $con .= pack("C", $p & 0xff);
+
+ syswrite($sock, $con, length($con));
+
+ $n1 = sysread($sock, $r1, 1);
+ $n2 = sysread($sock, $r2, 1);
+ $n3 = sysread($sock, $r3, 1);
+ $n4 = sysread($sock, $r4, 1);
+ $s1 = unpack("C", $r1);
+ $s2 = unpack("C", $r2);
+ $s3 = unpack("C", $r3);
+ $s4 = unpack("C", $r4);
+
+ if ($s4 == 0x1) {
+ sysread($sock, $r5, 4 + 2);
+ } elsif ($s4 == 0x3) {
+ sysread($sock, $r5, 1);
+ $s5 = unpack("C", $r5);
+ sysread($sock, $r6, $s5 + 2);
+ } elsif ($s4 == 0x4) {
+ sysread($sock, $r5, 16 + 2);
+ }
+
+ if ($s1 != 0x5 || $s2 != 0x0 || $s3 != 0x0) {
+ print STDERR "SOCKS5 failed: s1=$s1 s2=$s2 s3=$s3 s4=$s4 n1=$n1 n2=$n2 n3=$n3 n4=$n4\n";
+ close $sock;
+ exit(1);
+ }
+
+ } elsif ($ENV{PPROXY_SOCKS} eq "1") {
+ # SOCKS4 SOCKS4a
+ my ($h, $p) = ($CONNECT, "");
+ if ($h =~ /^(.*):(\d+)$/) {
+ $h = $1;
+ $p = $2;
+ }
+ $con .= pack("C", 0x04);
+ $con .= pack("C", 0x01);
+ $con .= pack("n", $p);
+
+ my $SOCKS_4a = 0;
+ if ($h eq "localhost" || $h eq "127.0.0.1") {
+ $con .= pack("C", 127);
+ $con .= pack("C", 0);
+ $con .= pack("C", 0);
+ $con .= pack("C", 1);
+ } elsif ($h =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
+ $con .= pack("C", $1);
+ $con .= pack("C", $2);
+ $con .= pack("C", $3);
+ $con .= pack("C", $4);
+ } else {
+ $con .= pack("C", 0);
+ $con .= pack("C", 0);
+ $con .= pack("C", 0);
+ $con .= pack("C", 3);
+ $SOCKS_4a = 1;
+ }
+
+ $con .= "nobody";
+ $con .= pack("C", 0);
+
+ $msg = "SOCKS4 via $cur_proxy to $h:$p\n\n";
+ if ($SOCKS_4a) {
+ $con .= $h;
+ $con .= pack("C", 0);
+ $msg =~ s/SOCKS4/SOCKS4a/;
+ }
+ print STDERR "proxy_request$w: $msg";
+ syswrite($sock, $con, length($con));
+
+ my $ok = 1;
+ for (my $i = 0; $i < 8; $i++) {
+ my $c;
+ sysread($sock, $c, 1);
+ my $s = unpack("C", $c);
+ if ($i == 0) {
+ $ok = 0 if $s != 0x0;
+ } elsif ($i == 1) {
+ $ok = 0 if $s != 0x5a;
+ }
+ }
+ if (! $ok) {
+ print STDERR "SOCKS4 failed.\n";
+ close $sock;
+ exit(1);
+ }
+ } elsif ($ENV{PPROXY_SOCKS} eq "0") {
+ # hack for ipv6 "proxy", nothing to do, assume INET6 call worked.
+ ;
+ } elsif ($ENV{PPROXY_REPEATER} ne "") {
+ my $rep = $ENV{PPROXY_REPEATER};
+ print STDERR "repeater: $rep\n";
+ $rep .= pack("x") x 250;
+ syswrite($sock, $rep, 250);
+
+ my $rfb = "";
+
+ my $ok = 1;
+ for (my $i = 0; $i < 12; $i++) {
+ my $c;
+ last if $ENV{PPROXY_GENERIC_REPEATER};
+ sysread($sock, $c, 1);
+ print STDERR $c;
+ $rfb .= $c;
+ }
+ if ($rfb ne "" && $rfb !~ /^RFB 000\.000/) {
+ $initial_data = $rfb;
+ $rfb =~ s/\n//g;
+ print STDERR "detected non-UltraVNC repeater; forwarding \"$rfb\"\nlength: ", length($initial_data), "\n";
+ }
+ } elsif ($ENV{PPROXY_VENCRYPT} ne "") {
+ my $vencrypt = $ENV{PPROXY_VENCRYPT};
+ vencrypt_dialog($vencrypt);
+
+ } else {
+ # Web Proxy:
+ $con = "CONNECT $CONNECT HTTP/1.1\r\n";
+ $con .= "Host: $CONNECT\r\n";
+ $con .= "Connection: close\r\n\r\n";
+ $msg = $con;
+
+ print STDERR "proxy_request$w: via $cur_proxy:\n$msg";
+ syswrite($sock, $con, length($con));
+
+ my $rep = "";
+ my $n = 0;
+ while ($rep !~ /\r\n\r\n/ && $n < 30000) {
+ my $c;
+ sysread($sock, $c, 1);
+ print STDERR $c;
+ $rep .= $c;
+ $n++;
+ }
+ if ($rep !~ m,HTTP/.* 200,) {
+ print STDERR "HTTP CONNECT failed.\n";
+ close $sock;
+ exit(1);
+ }
+ }
+}
+
+sub vdie {
+ append_handshake("done\n");
+ close $sock;
+ kill_proxy_pids();
+ exit(1);
+}
+
+sub anontls_handshake {
+ my ($vmode, $db) = @_;
+
+ print STDERR "\nPPROXY: Doing ANONTLS Handshake\n";
+
+ my $psec = pack("C", $rfbSecTypeAnonTls);
+ syswrite($sock, $psec, 1);
+
+ append_handshake("done\n");
+}
+
+sub vencrypt_handshake {
+
+ my ($vmode, $db) = @_;
+
+ print STDERR "\nPPROXY: Doing VeNCrypt Handshake\n";
+
+ my $psec = pack("C", $rfbSecTypeVencrypt);
+
+ if (exists $ENV{SSVNC_TEST_SEC_TYPE}) {
+ my $fake = $ENV{SSVNC_TEST_SEC_TYPE};
+ print STDERR "PPROXY: sending sec-type: $fake\n";
+ $psec = pack("C", $fake);
+ }
+
+ syswrite($sock, $psec, 1);
+
+ my $vmajor;
+ my $vminor;
+ sysread($sock, $vmajor, 1);
+ sysread($sock, $vminor, 1);
+
+ vdie if $vmajor eq "" || $vminor eq "";
+
+ $vmajor = unpack("C", $vmajor);
+ $vminor = unpack("C", $vminor);
+ print STDERR "server vencrypt version $vmajor.$vminor\n" if $db;
+
+ if (exists $ENV{SSVNC_TEST_SEC_TYPE}) {
+ print STDERR "PPROXY: continuing on in test mode.\n";
+ } else {
+ vdie if $vmajor ne 0;
+ vdie if $vminor < 2;
+ }
+
+ $vmajor = pack("C", 0);
+ $vminor = pack("C", 2);
+ append_handshake("subversion=0.2\n");
+
+ syswrite($sock, $vmajor, 1);
+ syswrite($sock, $vminor, 1);
+
+ my $result;
+ sysread($sock, $result, 1);
+ print STDERR "result empty\n" if $db && $result eq "";
+
+ vdie if $result eq "";
+ $result = unpack("C", $result);
+ print STDERR "result=$result\n" if $db;
+
+ vdie if $result ne 0;
+
+ my $nsubtypes;
+ sysread($sock, $nsubtypes, 1);
+
+ vdie if $nsubtypes eq "";
+ $nsubtypes = unpack("C", $nsubtypes);
+ print STDERR "nsubtypes=$nsubtypes\n" if $db;
+
+ my %subtypes;
+
+ for (my $i = 0; $i < $nsubtypes; $i++) {
+ my $subtype = "";
+ sysread($sock, $subtype, 4);
+ vdie if length($subtype) != 4;
+
+ # XXX fix 64bit.
+ $subtype = unpack("N", $subtype);
+ print STDERR "subtype: $subtype\n" if $db;
+ $subtypes{$subtype} = 1;
+ append_handshake("sst$i=$subtype\n");
+ }
+
+ my $subtype = 0;
+ if (exists $subtypes{$rfbVencryptX509None}) {
+ $subtype = $rfbVencryptX509None;
+ print STDERR "selected rfbVencryptX509None\n" if $db;
+ } elsif (exists $subtypes{$rfbVencryptX509Vnc}) {
+ $subtype = $rfbVencryptX509Vnc;
+ print STDERR "selected rfbVencryptX509Vnc\n" if $db;
+ } elsif (exists $subtypes{$rfbVencryptX509Plain}) {
+ $subtype = $rfbVencryptX509Plain;
+ print STDERR "selected rfbVencryptX509Plain\n" if $db;
+ } elsif (exists $subtypes{$rfbVencryptTlsNone}) {
+ $subtype = $rfbVencryptTlsNone;
+ print STDERR "selected rfbVencryptTlsNone\n" if $db;
+ } elsif (exists $subtypes{$rfbVencryptTlsVnc}) {
+ $subtype = $rfbVencryptTlsVnc;
+ print STDERR "selected rfbVencryptTlsVnc\n" if $db;
+ } elsif (exists $subtypes{$rfbVencryptTlsPlain}) {
+ $subtype = $rfbVencryptTlsPlain;
+ print STDERR "selected rfbVencryptTlsPlain\n" if $db;
+ }
+
+ if (exists $ENV{SSVNC_TEST_SEC_SUBTYPE}) {
+ my $fake = $ENV{SSVNC_TEST_SEC_SUBTYPE};
+ print STDERR "PPROXY: sending sec-subtype: $fake\n";
+ $subtype = $fake;
+ }
+
+ append_handshake("subtype=$subtype\n");
+
+ my $pst = pack("N", $subtype);
+ syswrite($sock, $pst, 4);
+
+ if (exists $ENV{SSVNC_TEST_SEC_SUBTYPE}) {
+ print STDERR "PPROXY: continuing on in test mode.\n";
+ } else {
+ vdie if $subtype == 0;
+ }
+
+ my $ok;
+ sysread($sock, $ok, 1);
+ $ok = unpack("C", $ok);
+ print STDERR "ok=$ok\n" if $db;
+
+ append_handshake("done\n");
+
+ vdie if $ok == 0;
+}
+
+sub vencrypt_dialog {
+ my $vmode = shift;
+ my $db = 0;
+
+ $db = 1 if exists $ENV{SS_DEBUG};
+ $db = 1 if exists $ENV{SSVNC_VENCRYPT_DEBUG};
+
+ append_handshake("mode=$vmode\n");
+
+ my $server_rfb = "";
+ #syswrite($sock, $rep, 250);
+ for (my $i = 0; $i < 12; $i++) {
+ my $c;
+ sysread($sock, $c, 1);
+ $server_rfb .= $c;
+ print STDERR $c;
+ }
+ print STDERR "server_rfb: $server_rfb\n" if $db;
+ append_handshake("server=$server_rfb");
+
+ my $minor = "";
+ if ($server_rfb =~ /^RFB 003\.(\d+)/) {
+ $minor = $1;
+ } else {
+ vdie;
+ }
+ my $viewer_rfb = "RFB 003.008\n";
+ if ($minor < 7) {
+ vdie;
+ } elsif ($minor == 7) {
+ $viewer_rfb = "RFB 003.007\n";
+ }
+ my $nsec;
+ my $t1 = gettime();
+ my $t0 = gettime();
+
+ syswrite($sock, $viewer_rfb, 12);
+ sysread($sock, $nsec, 1);
+
+ $t1 = gettime();
+ $t1 = sprintf("%.6f", $t1 - $t0);
+
+ append_handshake("viewer=$viewer_rfb");
+ append_handshake("latency=$t1\n");
+
+ vdie if $nsec eq "";
+
+ $nsec = unpack("C", $nsec);
+
+ print STDERR "nsec: $nsec\n" if $db;
+ vdie if $nsec eq 0 || $nsec > 100;
+
+ my %sectypes = ();
+
+ for (my $i = 0; $i < $nsec; $i++) {
+ my $sec;
+ sysread($sock, $sec, 1);
+ vdie if $sec eq "";
+ $sec = unpack("C", $sec);
+ print STDERR "sec: $sec\n" if $db;
+ $sectypes{$sec} = 1;
+ }
+
+ if (exists $sectypes{$rfbSecTypeVencrypt}) {
+ print STDERR "found rfbSecTypeVencrypt\n" if $db;
+ append_handshake("sectype=$rfbSecTypeVencrypt\n");
+ vencrypt_handshake($vmode, $db);
+ } elsif (exists $sectypes{$rfbSecTypeAnonTls}) {
+ print STDERR "found rfbSecTypeAnonTls\n" if $db;
+ append_handshake("sectype=$rfbSecTypeAnonTls\n");
+ anontls_handshake($vmode, $db);
+ } else {
+ print STDERR "No supported sec-type found\n" if $db;
+ vdie;
+ }
+}
+
+sub append_handshake {
+ my $str = shift;
+ if ($handshake_file) {
+ if (open(HSF, ">>$handshake_file")) {
+ print HSF $str;
+ close HSF;
+ }
+ }
+}
+
+sub do_vencrypt_viewer_bridge {
+ my ($listen, $connect) = @_;
+ print STDERR "\npproxy: starting vencrypt_viewer_bridge[$$]: $listen \-> $connect\n";
+ my $db = 0;
+ my $backwards = 0;
+ if ($listen < 0) {
+ $backwards = 1;
+ $listen = -$listen;
+ }
+ if ($handshake_file eq "") {
+ die "pproxy: vencrypt_viewer_bridge[$$]: no SSVNC_PREDIGESTED_HANDSHAKE\n";
+ }
+ my $listen_sock;
+ my $maxtry = 12;
+ my $sleep = 5;
+ for (my $i=0; $i < $maxtry; $i++) {
+ $listen_sock = IO::Socket::INET->new(
+ Listen => 2,
+ ReuseAddr => 1,
+ LocalAddr => "127.0.0.1",
+ LocalPort => $listen,
+ Proto => "tcp"
+ );
+ if (! $listen_sock) {
+ if ($i < $maxtry - 1) {
+ warn "pproxy: vencrypt_viewer_bridge[$$]: $!\n";
+ warn "Could not listen on port $listen, retrying in $sleep seconds... (Ctrl-C to quit)\n";
+ sleep $sleep;
+ }
+ } else {
+ last;
+ }
+ }
+ if (! $listen_sock) {
+ die "pproxy: vencrypt_viewer_bridge[$$]: $!\n";
+ }
+ print STDERR "pproxy: vencrypt_viewer_bridge[$$]: listening on port $listen\n\n";
+ my ($viewer_sock, $ip) = $listen_sock->accept();
+ my $err = $!;
+ close $listen_sock;
+ if (! $viewer_sock) {
+ die "pproxy: vencrypt_viewer_bridge[$$]: $err\n";
+ }
+ if ($ENV{PPROXY_LOOP_THYSELF_MASTER}) {
+ my $sml = $ENV{SSVNC_MULTIPLE_LISTEN};
+ if ($sml ne "" && $sml ne "0") {
+ setpgrp(0, 0);
+ if (fork()) {
+ close $viewer_sock;
+ wait;
+ exit 0;
+ }
+ if (fork()) {
+ close $viewer_sock;
+ exit 0;
+ }
+ setpgrp(0, 0);
+ $parent = $$;
+ }
+ }
+ print STDERR "vencrypt_viewer_bridge[$$]: viewer_sock $viewer_sock\n" if $db;
+
+ print STDERR "pproxy: vencrypt_viewer_bridge[$$]: connecting to 127.0.0.1:$connect\n";
+ my $server_sock = IO::Socket::INET->new(
+ PeerAddr => "127.0.0.1",
+ PeerPort => $connect,
+ Proto => "tcp"
+ );
+ print STDERR "vencrypt_viewer_bridge[$$]: server_sock $server_sock\n" if $db;
+ if (! $server_sock) {
+ my $err = $!;
+ die "pproxy: vencrypt_viewer_bridge[$$]: $err\n";
+ }
+
+ if ($backwards) {
+ print STDERR "vencrypt_viewer_bridge[$$]: reversing roles of viewer and server.\n";
+ my $t = $viewer_sock;
+ $viewer_sock = $server_sock;
+ $server_sock = $t;
+ }
+
+ my %hs = ();
+ my $dt = 0.2;
+ my $slept = 0.0;
+ while ($slept < 20.0) {
+ select(undef, undef, undef, $dt);
+ $slept += $dt;
+ if (-f $handshake_file && open(HSF, "<$handshake_file")) {
+ my $done = 0;
+ %hs = ();
+ my $str = "";
+ while (<HSF>) {
+ print STDERR "vencrypt_viewer_bridge[$$]: $_" if $ENV{VENCRYPT_VIEWER_BRIDGE_DEBUG};
+ $str .= "vencrypt_viewer_bridge[$$]: $_";
+ chomp;
+ if ($_ eq "done") {
+ $done = 1;
+ } else {
+ my ($k, $v) = split(/=/, $_, 2);
+ if ($k ne "" && $v ne "") {
+ $hs{$k} = $v;
+ }
+ }
+ }
+ close HSF;
+ if ($done) {
+ print STDERR "\n" . $str;
+ last;
+ }
+ }
+ }
+ if (! exists $hs{server}) {
+ $hs{server} = "RFB 003.008";
+ }
+ if (! exists $hs{sectype}) {
+ unlink($handshake_file);
+ die "pproxy: vencrypt_viewer_bridge[$$]: no sectype.\n";
+ }
+ syswrite($viewer_sock, "$hs{server}\n", length($hs{server}) + 1);
+ my $viewer_rfb = "";
+ for (my $i = 0; $i < 12; $i++) {
+ my $c;
+ sysread($viewer_sock, $c, 1);
+ $viewer_rfb .= $c;
+ print STDERR $c;
+ }
+ my $viewer_major = 3;
+ my $viewer_minor = 8;
+ if ($viewer_rfb =~ /RFB (\d+)\.(\d+)/) {
+ $viewer_major = $1;
+ $viewer_minor = $2;
+ }
+ my $u0 = pack("C", 0);
+ my $u1 = pack("C", 1);
+ my $u2 = pack("C", 2);
+ if ($hs{sectype} == $rfbSecTypeAnonTls) {
+ unlink($handshake_file);
+ print STDERR "\npproxy: vencrypt_viewer_bridge[$$]: rfbSecTypeAnonTls\n";
+ if ($viewer_major > 3 || $viewer_minor >= 7) {
+ ; # setup ok, proceed to xfer.
+ } else {
+ print STDERR "pproxy: vencrypt_viewer_bridge[$$]: faking RFB version 3.3 to viewer.\n";
+ my $n;
+ sysread($server_sock, $n, 1);
+ $n = unpack("C", $n);
+ if ($n == 0) {
+ die "pproxy: vencrypt_viewer_bridge[$$]: nsectypes == $n.\n";
+ }
+ my %types;
+ for (my $i = 0; $i < $n; $i++) {
+ my $t;
+ sysread($server_sock, $t, 1);
+ $t = unpack("C", $t);
+ $types{$t} = 1;
+ }
+ my $use = 1; # None
+ if (exists $types{1}) {
+ $use = 1; # None
+ } elsif (exists $types{2}) {
+ $use = 2; # VncAuth
+ } else {
+ die "pproxy: vencrypt_viewer_bridge[$$]: no valid sectypes" . join(",", keys %types) . "\n";
+ }
+
+ # send 4 bytes sectype to viewer:
+ # (note this should be MSB, network byte order...)
+ my $up = pack("C", $use);
+ syswrite($viewer_sock, $u0, 1);
+ syswrite($viewer_sock, $u0, 1);
+ syswrite($viewer_sock, $u0, 1);
+ syswrite($viewer_sock, $up, 1);
+ # and tell server the one we selected:
+ syswrite($server_sock, $up, 1);
+ if ($use == 1) {
+ # even None has security result, so read it here and discard it.
+ my $sr = "";
+ sysread($server_sock, $sr, 4);
+ }
+ }
+ } elsif ($hs{sectype} == $rfbSecTypeVencrypt) {
+ print STDERR "\npproxy: vencrypt_viewer_bridge[$$]: rfbSecTypeVencrypt\n";
+ if (! exists $hs{subtype}) {
+ unlink($handshake_file);
+ die "pproxy: vencrypt_viewer_bridge[$$]: no subtype.\n";
+ }
+ my $fake_type = "None";
+ my $plain = 0;
+ my $sub_type = $hs{subtype};
+ if ($sub_type == $rfbVencryptTlsNone) {
+ $fake_type = "None";
+ } elsif ($sub_type == $rfbVencryptTlsVnc) {
+ $fake_type = "VncAuth";
+ } elsif ($sub_type == $rfbVencryptTlsPlain) {
+ $fake_type = "None";
+ $plain = 1;
+ } elsif ($sub_type == $rfbVencryptX509None) {
+ $fake_type = "None";
+ } elsif ($sub_type == $rfbVencryptX509Vnc) {
+ $fake_type = "VncAuth";
+ } elsif ($sub_type == $rfbVencryptX509Plain) {
+ $fake_type = "None";
+ $plain = 1;
+ }
+ if ($plain) {
+ if (!open(W, ">$handshake_file")) {
+ unlink($handshake_file);
+ die "pproxy: vencrypt_viewer_bridge[$$]: $handshake_file $!\n";
+ }
+ print W <<"END";
+
+ proc print_out {} {
+ global user pass env
+
+ if [info exists env(SSVNC_UP_DEBUG)] {
+ toplevel .b
+ button .b.b -text "user=\$user pass=\$pass" -command {destroy .b}
+ pack .b.b
+ update
+ tkwait window .b
+ }
+
+ if [info exists env(SSVNC_UP_FILE)] {
+ set fh ""
+ catch {set fh [open \$env(SSVNC_UP_FILE) w]}
+ if {\$fh != ""} {
+ puts \$fh user=\$user\\npass=\$pass
+ flush \$fh
+ close \$fh
+ return
+ }
+ }
+ puts stdout user=\$user\\npass=\$pass
+ flush stdout
+ }
+
+ proc center_win {w} {
+ update
+ set W [winfo screenwidth \$w]
+ set W [expr \$W + 1]
+ wm geometry \$w +\$W+0
+ update
+ set x [expr [winfo screenwidth \$w]/2 - [winfo width \$w]/2]
+ set y [expr [winfo screenheight \$w]/2 - [winfo height \$w]/2]
+
+ wm geometry \$w +\$x+\$y
+ wm deiconify \$w
+ update
+ }
+
+ wm withdraw .
+
+ global env
+ set up {}
+ if [info exists env(SSVNC_UNIXPW)] {
+ set rm 0
+ set up \$env(SSVNC_UNIXPW)
+ if [regexp {^rm:} \$up] {
+ set rm 1
+ regsub {^rm:} \$up {} up
+ }
+ if [file exists \$up] {
+ set fh ""
+ set f \$up
+ catch {set fh [open \$up r]}
+ if {\$fh != ""} {
+ gets \$fh u
+ gets \$fh p
+ close \$fh
+ set up "\$u@\$p"
+ }
+ if {\$rm} {
+ catch {file delete \$f}
+ }
+ }
+ } elseif [info exists env(SSVNC_VENCRYPT_USERPASS)] {
+ set up \$env(SSVNC_VENCRYPT_USERPASS)
+ }
+ #puts stderr up=\$up
+ if {\$up != ""} {
+ if [regexp {@} \$up] {
+ global user pass
+ set user \$up
+ set pass \$up
+ regsub {@.*\$} \$user "" user
+ regsub {^[^@]*@} \$pass "" pass
+ print_out
+ exit
+ }
+ }
+
+ wm title . {VeNCrypt Viewer Bridge User/Pass}
+
+ set user {}
+ set pass {}
+
+ label .l -text {SSVNC VeNCrypt Viewer Bridge}
+
+ frame .f0
+ frame .f0.fL
+ label .f0.fL.la -text {Username: }
+ label .f0.fL.lb -text {Password: }
+
+ pack .f0.fL.la .f0.fL.lb -side top
+
+ frame .f0.fR
+ entry .f0.fR.ea -width 24 -textvariable user
+ entry .f0.fR.eb -width 24 -textvariable pass -show *
+
+ pack .f0.fR.ea .f0.fR.eb -side top -fill x
+
+ pack .f0.fL -side left
+ pack .f0.fR -side right -expand 1 -fill x
+
+ button .no -text Cancel -command {destroy .}
+ button .ok -text Done -command {print_out; destroy .}
+
+ center_win .
+ pack .l .f0 .no .ok -side top -fill x
+ update
+ wm deiconify .
+
+ bind .f0.fR.ea <Return> {focus .f0.fR.eb}
+ bind .f0.fR.eb <Return> {print_out; destroy .}
+ focus .f0.fR.ea
+
+ wm resizable . 1 0
+ wm minsize . [winfo reqwidth .] [winfo reqheight .]
+END
+ close W;
+
+ #system("cat $handshake_file");
+ my $w = "wish";
+ if ($ENV{WISH}) {
+ $w = $ENV{WISH};
+ }
+ print STDERR "pproxy: vencrypt_viewer_bridge[$$]: prompt VencryptPlain user and passwd.\n";
+ my $res = "";
+ if (`uname` =~ /Darwin/) {
+ my $mtmp = `mktemp /tmp/hsup.XXXXXX`;
+ chomp $mtmp;
+ system("env SSVNC_UP_FILE=$mtmp $w $handshake_file");
+ $res = `cat $mtmp`;
+ unlink $mtmp;
+ } else {
+ $res = `$w $handshake_file`;
+ }
+ my $user = "";
+ my $pass = "";
+ if ($res =~ /user=(\S*)/) {
+ $user = $1;
+ }
+ if ($res =~ /pass=(\S*)/) {
+ $pass = $1;
+ }
+ print STDERR "pproxy: vencrypt_viewer_bridge[$$]: sending VencryptPlain user and passwd.\n";
+ my $ulen = pack("C", length($user));
+ my $plen = pack("C", length($pass));
+ # (note this should be MSB, network byte order...)
+ syswrite($server_sock, $u0, 1);
+ syswrite($server_sock, $u0, 1);
+ syswrite($server_sock, $u0, 1);
+ syswrite($server_sock, $ulen, 1);
+ syswrite($server_sock, $u0, 1);
+ syswrite($server_sock, $u0, 1);
+ syswrite($server_sock, $u0, 1);
+ syswrite($server_sock, $plen, 1);
+ syswrite($server_sock, $user, length($user));
+ syswrite($server_sock, $pass, length($pass));
+ }
+ unlink($handshake_file);
+
+ my $ft = 0;
+ if ($fake_type eq "None") {
+ $ft = 1;
+ } elsif ($fake_type eq "VncAuth") {
+ $ft = 2;
+ } else {
+ die "pproxy: vencrypt_viewer_bridge[$$]: unknown fake type: $fake_type\n";
+ }
+ my $fp = pack("C", $ft);
+ if ($viewer_major > 3 || $viewer_minor >= 7) {
+ syswrite($viewer_sock, $u1, 1);
+ syswrite($viewer_sock, $fp, 1);
+ my $cr;
+ sysread($viewer_sock, $cr, 1);
+ $cr = unpack("C", $cr);
+ if ($cr != $ft) {
+ die "pproxy: vencrypt_viewer_bridge[$$]: client selected wrong type: $cr / $ft\n";
+ }
+ } else {
+ print STDERR "pproxy: vencrypt_viewer_bridge[$$]: faking RFB version 3.3 to viewer.\n";
+ # send 4 bytes sect type to viewer:
+ # (note this should be MSB, network byte order...)
+ syswrite($viewer_sock, $u0, 1);
+ syswrite($viewer_sock, $u0, 1);
+ syswrite($viewer_sock, $u0, 1);
+ syswrite($viewer_sock, $fp, 1);
+ if ($ft == 1) {
+ # even None has security result, so read it here and discard it.
+ my $sr = "";
+ sysread($server_sock, $sr, 4);
+ }
+ }
+ }
+
+ $listen_handle = $viewer_sock;
+ $sock = $server_sock;
+
+ xfer_both();
+}
+'
+ # '
+ # xpg_echo will expand \n \r, etc.
+ # try to unset and then test for it.
+ if type shopt > /dev/null 2>&1; then
+ shopt -u xpg_echo >/dev/null 2>&1
+ fi
+ v='print STDOUT "abc\n";'
+ echo "$v" > $tf
+ chmod 700 $tf
+
+ lc=`wc -l $tf | awk '{print $1}'`
+ if [ "X$lc" = "X1" ]; then
+ echo "$cod" > $tf
+ else
+ printf "%s" "$cod" > $tf
+ echo "" >> $tf
+ fi
+ # prime perl
+ perl -e 'use IO::Socket::INET; select(undef, undef, undef, 0.01)' >/dev/null 2>&1
+}
+
+# make_tcert is no longer invoked via the ssvnc gui (Listen mode).
+# make_tcert is for testing only now via -mycert BUILTIN
+make_tcert() {
+ tcert="/tmp/ss_vnc_viewer_tcert${RANDOM}.$$"
+ tcert=`mytmp "$tcert"`
+ cat > $tcert <<END
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAvkfXxb0wcxgrjV2ziFikjII+ze8iKcTBt47L0GM/c21efelN
++zZpJUUXLu4zz8Ryq8Q+sQgfNy7uTOpN9bUUaOk1TnD7gaDQnQWiNHmqbW2kL+DS
+OKngJVPo9dETAS8hf7+D1e1DBZxjTc1a4RQqWJixwpYj99ixWzu8VC2m/xXsjvOs
+jp4+DLBB490nbkwvstmhmiWm1CmI5O5xOkgioVNQqHvQMdVKOSz9PpbjvZiRX1Uo
+qoMrk+2NOqwP90TB35yPASXb9zXKpO7DLhkube+yYGf+yk46aD707L07Eb7cosFP
+S84vNZ9gX7rQ0UOwm5rYA/oZTBskgaqhtIzkLwIDAQABAoIBAD4ot/sXt5kRn0Ca
+CIkU9AQWlC+v28grR2EQW9JiaZrqcoDNUzUqbCTJsi4ZkIFh2lf0TsqELbZYNW6Y
+6AjJM7al4E0UqYSKJTv2WCuuRxdiRs2BMwthqyBmjeanev7bB6V0ybt7u3Y8xU/o
+MrTuYnr4vrEjXPKdLirwk7AoDbKsRXHSIiHEIBOq1+dUQ32t36ukdnnza4wKDLZc
+PKHiCdCk/wOGhuDlxD6RspqUAlRnJ8/aEhrgWxadFXw1hRhRsf/v1shtB0T3DmTe
+Jchjwyiw9mryb9JZAcKxW+fUc4EVvj6VdQGqYInQJY5Yxm5JAlVQUJicuuJEvn6A
+rj5osQECgYEA552CaHpUiFlB4HGkjaH00kL+f0+gRF4PANCPk6X3UPDVYzKnzmuu
+yDvIdEETGFWBwoztUrOOKqVvPEQ+kBa2+DWWYaERZLtg2cI5byfDJxQ3ldzilS3J
+1S3WgCojqcsG/hlxoQJ1dZFanUy/QhUZ0B+wlC+Zp1Q8AyuGQvhHp68CgYEA0lBI
+eqq2GGCdJuNHMPFbi8Q0BnX55LW5C1hWjhuYiEkb3hOaIJuJrqvayBlhcQa2cGqp
+uP34e9UCfoeLgmoCQ0b4KpL2NGov/mL4i8bMgog4hcoYuIi3qxN18vVR14VKEh4U
+RLk0igAYPU+IK2QByaQlBo9OSaKkcfm7U1/pK4ECgYAxr6VpGk0GDvfF2Tsusv6d
+GIgV8ZP09qSLTTJvvxvF/lQYeqZq7sjI5aJD5i3de4JhpO/IXQJzfZfWOuGc8XKA
+3qYK/Y2IqXXGYRcHFGWV/Y1LFd55mCADHlk0l1WdOBOg8P5iRu/Br9PbiLpCx9oI
+vrOXpnp03eod1/luZmqguwKBgQCWFRSj9Q7ddpSvG6HCG3ro0qsNsUMTI1tZ7UBX
+SPogx4tLf1GN03D9ZUZLZVFUByZKMtPLX/Hi7K9K/A9ikaPrvsl6GEX6QYzeTGJx
+3Pw0amFrmDzr8ySewNR6/PXahxPEuhJcuI31rPufRRI3ZLah3rFNbRbBFX+klkJH
+zTnoAQKBgDbUK/aQFGduSy7WUT7LlM3UlGxJ2sA90TQh4JRQwzur0ACN5GdYZkqM
+YBts4sBJVwwJoxD9OpbvKu3uKCt41BSj0/KyoBzjT44S2io2tj1syujtlVUsyyBy
+/ca0A7WBB8lD1D7QMIhYUm2O9kYtSCLlUTHt5leqGaRG38DqlX36
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDzDCCArQCCQDSzxzxqhyqLzANBgkqhkiG9w0BAQQFADCBpzELMAkGA1UEBhMC
+VVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxDzANBgNVBAcTBkJvc3RvbjETMBEG
+A1UEChMKTXkgQ29tcGFueTEcMBoGA1UECxMTUHJvZHVjdCBEZXZlbG9wbWVudDEZ
+MBcGA1UEAxMQd3d3Lm5vd2hlcmUubm9uZTEhMB8GCSqGSIb3DQEJARYSYWRtaW5A
+bm93aGVyZS5ub25lMB4XDTA3MDMyMzE4MDc0NVoXDTI2MDUyMjE4MDc0NVowgacx
+CzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1NYXNzYWNodXNldHRzMQ8wDQYDVQQHEwZC
+b3N0b24xEzARBgNVBAoTCk15IENvbXBhbnkxHDAaBgNVBAsTE1Byb2R1Y3QgRGV2
+ZWxvcG1lbnQxGTAXBgNVBAMTEHd3dy5ub3doZXJlLm5vbmUxITAfBgkqhkiG9w0B
+CQEWEmFkbWluQG5vd2hlcmUubm9uZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAL5H18W9MHMYK41ds4hYpIyCPs3vIinEwbeOy9BjP3NtXn3pTfs2aSVF
+Fy7uM8/EcqvEPrEIHzcu7kzqTfW1FGjpNU5w+4Gg0J0FojR5qm1tpC/g0jip4CVT
+6PXREwEvIX+/g9XtQwWcY03NWuEUKliYscKWI/fYsVs7vFQtpv8V7I7zrI6ePgyw
+QePdJ25ML7LZoZolptQpiOTucTpIIqFTUKh70DHVSjks/T6W472YkV9VKKqDK5Pt
+jTqsD/dEwd+cjwEl2/c1yqTuwy4ZLm3vsmBn/spOOmg+9Oy9OxG+3KLBT0vOLzWf
+YF+60NFDsJua2AP6GUwbJIGqobSM5C8CAwEAATANBgkqhkiG9w0BAQQFAAOCAQEA
+vGomHEp6TVU83X2EBUgnbOhzKJ9u3fOI/Uf5L7p//Vxqow7OR1cguzh/YEzmXOIL
+ilMVnzX9nj/bvcLAuqEP7MR1A8f4+E807p/L/Sf49BiCcwQq5I966sGKYXjkve+T
+2GTBNwMSq+5kLSf6QY8VZI+qnrAudEQMeJByQhTZZ0dH8Njeq8EGl9KUio+VWaiW
+CQK6xJuAvAHqa06OjLmwu1fYD4GLGSrOIiRVkSXV8qLIUmzxdJaIRznkFWsrCEKR
+wAH966SAOvd2s6yOHMvyDRIL7WHxfESB6rDHsdIW/yny1fBePjv473KrxyXtbz7I
+dMw1yW09l+eEo4A7GzwOdw==
+-----END CERTIFICATE-----
+END
+ chmod 600 $tcert
+ echo "$tcert"
+}
+
+Kecho() {
+ NO_KECHO=1
+ if [ "X$USER" = "Xrunge" -a "X$NO_KECHO" = "X" ]; then
+ echo "dbg: $*"
+ fi
+}
+
+NHAFL_warning() {
+ echo ""
+ echo "** Warning: For the proxy: $proxy"
+ echo "** Warning: the ssh(1) option: $ssh_NHAFL"
+ echo "** Warning: will be used to avoid frequent 'ssh key has changed for localhost'"
+ echo "** Warning: dialogs and connection failures (for example, ssh will exit asking"
+ echo "** Warning: you to manually remove a key from ~/.ssh/known_hosts.)"
+ echo "** Warning: "
+ echo "** Warning: This decreases security: a Man-In-The-Middle attack is possible."
+ echo "** Warning: For chained ssh connections the first ssh leg is secure but the"
+ echo "** Warning: 2nd ssh leg is vulnerable. For an ssh connection going through"
+ echo "** Warning: a HTTP or SOCKS proxy the ssh connection is vulnerable."
+ echo "** Warning: "
+ echo "** Warning: You can set the SSVNC_SSH_LOCALHOST_AUTH=1 env. var. to disable"
+ echo "** Warning: using the NoHostAuthenticationForLocalhost=yes ssh option."
+ echo "** Warning: "
+ echo "** Warning: A better solution is to configure (in the SSVNC GUI) the setting:"
+ echo "** Warning: 'Options -> Advanced -> Private SSH KnownHosts file' (or set"
+ echo "** Warning: SSVNC_KNOWN_HOSTS_FILE directly) to a per-connection known hosts"
+ echo "** Warning: file. That file holds the 'localhost' cert for this specific"
+ echo "** Warning: connection. This yields a both secure and convenient solution."
+ echo ""
+}
+
+space_expand() {
+ str=`echo "$1" | sed -e 's/%SPACE/ /g' -e 's/%TAB/\t/g'`
+ echo "$str"
+}
+
+# handle ssh case:
+#
+if [ "X$use_ssh" = "X1" ]; then
+ #
+ # USING SSH
+ #
+ ssh_port="22"
+ ssh_host="$host"
+ vnc_host="$localhost"
+ ssh_UKHF=""
+ localhost_extra=""
+ # let user override ssh via $SSH
+ ssh=${SSH:-"ssh -x"}
+
+ sshword=`echo "$ssh" | awk '{print $1}'`
+ if [ "X$sshword" != "X" ]; then
+ if [ -x "$sshword" ]; then
+ :
+ elif type "$sshword" > /dev/null 2>&1; then
+ :
+ else
+ echo ""
+ echo "*********************************************************"
+ echo "** Problem finding the SSH command '$sshword': **"
+ echo ""
+ type "$sshword"
+ echo ""
+ echo "** Perhaps you need to install the SSH client package. **"
+ echo "*********************************************************"
+ echo ""
+ sleep 5
+ fi
+ fi
+
+ ssh_NHAFL="-o NoHostAuthenticationForLocalhost=yes"
+ if [ "X$SSVNC_SSH_LOCALHOST_AUTH" = "X1" ]; then
+ ssh_NHAFL=""
+ fi
+ if [ "X$SSVNC_KNOWN_HOSTS_FILE" != "X" ]; then
+ ssh_NHAFL=""
+
+ ssh_UKHF="-o UserKnownHostsFile=$SSVNC_KNOWN_HOSTS_FILE"
+ ssh_args="$ssh_args $ssh_UKHF"
+ if [ ! -f "$SSVNC_KNOWN_HOSTS_FILE" ]; then
+ touch "$SSVNC_KNOWN_HOSTS_FILE" >/dev/null 2>&1
+ fi
+ chmod 600 "$SSVNC_KNOWN_HOSTS_FILE" >/dev/null 2>&1
+ fi
+ did_ssh_NHAFL=""
+
+ if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ]; then
+ SSVNC_LIM_ACCEPT_PRELOAD="$SSVNC_BASEDIR/$SSVNC_UNAME/$SSVNC_LIM_ACCEPT_PRELOAD"
+ fi
+ if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ]; then
+ echo ""
+ echo "SSVNC_LIM_ACCEPT_PRELOAD=$SSVNC_LIM_ACCEPT_PRELOAD"
+ fi
+
+ if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" -a -f "$SSVNC_LIM_ACCEPT_PRELOAD" ]; then
+ plvar=LD_PRELOAD
+ if uname | grep Darwin >/dev/null; then
+ plvar="DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES"
+ fi
+ ssh="env $plvar=$SSVNC_LIM_ACCEPT_PRELOAD $ssh"
+ else
+ SSVNC_LIM_ACCEPT_PRELOAD=""
+ fi
+
+ ssh_vencrypt_proxy=""
+ # We handle vencrypt for SSH+SSL mode.
+ if echo "$proxy" | grep 'vencrypt://' > /dev/null; then
+ proxynew=""
+ for part in `echo "$proxy" | tr ',' ' '`
+ do
+ if echo "$part" | egrep -i '^vencrypt://' > /dev/null; then
+ ssh_vencrypt_proxy=$part
+ else
+ if [ "X$proxynew" = "X" ]; then
+ proxynew="$part"
+ else
+ proxynew="$proxynew,$part"
+ fi
+ fi
+ done
+ proxy=$proxynew
+ fi
+ Kecho ssh_vencrypt_proxy=$ssh_vencrypt_proxy
+
+ # note that user must supply http:// for web proxy in SSH and SSH+SSL.
+ # No xxxx:// implies ssh server+port.
+ #
+ if echo "$proxy" | egrep '(http|https|socks|socks4|socks5)://' > /dev/null; then
+ # Handle Web or SOCKS proxy(ies) for the initial connect.
+ Kecho host=$host
+ Kecho port=$port
+ pproxy=""
+ sproxy1=""
+ sproxy_rest=""
+ for part in `echo "$proxy" | tr ',' ' '`
+ do
+ Kecho proxy_part=$part
+ if [ "X$part" = "X" ]; then
+ continue
+ elif echo "$part" | egrep -i '^(http|https|socks|socks4|socks5)://' > /dev/null; then
+ pproxy="$pproxy,$part"
+ else
+ if [ "X$sproxy1" = "X" ]; then
+ sproxy1="$part"
+ else
+ sproxy_rest="$sproxy_rest,$part"
+ fi
+ fi
+ done
+ pproxy=`echo "$pproxy" | sed -e 's/^,,*//' -e 's/,,*/,/g'`
+ sproxy_rest=`echo "$sproxy_rest" | sed -e 's/^,,*//' -e 's/,,*/,/g'`
+
+ Kecho pproxy=$pproxy
+ Kecho sproxy1=$sproxy1
+ Kecho sproxy_rest=$sproxy_rest
+
+ sproxy1_host=""
+ sproxy1_port=""
+ sproxy1_user=""
+
+ if [ "X$sproxy1" != "X" ]; then
+ # XXX fix ipv6 ip adder here and below.
+ sproxy1_host=`echo "$sproxy1" | awk -F: '{print $1}'`
+ sproxy1_user=`echo "$sproxy1_host" | awk -F@ '{print $1}'`
+ sproxy1_host=`echo "$sproxy1_host" | awk -F@ '{print $2}'`
+ if [ "X$sproxy1_host" = "X" ]; then
+ sproxy1_host=$sproxy1_user
+ sproxy1_user=""
+ else
+ sproxy1_user="${sproxy1_user}@"
+ fi
+ sproxy1_port=`echo "$sproxy1" | awk -F: '{print $2}'`
+ if [ "X$sproxy1_port" = "X" ]; then
+ sproxy1_port="22"
+ fi
+ else
+ sproxy1_host=`echo "$host" | awk -F: '{print $1}'`
+ sproxy1_user=`echo "$sproxy1_host" | awk -F@ '{print $1}'`
+ sproxy1_host=`echo "$sproxy1_host" | awk -F@ '{print $2}'`
+ if [ "X$sproxy1_host" = "X" ]; then
+ sproxy1_host=$sproxy1_user
+ sproxy1_user=""
+ else
+ sproxy1_user="${sproxy1_user}@"
+ fi
+ sproxy1_port=`echo "$host" | awk -F: '{print $2}'`
+ if [ "X$sproxy1_port" = "X" ]; then
+ sproxy1_port="22"
+ fi
+ fi
+
+ Kecho sproxy1_host=$sproxy1_host
+ Kecho sproxy1_port=$sproxy1_port
+ Kecho sproxy1_user=$sproxy1_user
+
+ ptmp="/tmp/ss_vncviewer_ssh${RANDOM}.$$.pl"
+ ptmp=`mytmp "$ptmp"`
+ PPROXY_REMOVE=1; export PPROXY_REMOVE
+ proxy=$pproxy
+ port_save=$port
+ host_save=$host
+ if [ "X$sproxy1_host" != "X" ]; then
+ host=$sproxy1_host
+ fi
+ if [ "X$sproxy1_port" != "X" ]; then
+ port=$sproxy1_port
+ fi
+ host=`echo "$host" | sed -e 's/^.*@//'`
+ port=`echo "$port" | sed -e 's/^.*://'`
+ pcode "$ptmp"
+ port=$port_save
+ host=$host_save
+
+ nd=`findfree 6600`
+ PPROXY_LISTEN=$nd; export PPROXY_LISTEN
+ # XXX no reverse forever PPROXY_LOOP_THYSELF ...
+ $ptmp &
+ sleep 1
+ if [ "X$ssh_NHAFL" != "X" -a "X$did_ssh_NHAFL" != "X1" ]; then
+ NHAFL_warning
+ ssh_args="$ssh_args $ssh_NHAFL"
+ did_ssh_NHAFL=1
+ fi
+ sleep 1
+ if [ "X$sproxy1" = "X" ]; then
+ u=""
+ if echo "$host" | grep '@' > /dev/null; then
+ u=`echo "$host" | sed -e 's/@.*$/@/'`
+ fi
+
+ proxy="${u}$localhost:$nd"
+ else
+ proxy="${sproxy1_user}$localhost:$nd"
+ fi
+ localhost_extra=".2"
+ if [ "X$sproxy_rest" != "X" ]; then
+ proxy="$proxy,$sproxy_rest"
+ fi
+ Kecho proxy=$proxy
+ fi
+
+ if echo "$proxy" | grep "," > /dev/null; then
+
+ proxy1=`echo "$proxy" | awk -F, '{print $1}'`
+ proxy2=`echo "$proxy" | awk -F, '{print $2}'`
+
+ # user1@gw1.com:port1,user2@ws2:port2
+ ssh_host1=`echo "$proxy1" | awk -F: '{print $1}'`
+ ssh_port1=`echo "$proxy1" | awk -F: '{print $2}'`
+ if [ "X$ssh_port1" != "X" ]; then
+ ssh_port1="-p $ssh_port1"
+ fi
+ ssh_host2=`echo "$proxy2" | awk -F: '{print $1}'`
+ ssh_user2=`echo "$ssh_host2" | awk -F@ '{print $1}'`
+ ssh_host2=`echo "$ssh_host2" | awk -F@ '{print $2}'`
+ if [ "X$ssh_host2" = "X" ]; then
+ ssh_host2=$ssh_user2
+ ssh_user2=""
+ else
+ ssh_user2="${ssh_user2}@"
+ fi
+ ssh_port2=`echo "$proxy2" | awk -F: '{print $2}'`
+ if [ "X$ssh_port2" = "X" ]; then
+ ssh_port2="22"
+ fi
+ proxport=`findfree 3500`
+ if [ "X$ssh_NHAFL" != "X" -a "X$did_ssh_NHAFL" != "X1" ]; then
+ NHAFL_warning
+ did_ssh_NHAFL=1
+ sleep 1
+ fi
+ echo
+ echo "Running 1st ssh proxy:"
+ ukhf=""
+ if [ "X$ssh_UKHF" != "X" ]; then
+ ukhf="$ssh_UKHF$localhost_extra"
+ fi
+ if echo "$ssh_host1" | grep '%' > /dev/null; then
+ uath=`space_expand "$ssh_host1"`
+ else
+ uath="$ssh_host1"
+ fi
+ echo "$ssh -f -x $ssh_port1 $targ -e none $ssh_NHAFL $ukhf -L $proxport:$ssh_host2:$ssh_port2 \"$uath\" \"sleep 30\""
+ echo ""
+ $ssh -f -x $ssh_port1 $targ -e none $ssh_NHAFL $ukhf -L $proxport:$ssh_host2:$ssh_port2 "$uath" "sleep 30"
+ ssh_args="$ssh_args $ssh_NHAFL"
+ sleep 1
+ stty sane
+ proxy="${ssh_user2}$localhost:$proxport"
+ fi
+
+ if [ "X$proxy" != "X" ]; then
+ ssh_port=`echo "$proxy" | awk -F: '{print $2}'`
+ if [ "X$ssh_port" = "X" ]; then
+ ssh_port="22"
+ fi
+ ssh_host=`echo "$proxy" | awk -F: '{print $1}'`
+ vnc_host="$host"
+ fi
+
+ echo ""
+ echo "Running ssh:"
+ sz=`echo "$ssh_cmd" | wc -c`
+ if [ "$sz" -gt 300 ]; then
+ info="..."
+ else
+ info="$ssh_cmd"
+ fi
+
+ C=""
+ if [ "X$SS_VNCVIEWER_USE_C" != "X" ]; then
+ C="-C"
+ fi
+
+ getport=""
+ teeport=""
+ if echo "$ssh_cmd" | egrep "(PORT=|P=) " > /dev/null; then
+ getport=1
+ if echo "$ssh_cmd" | egrep "P= " > /dev/null; then
+ teeport=1
+ fi
+
+ PORT=""
+ ssh_cmd=`echo "$ssh_cmd" | sed -e 's/PORT=[ ]*//' -e 's/P=//'`
+ SSVNC_NO_ENC_WARN=1
+ if [ "X$use_sshssl" = "X" ]; then
+ direct_connect=1
+ fi
+ fi
+ if [ "X$getport" != "X" ]; then
+ ssh_redir="-D ${use}"
+ elif [ "X$reverse" = "X" ]; then
+ ssh_redir="-L ${use}:${vnc_host}:${port}"
+ else
+ ssh_redir="-R ${port}:${vnc_host}:${use}"
+ fi
+ pmark=`sh -c 'echo $$'`
+
+ # the -t option actually speeds up typing response via VNC!!
+ if [ "X$ssh_port" = "X22" ]; then
+ ssh_port=""
+ else
+ ssh_port="-p $ssh_port"
+ fi
+
+ if echo "$ssh_host" | grep '%' > /dev/null; then
+ uath=`space_expand "$ssh_host"`
+ else
+ uath="$ssh_host"
+ fi
+ if [ "X$SS_VNCVIEWER_SSH_ONLY" != "X" ]; then
+ echo "$ssh -x $ssh_port $targ $C $ssh_args \"$uath\" \"$info\""
+ echo ""
+ $ssh -x $ssh_port $targ $C $ssh_args "$uath" "$ssh_cmd"
+ exit $?
+
+ elif [ "X$SS_VNCVIEWER_NO_F" != "X" ]; then
+ echo "$ssh -x $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\""
+ echo ""
+ $ssh -x $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd"
+ rc=$?
+
+ elif [ "X$getport" != "X" ]; then
+ tport=/tmp/ss_vncviewer_tport${RANDOM}.$$
+ tport=`mytmp "$tport"`
+ tport2=/tmp/ss_vncviewer_tport2${RANDOM}.$$
+ tport2=`mytmp "$tport2"`
+
+ if [ "X$rsh" != "X1" ]; then
+ if echo "$ssh_cmd" | grep "sudo " > /dev/null; then
+ echo ""
+ echo "Initial ssh with 'sudo id' to prime sudo so hopefully the next one"
+ echo "will require no password..."
+ echo ""
+ targ="-t"
+ $ssh -x $ssh_port $targ $ssh_args "$uath" "sudo id; tty"
+ echo ""
+ fi
+ echo "$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\""
+ echo ""
+ $ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" > $tport 2> $tport2
+ if [ "X$teeport" = "X1" ]; then
+ tail -f $tport 1>&2 &
+ tail_pid=$!
+ tail -f $tport2 1>&2 &
+ tail_pid2=$!
+ fi
+ rc=$?
+ else
+ rsh_setup
+ echo "rsh $ul \"$ssh_host\" \"$ssh_cmd\""
+ echo ""
+ rsh $ul "$ssh_host" "$ssh_cmd" > $tport &
+ sleep 1
+ rc=0
+ fi
+
+ if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then
+ echo "sleep $SSVNC_EXTRA_SLEEP"
+ sleep $SSVNC_EXTRA_SLEEP
+ fi
+
+ stty sane
+ i=0
+ if type perl > /dev/null 2>&1; then
+ imax=50
+ sleepit="perl -e 'select(undef, undef, undef, 0.20)'"
+ else
+ imax=10
+ sleepit="sleep 1"
+ fi
+ while [ $i -lt $imax ]; do
+ #echo $sleepit
+ eval $sleepit
+ PORT=`grep "^PORT=" $tport | tr '\r' ' ' | head -n 1 | sed -e 's/PORT=//' -e 's/\r//g' -e 's/ *$//'`
+ if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then
+ break
+ fi
+ vnss=`sed -e 's/\r//g' $tport $tport2 | egrep -i '^(New.* desktop is|A VNC server is already running).*:[0-9[0-9]*$' | head -n 1 | awk '{print $NF}'`
+ if [ "X$vnss" != "X" ]; then
+ PORT=`echo "$vnss" | awk -F: '{print $2}'`
+ if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then
+ if [ $PORT -lt 100 ]; then
+ PORT=`expr $PORT + 5900`
+ fi
+ fi
+ if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then
+ vnss=`sed -e 's/\r//g' $tport | egrep -i '^(New.* desktop is|A VNC server is already running).*:[0-9[0-9]*$' | head -n 1`
+ echo "vncserver string: $vnss" 1>&2
+ break
+ fi
+ fi
+ i=`expr $i + 1`
+ done
+
+ echo "found: PORT='$PORT'" 1>&2
+ lh6=""
+ if [ "X$SSVNC_PORT_IPV6" != "X" ]; then
+ lh6=1
+ elif egrep 'Info: listening on IPv6 only|Info: listening only on IPv6' $tport > /dev/null; then
+ lh6=1
+ fi
+ if [ "X$lh6" = "X1" ]; then
+ echo "set SOCKS5 localhost to ::1" 1>&2
+ fi
+ rm -f $tport $tport2
+ if [ "X$rsh" = "X1" ]; then
+ rsh_viewer "$@"
+ exit $?
+ fi
+ PPROXY_SOCKS=5
+ if [ "X$SSVNC_SOCKS5" != "X" ]; then
+ PPROXY_SOCKS=5
+ elif [ "X$SSVNC_SOCKS4" != "X" ]; then
+ PPROXY_SOCKS=1
+ fi
+ export PPROXY_SOCKS
+ if [ "X$lh6" = "X" ]; then
+ host="$localhost"
+ else
+ host="::1"
+ fi
+ port="$PORT"
+ proxy="$localhost:$use"
+
+ else
+ if [ "X$rsh" != "X1" ]; then
+ echo "$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\""
+ echo ""
+ $ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd"
+ rc=$?
+ else
+ rsh_setup
+ echo "rsh $ul \"$ssh_host\" \"$ssh_cmd\""
+ echo ""
+ rsh $ul "$ssh_host" "$ssh_cmd" &
+ sleep 1
+ PORT=$port
+ rsh_viewer "$@"
+ exit $?
+ fi
+ fi
+
+ if [ "$rc" != "0" ]; then
+ echo ""
+ echo "ssh to \"$uath\" failed."
+ exit 1
+ fi
+ stty sane
+
+ c=0
+ pssh=""
+ while [ $c -lt 40 ]
+ do
+ p=`expr $pmark + $c`
+ pout=`ps -p "$p" 2>/dev/null | grep -v '^[ ]*PID' | sed -e 's/-L.*$//' -e 's/-x .*$//'`
+ if echo "$pout" | grep "ssh" > /dev/null; then
+ if echo "$pout" | egrep -i 'ssh.*(-add|-agent|-ask|-keygen|-argv0|vnc)' >/dev/null; then
+ :
+ elif echo "$pout" | egrep -i 'scp|sshd' >/dev/null; then
+ :
+ else
+ pssh=$p
+ break
+ fi
+ fi
+ c=`expr $c + 1`
+ done
+ if [ "X$getport" != "X" ]; then
+ :
+ elif [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ] ; then
+ sleep 2
+ elif [ "X$ssh_cmd" = "Xsleep $ssh_sleep" ] ; then
+ #echo T sleep 1
+ sleep 1
+ elif echo "$ssh_cmd" | grep '^sleep ' >/dev/null; then
+ #echo T sleep 2
+ sleep 2
+ else
+ # let any command get started a bit.
+ #echo T sleep 5
+ sleep 5
+ fi
+ echo ""
+ #reset
+ stty sane
+ if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then
+ echo "sleep $SSVNC_EXTRA_SLEEP"
+ sleep $SSVNC_EXTRA_SLEEP
+ fi
+ echo "ssh_pid='$pssh'"; echo
+ if [ "X$use_sshssl" = "X" -a "X$getport" = "X" ]; then
+ if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then
+ (sh -c "$SSVNC_EXTRA_COMMAND") &
+ echo "($SSVNC_EXTRA_COMMAND) &"; echo
+ fi
+ echo "Running viewer:"
+
+ trap "final" 0 2 15
+ if [ "X$reverse" = "X" ]; then
+ echo "$VNCVIEWERCMD" "$@" $localhost:$N
+ echo ""
+ $VNCVIEWERCMD "$@" $localhost:$N
+ if [ $? != 0 ]; then
+ echo "vncviewer command failed: $?"
+ if [ "X$secondtry" = "X1" ]; then
+ sleep 2
+ $VNCVIEWERCMD "$@" $localhost:$N
+ fi
+ fi
+ else
+ echo ""
+ echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode."
+ echo ""
+ N2=$N
+ if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then
+ N2=`echo "$N2" | sed -e 's/://g'`
+ if [ $N2 -le 200 ]; then
+ N2=`expr $N2 + 5500`
+ fi
+ fi
+ echo "$VNCVIEWERCMD" "$@" -listen $N2
+ echo ""
+ $VNCVIEWERCMD "$@" -listen $N2
+ fi
+
+ exit $?
+ else
+ use2=`findfree 5960`
+ host0=$host
+ port0=$port
+ host=$localhost
+ port=$use
+ use=$use2
+ N=`expr $use - 5900`
+ if [ "X$getport" != "X" ]; then
+ host="$host0"
+ port="$port0"
+ else
+ proxy=""
+ fi
+ if [ "X$ssh_vencrypt_proxy" != "X" ]; then
+ ssh_vencrypt_proxy="vencrypt://$host:$port"
+ if [ "X$proxy" = "X" ]; then
+ proxy=$ssh_vencrypt_proxy
+ else
+ proxy="$proxy,$ssh_vencrypt_proxy"
+ fi
+ Kecho "proxy_now=$proxy"
+ unset PPROXY_LISTEN
+ fi
+ fi
+fi
+
+if [ "X$stunnel_set_here" = "X1" -a "X$showcert" = "X" ]; then
+ if type $STUNNEL > /dev/null 2>&1; then
+ :
+ else
+ echo ""
+ echo "***************************************************************"
+ echo "** Problem finding the Stunnel command '$STUNNEL': **"
+ echo ""
+ type $STUNNEL
+ echo ""
+ echo "** Perhaps you need to install the stunnel/stunnel4 package. **"
+ echo "***************************************************************"
+ echo ""
+ sleep 5
+ fi
+fi
+
+# create the stunnel config file:
+if [ "X$verify" != "X" ]; then
+ if [ -d $verify ]; then
+ verify="CApath = $verify"
+ else
+ verify="CAfile = $verify"
+ fi
+ verify="$verify
+verify = 2"
+fi
+if [ "X$SSVNC_STUNNEL_VERIFY3" != "X" ]; then
+ verify=`echo "$verify" | sed -e 's/verify = 2/verify = 3/'`
+fi
+if [ "X$mycert" != "X" ]; then
+ cert="cert = $mycert"
+fi
+if [ "X$crl" != "X" ]; then
+ if [ -d $crl ]; then
+ crl="CRLpath = $crl"
+ else
+ crl="CRLfile = $crl"
+ fi
+fi
+
+if [ "X$showcert" = "X1" ]; then
+ if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then
+ :
+ elif [ "X$SSVNC_NO_IPV6_PROXY" != "X" ]; then
+ :
+ elif [ "X$ipv6" = "X1" -a "X$proxy" = "X" ]; then
+ proxy="ipv6://$host:$port"
+ fi
+fi
+
+if [ "X$direct_connect" != "X" -a "X$STUNNEL_LISTEN" != "X" ]; then
+ proxy=reverse_direct
+fi
+
+ptmp=""
+if [ "X$proxy" != "X" ]; then
+ ptmp="/tmp/ss_vncviewer${RANDOM}.$$.pl"
+ ptmp=`mytmp "$ptmp"`
+ PPROXY_REMOVE=1; export PPROXY_REMOVE
+ pcode "$ptmp"
+ if [ "X$showcert" != "X1" -a "X$direct_connect" = "X" ]; then
+ if uname | egrep 'Darwin|SunOS' >/dev/null; then
+ vout=`echo "$proxy" | grep -i vencrypt`
+ if [ "X$vout" != "X" -a "X$reverse" = "X1" ]; then
+ # need to exec for reverse vencrypt
+ connect="exec = $ptmp"
+ else
+ # on mac and solaris we need to listen on socket instead of stdio:
+ nd=`findfree 6700`
+ PPROXY_LISTEN=$nd
+ export PPROXY_LISTEN
+ if [ "X$reverse" = "X" ]; then
+ $ptmp &
+ fi
+ sleep 2
+ host="$localhost"
+ port="$nd"
+ connect="connect = $localhost:$nd"
+ fi
+ else
+ # otherwise on unix we can exec it:
+ connect="exec = $ptmp"
+ fi
+ else
+ connect="exec = $ptmp"
+ fi
+else
+ connect="connect = $host:$port"
+fi
+
+# handle showcert case:
+#
+if [ "X$showcert" = "X1" ]; then
+ if [ "X$proxy" != "X" ]; then
+ PPROXY_LISTEN=$use
+ export PPROXY_LISTEN
+ if [ "X$SS_DEBUG" != "X" ]; then
+ $ptmp &
+ else
+ $ptmp 2>/dev/null &
+ fi
+ sleep 1
+ more_sleep=1
+ if uname | grep Linux > /dev/null; then
+ if netstat -ant | grep LISTEN | grep "127.0.0.1:$use" > /dev/null; then
+ more_sleep=""
+ fi
+ elif uname | grep SunOS > /dev/null; then
+ if netstat -an -f inet -P tcp | grep LISTEN | grep "127.0.0.1.$use" > /dev/null; then
+ more_sleep=""
+ fi
+ elif uname | egrep -i 'bsd|darwin' > /dev/null; then
+ if netstat -ant -f inet | grep LISTEN | grep "127.0.0.1.$use" > /dev/null; then
+ more_sleep=""
+ fi
+ fi
+ if [ "X$more_sleep" = "X1" ]; then
+ sleep 1
+ fi
+ host="$localhost"
+ port="$use"
+ fi
+ cipher_args=""
+ if [ "X$ciphers" != "X" ]; then
+ cipher_args=`echo "$ciphers" | sed -e 's/ciphers=/-cipher /'`
+ fi
+ if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then
+ :
+ elif type openssl > /dev/null 2>&1; then
+ :
+ else
+ echo ""
+ echo "********************************************************"
+ echo "** Problem finding the OpenSSL command 'openssl': **"
+ echo ""
+ type openssl 2>&1
+ echo ""
+ echo "** Perhaps you need to install the 'openssl' package. **"
+ echo "********************************************************"
+ echo ""
+ fi
+ #echo "openssl s_client $cipher_args -connect $host:$port"
+ if [ "X$reverse" = "X" ]; then
+ if type host > /dev/null 2>/dev/null; then
+ host $host >/dev/null 2>&1
+ host $host >/dev/null 2>&1
+ fi
+ timeout=15
+ if [ "X$SSVNC_FETCH_TIMEOUT" != "X" ]; then
+ timeout=$SSVNC_FETCH_TIMEOUT
+ fi
+ if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then
+ if type pkill >/dev/null 2>&1; then
+ (sleep $timeout; if kill -0 $$; then pkill -TERM -f "ultravnc_dsm_helper.*$host.*$port"; fi) >/dev/null 2>&1 &
+ fi
+ ultravnc_dsm_helper showcert $host:$port 2>&1
+ else
+ if type pkill >/dev/null 2>&1; then
+ (sleep $timeout; if kill -0 $$; then pkill -TERM -f "openssl.*s_client.*$host.*$port"; fi) >/dev/null 2>&1 &
+ fi
+ openssl s_client $cipher_args -prexit -connect $host:$port 2>&1 < /dev/null
+ fi
+ rc=$?
+ else
+ tcert=""
+ if [ "X$mycert" = "X" ]; then
+ tcert=`make_tcert`
+ cert_args="-cert $tcert -CAfile $tcert"
+ else
+ cert_args="-cert $mycert -CAfile $mycert"
+ fi
+ tmp_out=/tmp/showcert_out${RANDOM}.$$
+ tmp_out=`mytmp "$tmp_out"`
+ tmp_err=/tmp/showcert_err${RANDOM}.$$
+ tmp_err=`mytmp "$tmp_err"`
+
+ #echo "openssl s_server $cipher_args $cert_args -accept $port -verify 2 > $tmp_out 2> $tmp_err" 1>&2
+
+ # assume we have perl:
+ check_perl perl
+
+ perl -e "
+ \$p = open(O, \"|openssl s_server $cipher_args $cert_args -accept $port -verify 2 1>$tmp_out 2> $tmp_err\");
+ exit 1 unless \$p;
+ while (1) {
+ sleep 1;
+ if (!open(F, \"<$tmp_out\")) {
+ kill \$p;
+ exit 1;
+ }
+ while (<F>) {
+ if (/RFB 00/) {
+ fsleep(0.25);
+ print O \"RFB 000.000\\n\";
+ fsleep(1.00);
+ kill \$p;
+ fsleep(0.25);
+ exit 0;
+ }
+ }
+ close F;
+ }
+ sub fsleep {
+ select(undef, undef, undef, shift);
+ }
+ ";
+
+ echo ""
+ cat $tmp_out
+ echo ""
+ echo "----2----"
+ cat $tmp_err
+ if grep BEGIN.CERTIFICATE $tmp_out >/dev/null; then
+ rc=0
+ else
+ rc=1
+ fi
+
+ rm -f $tmp_out $tmp_err
+ fi
+ if [ "X$SSVNC_PREDIGESTED_HANDSHAKE" != "X" ]; then
+ rm -f $SSVNC_PREDIGESTED_HANDSHAKE
+ fi
+ if [ "X$SSVNC_SHOWCERT_EXIT_0" = "X1" ]; then
+ exit 0
+ else
+ exit $rc
+ fi
+fi
+
+# handle direct connect case:
+#
+if [ "X$direct_connect" != "X" ]; then
+ if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then
+ SSVNC_NO_ENC_WARN=1
+ echo ""
+ echo "Using UltraVNC DSM Plugin key for encryption:"
+ echo ""
+ ustr=`echo "$SSVNC_ULTRA_DSM" | sed -e 's/pw=[^ ]*/pw=******/g'`
+ echo " $ustr PORT HOST:PORT"
+ echo ""
+ elif [ "X$getport" = "X" ]; then
+ echo ""
+ echo "Running viewer for direct connection:"
+ if echo X"$@" | grep chatonly > /dev/null; then
+ :
+ else
+ echo ""
+ echo "** WARNING: THERE WILL BE NO SSL OR SSH ENCRYPTION **"
+ echo ""
+ fi
+ fi
+ x=""
+ if [ "X$SSVNC_NO_ENC_WARN" != "X" ]; then
+ if [ "X$getport" = "X" ]; then
+ sleep 1
+ fi
+ elif type printf > /dev/null 2>&1; then
+ printf "Are you sure you want to continue? [y]/n "
+ read x
+ else
+ echo -n "Are you sure you want to continue? [y]/n "
+ read x
+ fi
+ if [ "X$x" = "Xn" ]; then
+ exit 1
+ fi
+ echo ""
+ if [ "X$ptmp" != "X" ]; then
+ if [ "X$reverse" = "X" ]; then
+ PPROXY_LISTEN=$use
+ export PPROXY_LISTEN
+ else
+ if [ "X$proxy" = "Xreverse_direct" ]; then
+ PPROXY_LISTEN="$STUNNEL_LISTEN:`expr 5500 + $disp`"
+ PPROXY_DEST="$localhost:$use"
+ PPROXY_PROXY="ipv6://$localhost:$use" # not always ipv6..
+ export PPROXY_LISTEN PPROXY_DEST PPROXY_PROXY
+ pps=1
+ else
+ PPROXY_REVERSE="$localhost:$use"
+ export PPROXY_LISTEN
+ pps=3
+ fi
+ if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then
+ PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself.${RANDOM}.$$"`
+ export PPROXY_LOOP_THYSELF
+ pps=2
+ fi
+ if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then
+ pps=`expr $pps + $SSVNC_EXTRA_SLEEP`
+ fi
+ PPROXY_SLEEP=$pps; export PPROXY_SLEEP;
+ PPROXY_KILLPID=+1; export PPROXY_KILLPID;
+ fi
+
+ $ptmp &
+
+ if [ "X$reverse" = "X" ]; then
+ #sleep 2
+ #echo T sleep 1
+ sleep 1
+ fi
+ host="$localhost"
+ disp="$N"
+ port=`expr $disp + 5900`
+ fi
+ if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then
+ echo "T sleep $SSVNC_EXTRA_SLEEP"
+ sleep $SSVNC_EXTRA_SLEEP
+ fi
+ if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then
+ (sh -c "$SSVNC_EXTRA_COMMAND") &
+ echo "($SSVNC_EXTRA_COMMAND) &"; echo
+ fi
+ if [ "X$reverse" = "X" ]; then
+ hostdisp="$host:$disp"
+ if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then
+ if [ "X$SSVNC_USE_OURS" = "X1" ]; then
+ hostdisp="exec=$SSVNC_ULTRA_DSM 0 $host:$port"
+ else
+ pf=`findfree 5970`
+ cmd="$SSVNC_ULTRA_DSM -$pf $host:$port"
+ pf=`expr $pf - 5900`
+ hostdisp="$localhost:$pf"
+ ustr=`echo "$cmd" | sed -e 's/pw=[^ ]*/pw=******/g'`
+ echo "Running:"
+ echo
+ echo "$ustr &"
+ echo
+ $cmd &
+ dsm_pid=$!
+ sleep 2
+ fi
+ fi
+ hostdisp2=`echo "$hostdisp" | sed -e 's/pw=[^ ]*/pw=******/g'`
+ echo "$VNCVIEWERCMD" "$@" "$hostdisp2"
+ trap "final" 0 2 15
+ echo ""
+ $VNCVIEWERCMD "$@" "$hostdisp"
+ if [ $? != 0 ]; then
+ echo "vncviewer command failed: $?"
+ if [ "X$secondtry" = "X1" ]; then
+ sleep 2
+ $VNCVIEWERCMD "$@" "$hostdisp"
+ fi
+ fi
+ else
+ echo ""
+ echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode."
+ echo ""
+ trap "final" 0 2 15
+ if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then
+ if [ "X$SSVNC_LISTEN_ONCE" = "X1" ]; then
+ echo "NOTE: The ultravnc_dsm_helper only runs once. So after the first LISTEN"
+ echo " ends you must restart the Listening mode. You may also need to"
+ echo " Press Ctrl-C to stop the viewer and restart for another connection."
+ echo ""
+ fi
+ #SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE
+ VNCVIEWER_LISTEN_LOCALHOST=1
+ export VNCVIEWER_LISTEN_LOCALHOST
+ dport=`expr 5500 + $disp`
+ cmd="$SSVNC_ULTRA_DSM $dport $localhost:$use"
+ ustr=`echo "$cmd" | sed -e 's/pw=[^ ]*/pw=******/g'`
+ echo "Running:"
+ echo
+ echo "$ustr &"
+ echo
+ if [ "X$SSVNC_LISTEN_ONCE" = "X1" ]; then
+ $cmd &
+ dsm_pid=$!
+ else
+ while [ 1 ]; do $cmd; sleep 1; done &
+ dsm_pid=$!
+ fi
+ sleep 2
+ disp=$use
+ if [ $disp -ge 5500 ]; then
+ disp=`expr $disp - 5500`
+ fi
+ fi
+ disp2=$disp
+ if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then
+ disp2=`echo "$disp2" | sed -e 's/://g'`
+ if [ $disp2 -le 200 ]; then
+ disp2=`expr $disp2 + 5500`
+ fi
+ fi
+ echo "$VNCVIEWERCMD" "$@" -listen $disp2
+ echo ""
+ $VNCVIEWERCMD "$@" -listen $disp2
+ if [ "X$PPROXY_LOOP_THYSELF" != "X" ]; then
+ rm -f $PPROXY_LOOP_THYSELF
+ fi
+ fi
+ exit $?
+fi
+
+tmp_cfg=/tmp/ss_vncviewer${RANDOM}.$$
+tmp_cfg=`mytmp "$tmp_cfg"`
+
+stunnel_exec=""
+if [ "X$SSVNC_USE_OURS" != "X1" ]; then
+ :
+elif echo $STUNNEL_EXTRA_SVC_OPTS | grep '#stunnel-exec' > /dev/null; then
+ stunnel_exec="#"
+fi
+
+if [ "X$reverse" = "X" ]; then
+
+ if echo "$proxy" | grep "^repeater://" > /dev/null; then
+ if [ "X$cert" = "XBUILTIN" ]; then
+ ttcert=`make_tcert`
+ cert="cert = $ttcert"
+ fi
+ # Note for listen mode, an empty cert will cause stunnel to fail.
+ # The ssvnc gui will have already taken care of this.
+ fi
+
+ cat > "$tmp_cfg" <<END
+foreground = yes
+pid =
+client = yes
+debug = $stunnel_debug
+$ciphers
+$STUNNEL_EXTRA_OPTS
+$STUNNEL_EXTRA_OPTS_USER
+$cert
+$crl
+$verify
+
+${stunnel_exec}[vnc_stunnel]
+${stunnel_exec}accept = $localhost:$use
+$connect
+$STUNNEL_EXTRA_SVC_OPTS
+$STUNNEL_EXTRA_SVC_OPTS_USER
+
+END
+
+else
+ # REVERSE case:
+
+ stunnel_exec="" # doesn't work for listening.
+
+ p2=`expr 5500 + $N`
+ connect="connect = $localhost:$p2"
+ if [ "X$cert" = "XBUILTIN" ]; then
+ ttcert=`make_tcert`
+ cert="cert = $ttcert"
+ fi
+ # Note for listen mode, an empty cert will cause stunnel to fail.
+ # The ssvnc gui will have already taken care of this.
+
+
+ hloc=""
+ if [ "X$use_ssh" = "X1" ]; then
+ hloc="$localhost:"
+ elif [ "X$STUNNEL_LISTEN" != "X" ]; then
+ hloc="$STUNNEL_LISTEN:"
+ fi
+ if echo "$proxy" | grep -i '^vencrypt:' > /dev/null; then
+ hloc="$localhost:"
+ pv=`findfree 5570`
+ proxy="vencrypt:$pv:$port"
+ port=$pv
+ if [ "X$anondh_set" = "X1" ]; then
+ # not needed for ANONDH in this mode
+ #ciphers="ciphers = ADH:@STRENGTH"
+ :
+ fi
+ fi
+ cat > "$tmp_cfg" <<END
+foreground = yes
+pid =
+client = no
+debug = $stunnel_debug
+$ciphers
+$STUNNEL_EXTRA_OPTS
+$STUNNEL_EXTRA_OPTS_USER
+$cert
+$crl
+$verify
+
+[vnc_stunnel]
+accept = $hloc$port
+$connect
+$STUNNEL_EXTRA_SVC_OPTS
+$STUNNEL_EXTRA_SVC_OPTS_USER
+
+END
+
+fi
+
+echo ""
+echo "Using this stunnel configuration:"
+echo ""
+cat "$tmp_cfg" | uniq
+echo ""
+if egrep -i '^[ ]*(CApath|CAfile) =' "$tmp_cfg" > /dev/null ; then
+ :
+else
+ echo "** WARNING: THE STUNNEL CONFIG HAS NO SERVER CERTIFICATE SPECIFIED **"
+ echo "** WARNING: (the CApath or CAfile stunnel option) THE VNC SERVER WILL **"
+ echo "** WARNING: NOT BE AUTHENTICATED. A MAN-IN-THE-MIDDLE ATTACK IS POSSIBLE **"
+ echo ""
+fi
+sleep 1
+
+if [ "X$stunnel_exec" = "X" ]; then
+ echo ""
+ echo "Running stunnel:"
+ echo "$STUNNEL $tmp_cfg"
+ st=`echo "$STUNNEL" | awk '{print $1}'`
+ $st -help > /dev/null 2>&1
+ $STUNNEL "$tmp_cfg" < /dev/tty > /dev/tty &
+ stunnel_pid=$!
+ echo ""
+
+ # pause here to let the user supply a possible passphrase for the
+ # mycert key:
+ if [ "X$mycert" != "X" ]; then
+ nsl=10
+ dsl=0
+ if [ ! -f $mycert ]; then
+ dsl=0
+ elif grep -i 'Proc-Type.*ENCRYPTED' "$mycert" > /dev/null 2>/dev/null; then
+ dsl=1
+ fi
+ if [ "X$dsl" = "X1" ]; then
+ echo ""
+ echo "(** pausing $nsl secs for possible certificate passphrase dialog **)"
+ echo ""
+ sleep $nsl
+ echo "(** done pausing for passphrase **)"
+ echo ""
+ fi
+ fi
+ #echo T sleep 1
+ sleep 1
+ rm -f "$tmp_cfg"
+fi
+
+
+echo ""
+if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then
+ echo "sleep $SSVNC_EXTRA_SLEEP"
+ sleep $SSVNC_EXTRA_SLEEP
+fi
+if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then
+ (sh -c "$SSVNC_EXTRA_COMMAND") &
+ echo "($SSVNC_EXTRA_COMMAND) &"; echo
+fi
+
+if [ "X$reverse" = "X" ]; then
+ if [ "X$NEED_VENCRYPT_VIEWER_BRIDGE" = "X1" -a "X$ptmp" != "X" ] ; then
+ port1=`expr 5900 + $N` # stunnel port
+ port2=`findfree 5970` # bridge port (viewer connects to it.)
+ N=`expr $port2 - 5900`
+ env PPROXY_REMOVE=0 PPROXY_SLEEP=0 PPROXY_VENCRYPT_VIEWER_BRIDGE="$port2,$port1" $ptmp &
+ sleep 1
+ fi
+ echo "Running viewer:"
+ vnc_hp=$localhost:$N
+ if [ "X$stunnel_exec" != "X" ]; then
+ vnc_hp="exec=$STUNNEL $tmp_cfg"
+ fi
+ echo "$VNCVIEWERCMD" "$@" "$vnc_hp"
+ trap "final" 0 2 15
+ echo ""
+ $VNCVIEWERCMD "$@" "$vnc_hp"
+ if [ $? != 0 ]; then
+ echo "vncviewer command failed: $?"
+ if [ "X$secondtry" = "X1" ]; then
+ sleep 2
+ $VNCVIEWERCMD "$@" "$vnc_hp"
+ fi
+ fi
+else
+ echo "Running viewer:"
+ echo ""
+ echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode."
+ echo ""
+ trap "final" 0 2 15
+ N2=$N
+ N2_trim=`echo "$N2" | sed -e 's/://g'`
+ if [ $N2_trim -le 200 ]; then
+ N2_trim=`expr $N2_trim + 5500`
+ fi
+ if [ "X$proxy" != "X" ]; then
+ if echo "$proxy" | grep -i '^vencrypt:' > /dev/null; then
+ pstunnel=`echo "$proxy" | awk -F: '{print $2}'`
+ plisten=`echo "$proxy" | awk -F: '{print $3}'`
+ IF=INADDR_ANY
+ if [ "X$STUNNEL_LISTEN" != "X" ]; then
+ IF=$STUNNEL_LISTEN
+ fi
+ PPROXY_VENCRYPT_REVERSE=1; export PPROXY_VENCRYPT_REVERSE
+ PPROXY_LISTEN="$IF:$plisten"; export PPROXY_LISTEN
+ PPROXY_PROXY="vencrypt://$localhost:$pstunnel"; export PPROXY_PROXY
+ PPROXY_DEST="$localhost:$pstunnel"; export PPROXY_DEST
+ STUNNEL_ONCE=1; export STUNNEL_ONCE
+ STUNNEL_MAX_CLIENTS=1; export STUNNEL_MAX_CLIENTS
+ if [ "X$NEED_VENCRYPT_VIEWER_BRIDGE" = "X1" -a "X$ptmp" != "X" ] ; then
+ port1=`expr 5500 + $N2`
+ port2=`findfree 5580`
+ N2=`expr $port2 - 5500`
+ N2_trim=`echo "$N2" | sed -e 's/://g'`
+ if [ $N2_trim -le 200 ]; then
+ N2_trim=`expr $N2_trim + 5500`
+ fi
+ if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then
+ PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself1.${RANDOM}.$$"`
+ export PPROXY_LOOP_THYSELF
+ PPROXY_LOOP_THYSELF0=$PPROXY_LOOP_THYSELF
+ fi
+ env PPROXY_REMOVE=0 PPROXY_SLEEP=0 PPROXY_VENCRYPT_VIEWER_BRIDGE="-$port1,$port2" $ptmp &
+ sleep 1
+ fi
+ else
+ PPROXY_REVERSE="$localhost:$port"; export PPROXY_REVERSE
+ PPROXY_SLEEP=1; export PPROXY_SLEEP;
+ fi
+ PPROXY_KILLPID=+1; export PPROXY_KILLPID;
+ if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then
+ PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself2.${RANDOM}.$$"`
+ export PPROXY_LOOP_THYSELF
+ fi
+ $ptmp &
+ # Important to have no extra pids generated between here and VNCVIEWERCMD
+ fi
+ if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then
+ N2=$N2_trim
+ fi
+ echo "$VNCVIEWERCMD" "$@" -listen $N2
+ echo ""
+ $VNCVIEWERCMD "$@" -listen $N2
+
+ if [ "X$PPROXY_LOOP_THYSELF" != "X" ]; then
+ rm -f $PPROXY_LOOP_THYSELF
+ fi
+ if [ "X$PPROXY_LOOP_THYSELF0" != "X" ]; then
+ rm -f $PPROXY_LOOP_THYSELF0
+ fi
+fi
+
+sleep 1
diff --git a/webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch b/webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch
new file mode 100644
index 0000000..bc10f3c
--- /dev/null
+++ b/webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch
@@ -0,0 +1,111 @@
+--- vnc_javasrc.orig/VncCanvas.java 2004-10-10 02:15:54.000000000 -0400
++++ vnc_javasrc/VncCanvas.java 2010-11-30 21:01:15.000000000 -0500
+@@ -28,13 +28,14 @@
+ import java.lang.*;
+ import java.util.zip.*;
+
++import java.util.Collections;
+
+ //
+ // VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
+ //
+
+ class VncCanvas extends Canvas
+- implements KeyListener, MouseListener, MouseMotionListener {
++ implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener {
+
+ VncViewer viewer;
+ RfbProto rfb;
+@@ -81,6 +82,20 @@
+ cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
+ cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
+
++ // kludge to not show any Java cursor in the canvas since we are
++ // showing the soft cursor (should be a user setting...)
++ Cursor dot = Toolkit.getDefaultToolkit().createCustomCursor(
++ Toolkit.getDefaultToolkit().createImage(new byte[4]), new Point(0,0),
++ "dot");
++ this.setCursor(dot);
++
++ // while we are at it... get rid of the keyboard traversals that
++ // make it so we can't type a Tab character:
++ this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
++ Collections.EMPTY_SET);
++ this.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
++ Collections.EMPTY_SET);
++
+ colors = new Color[256];
+ for (int i = 0; i < 256; i++)
+ colors[i] = new Color(cm8.getRGB(i));
+@@ -169,6 +184,7 @@
+ inputEnabled = true;
+ addMouseListener(this);
+ addMouseMotionListener(this);
++ addMouseWheelListener(this);
+ if (viewer.showControls) {
+ viewer.buttonPanel.enableRemoteAccessControls(true);
+ }
+@@ -177,6 +193,7 @@
+ inputEnabled = false;
+ removeMouseListener(this);
+ removeMouseMotionListener(this);
++ removeMouseWheelListener(this);
+ if (viewer.showControls) {
+ viewer.buttonPanel.enableRemoteAccessControls(false);
+ }
+@@ -1190,6 +1207,9 @@
+ public void mouseDragged(MouseEvent evt) {
+ processLocalMouseEvent(evt, true);
+ }
++ public void mouseWheelMoved(MouseWheelEvent evt) {
++ processLocalMouseWheelEvent(evt);
++ }
+
+ public void processLocalKeyEvent(KeyEvent evt) {
+ if (viewer.rfb != null && rfb.inNormalProtocol) {
+@@ -1221,6 +1241,19 @@
+ evt.consume();
+ }
+
++ public void processLocalMouseWheelEvent(MouseWheelEvent evt) {
++ if (viewer.rfb != null && rfb.inNormalProtocol) {
++ synchronized(rfb) {
++ try {
++ rfb.writeWheelEvent(evt);
++ } catch (Exception e) {
++ e.printStackTrace();
++ }
++ rfb.notify();
++ }
++ }
++ }
++
+ public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
+ if (viewer.rfb != null && rfb.inNormalProtocol) {
+ if (moved) {
+@@ -1387,9 +1420,9 @@
+ result = cm8.getRGB(pixBuf[i]);
+ } else {
+ result = 0xFF000000 |
+- (pixBuf[i * 4 + 1] & 0xFF) << 16 |
+- (pixBuf[i * 4 + 2] & 0xFF) << 8 |
+- (pixBuf[i * 4 + 3] & 0xFF);
++ (pixBuf[i * 4 + 2] & 0xFF) << 16 |
++ (pixBuf[i * 4 + 1] & 0xFF) << 8 |
++ (pixBuf[i * 4 + 0] & 0xFF);
+ }
+ } else {
+ result = 0; // Transparent pixel
+@@ -1403,9 +1436,9 @@
+ result = cm8.getRGB(pixBuf[i]);
+ } else {
+ result = 0xFF000000 |
+- (pixBuf[i * 4 + 1] & 0xFF) << 16 |
+- (pixBuf[i * 4 + 2] & 0xFF) << 8 |
+- (pixBuf[i * 4 + 3] & 0xFF);
++ (pixBuf[i * 4 + 2] & 0xFF) << 16 |
++ (pixBuf[i * 4 + 1] & 0xFF) << 8 |
++ (pixBuf[i * 4 + 0] & 0xFF);
+ }
+ } else {
+ result = 0; // Transparent pixel
diff --git a/webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch b/webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch
new file mode 100644
index 0000000..801234a
--- /dev/null
+++ b/webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch
@@ -0,0 +1,2600 @@
+diff -Naur vnc_javasrc.orig/Makefile vnc_javasrc/Makefile
+--- vnc_javasrc.orig/Makefile 2004-03-04 08:34:25.000000000 -0500
++++ vnc_javasrc/Makefile 2010-05-18 20:56:26.000000000 -0400
+@@ -4,6 +4,7 @@
+
+ CP = cp
+ JC = javac
++JC_ARGS = -target 1.4 -source 1.4
+ JAR = jar
+ ARCHIVE = VncViewer.jar
+ MANIFEST = MANIFEST.MF
+@@ -15,25 +16,29 @@
+ DesCipher.class CapabilityInfo.class CapsContainer.class \
+ RecordingFrame.class SessionRecorder.class AuthUnixLoginPanel.class \
+ SocketFactory.class HTTPConnectSocketFactory.class \
+- HTTPConnectSocket.class ReloginPanel.class
++ HTTPConnectSocket.class ReloginPanel.class \
++ SSLSocketToMe.class
++
++SSL_CLASSES = SSLSocketToMe*.class TrustDialog.class
+
+ SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \
+ OptionsFrame.java ClipboardFrame.java ButtonPanel.java \
+ DesCipher.java CapabilityInfo.java CapsContainer.java \
+ RecordingFrame.java SessionRecorder.java AuthUnixLoginPanel.java \
+ SocketFactory.java HTTPConnectSocketFactory.java \
+- HTTPConnectSocket.java ReloginPanel.java
++ HTTPConnectSocket.java ReloginPanel.java \
++ SSLSocketToMe.java
+
+ all: $(CLASSES) $(ARCHIVE)
+
+ $(CLASSES): $(SOURCES)
+- $(JC) -target 1.1 -O $(SOURCES)
++ $(JC) $(JC_ARGS) -O $(SOURCES)
+
+ $(ARCHIVE): $(CLASSES) $(MANIFEST)
+- $(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES)
++ $(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES) $(SSL_CLASSES)
+
+ install: $(CLASSES) $(ARCHIVE)
+- $(CP) $(CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR)
++ $(CP) $(CLASSES) $(SSL_CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR)
+
+ export:: $(CLASSES) $(ARCHIVE) $(PAGES)
+ @$(ExportJavaClasses)
+diff -Naur vnc_javasrc.orig/RfbProto.java vnc_javasrc/RfbProto.java
+--- vnc_javasrc.orig/RfbProto.java 2004-03-04 08:34:25.000000000 -0500
++++ vnc_javasrc/RfbProto.java 2010-11-30 22:05:12.000000000 -0500
+@@ -199,7 +199,21 @@
+ host = h;
+ port = p;
+
+- if (viewer.socketFactory == null) {
++ if (! viewer.disableSSL) {
++ System.out.println("new SSLSocketToMe");
++ SSLSocketToMe ssl;
++ try {
++ ssl = new SSLSocketToMe(host, port, v);
++ } catch (Exception e) {
++ throw new IOException(e.getMessage());
++ }
++
++ try {
++ sock = ssl.connectSock();
++ } catch (Exception es) {
++ throw new IOException(es.getMessage());
++ }
++ } else if (viewer.socketFactory == null) {
+ sock = new Socket(host, port);
+ } else {
+ try {
+@@ -255,7 +269,7 @@
+ || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n'))
+ {
+ throw new Exception("Host " + host + " port " + port +
+- " is not an RFB server");
++ " is not an RFB server: " + b);
+ }
+
+ serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0');
+@@ -892,6 +906,38 @@
+ final static int ALT_MASK = InputEvent.ALT_MASK;
+
+
++ void writeWheelEvent(MouseWheelEvent evt) throws IOException {
++
++ eventBufLen = 0;
++
++ int x = evt.getX();
++ int y = evt.getY();
++
++ if (x < 0) x = 0;
++ if (y < 0) y = 0;
++
++ int ptrmask;
++
++ int clicks = evt.getWheelRotation();
++ System.out.println("writeWheelEvent: clicks: " + clicks);
++ if (clicks > 0) {
++ ptrmask = 16;
++ } else if (clicks < 0) {
++ ptrmask = 8;
++ } else {
++ return;
++ }
++
++ eventBuf[eventBufLen++] = (byte) PointerEvent;
++ eventBuf[eventBufLen++] = (byte) ptrmask;
++ eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff);
++ eventBuf[eventBufLen++] = (byte) (x & 0xff);
++ eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff);
++ eventBuf[eventBufLen++] = (byte) (y & 0xff);
++
++ os.write(eventBuf, 0, eventBufLen);
++ }
++
+ //
+ // Write a pointer event message. We may need to send modifier key events
+ // around it to set the correct modifier state.
+@@ -992,6 +1038,19 @@
+ boolean down = (evt.getID() == KeyEvent.KEY_PRESSED);
+
+ int key;
++ if (viewer.debugKeyboard) {
++ System.out.println("----------------------------------------");
++ System.out.println("evt.getKeyChar: " + evt.getKeyChar());
++ System.out.println("getKeyText: " + KeyEvent.getKeyText(evt.getKeyCode()));
++ System.out.println("evt.getKeyCode: " + evt.getKeyCode());
++ System.out.println("evt.getID: " + evt.getID());
++ System.out.println("evt.getKeyLocation: " + evt.getKeyLocation());
++ System.out.println("evt.isActionKey: " + evt.isActionKey());
++ System.out.println("evt.isControlDown: " + evt.isControlDown());
++ System.out.println("evt.getModifiers: " + evt.getModifiers());
++ System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers()));
++ System.out.println("evt.paramString: " + evt.paramString());
++ }
+ if (evt.isActionKey()) {
+
+ //
+@@ -1025,6 +1084,13 @@
+ return;
+ }
+
++ if(key == 0xffc2 && viewer.mapF5_to_atsign) {
++ if (viewer.debugKeyboard) {
++ System.out.println("Mapping: F5 -> AT ");
++ }
++ key = 0x40;
++ }
++
+ } else {
+
+ //
+@@ -1036,6 +1102,7 @@
+
+ key = keyChar;
+
++
+ if (key < 0x20) {
+ if (evt.isControlDown()) {
+ key += 0x60;
+@@ -1121,6 +1188,16 @@
+ int oldModifiers = 0;
+
+ void writeModifierKeyEvents(int newModifiers) {
++ if(viewer.forbid_Ctrl_Alt) {
++ if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) {
++ int orig = newModifiers;
++ newModifiers &= ~ALT_MASK;
++ newModifiers &= ~CTRL_MASK;
++ if (viewer.debugKeyboard) {
++ System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers);
++ }
++ }
++ }
+ if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK))
+ writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0);
+
+diff -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSLSocketToMe.java
+--- vnc_javasrc.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500
++++ vnc_javasrc/SSLSocketToMe.java 2010-07-10 19:18:06.000000000 -0400
+@@ -0,0 +1,2067 @@
++/*
++ * SSLSocketToMe.java: add SSL encryption to Java VNC Viewer.
++ *
++ * Copyright (c) 2006 Karl J. Runge <runge@karlrunge.com>
++ * 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; version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This software is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this software; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++ * USA.
++ *
++ */
++
++import java.net.*;
++import java.io.*;
++import javax.net.ssl.*;
++import java.util.*;
++
++import java.security.*;
++import java.security.cert.*;
++import java.security.spec.*;
++import java.security.cert.Certificate;
++import java.security.cert.CertificateFactory;
++
++import java.awt.*;
++import java.awt.event.*;
++
++public class SSLSocketToMe {
++
++ /* basic member data: */
++ String host;
++ int port;
++ VncViewer viewer;
++
++ boolean debug = true;
++ boolean debug_certs = false;
++
++ /* sockets */
++ SSLSocket socket = null;
++ SSLSocketFactory factory;
++
++ /* fallback for Proxy connection */
++ boolean proxy_in_use = false;
++ boolean proxy_failure = false;
++ public DataInputStream is = null;
++ public OutputStream os = null;
++
++ /* strings from user WRT proxy: */
++ String proxy_auth_string = null;
++ String proxy_dialog_host = null;
++ int proxy_dialog_port = 0;
++
++ Socket proxySock;
++ DataInputStream proxy_is;
++ OutputStream proxy_os;
++
++ /* trust contexts */
++ SSLContext trustloc_ctx;
++ SSLContext trustall_ctx;
++ SSLContext trustsrv_ctx;
++ SSLContext trusturl_ctx;
++ SSLContext trustone_ctx;
++
++ /* corresponding trust managers */
++ TrustManager[] trustAllCerts;
++ TrustManager[] trustSrvCert;
++ TrustManager[] trustUrlCert;
++ TrustManager[] trustOneCert;
++
++ /* client-side SSL auth key (oneTimeKey=...) */
++ KeyManager[] mykey = null;
++
++ boolean user_wants_to_see_cert = true;
++ String cert_fail = null;
++
++ /* cert(s) we retrieve from Web server, VNC server, or serverCert param: */
++ java.security.cert.Certificate[] trustallCerts = null;
++ java.security.cert.Certificate[] trustsrvCerts = null;
++ java.security.cert.Certificate[] trusturlCerts = null;
++
++ /* utility to decode hex oneTimeKey=... and serverCert=... */
++ byte[] hex2bytes(String s) {
++ byte[] bytes = new byte[s.length()/2];
++ for (int i=0; i<s.length()/2; i++) {
++ int j = 2*i;
++ try {
++ int val = Integer.parseInt(s.substring(j, j+2), 16);
++ if (val > 127) {
++ val -= 256;
++ }
++ Integer I = new Integer(val);
++ bytes[i] = Byte.decode(I.toString()).byteValue();
++
++ } catch (Exception e) {
++ ;
++ }
++ }
++ return bytes;
++ }
++
++ SSLSocketToMe(String h, int p, VncViewer v) throws Exception {
++ host = h;
++ port = p;
++ viewer = v;
++
++ debug_certs = v.debugCerts;
++
++ /* we will first try default factory for certification: */
++
++ factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
++
++ dbg("SSL startup: " + host + " " + port);
++
++
++ /* create trust managers to be used if initial handshake fails: */
++
++ trustAllCerts = new TrustManager[] {
++ /*
++ * this one accepts everything. Only used if user
++ * has disabled checking (trustAllVncCerts=yes)
++ * or when we grab the cert to show it to them in
++ * a dialog and ask them to manually verify/accept it.
++ */
++ new X509TrustManager() {
++ public java.security.cert.X509Certificate[]
++ getAcceptedIssuers() {
++ return null;
++ }
++ public void checkClientTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) {
++ /* empty */
++ }
++ public void checkServerTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) {
++ /* empty */
++ dbg("ALL: an untrusted connect to grab cert.");
++ }
++ }
++ };
++
++ trustUrlCert = new TrustManager[] {
++ /*
++ * this one accepts only the retrieved server
++ * cert by SSLSocket by this applet and stored in
++ * trusturlCerts.
++ */
++ new X509TrustManager() {
++ public java.security.cert.X509Certificate[]
++ getAcceptedIssuers() {
++ return null;
++ }
++ public void checkClientTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) throws CertificateException {
++ throw new CertificateException("No Clients (URL)");
++ }
++ public void checkServerTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) throws CertificateException {
++ /* we want to check 'certs' against 'trusturlCerts' */
++ if (trusturlCerts == null) {
++ throw new CertificateException(
++ "No Trust url Certs array.");
++ }
++ if (trusturlCerts.length < 1) {
++ throw new CertificateException(
++ "No Trust url Certs.");
++ }
++ if (certs == null) {
++ throw new CertificateException(
++ "No this-certs array.");
++ }
++ if (certs.length < 1) {
++ throw new CertificateException(
++ "No this-certs Certs.");
++ }
++ if (certs.length != trusturlCerts.length) {
++ throw new CertificateException(
++ "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length);
++ }
++ boolean ok = true;
++ for (int i = 0; i < certs.length; i++) {
++ if (! trusturlCerts[i].equals(certs[i])) {
++ ok = false;
++ dbg("URL: cert mismatch at i=" + i);
++ dbg("URL: cert mismatch cert" + certs[i]);
++ dbg("URL: cert mismatch url" + trusturlCerts[i]);
++ if (cert_fail == null) {
++ cert_fail = "cert-mismatch";
++ }
++ }
++ if (debug_certs) {
++ dbg("\n***********************************************");
++ dbg("URL: cert info at i=" + i);
++ dbg("URL: cert info cert" + certs[i]);
++ dbg("===============================================");
++ dbg("URL: cert info url" + trusturlCerts[i]);
++ dbg("***********************************************");
++ }
++ }
++ if (!ok) {
++ throw new CertificateException(
++ "Server Cert Chain != URL Cert Chain.");
++ }
++ dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1));
++ }
++ }
++ };
++
++ trustSrvCert = new TrustManager[] {
++ /*
++ * this one accepts cert given to us in the serverCert
++ * Applet Parameter we were started with. It is
++ * currently a fatal error if the VNC Server's cert
++ * doesn't match it.
++ */
++ new X509TrustManager() {
++ public java.security.cert.X509Certificate[]
++ getAcceptedIssuers() {
++ return null;
++ }
++ public void checkClientTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) throws CertificateException {
++ throw new CertificateException("No Clients (SRV)");
++ }
++ public void checkServerTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) throws CertificateException {
++ /* we want to check 'certs' against 'trustsrvCerts' */
++ if (trustsrvCerts == null) {
++ throw new CertificateException(
++ "No Trust srv Certs array.");
++ }
++ if (trustsrvCerts.length < 1) {
++ throw new CertificateException(
++ "No Trust srv Certs.");
++ }
++ if (certs == null) {
++ throw new CertificateException(
++ "No this-certs array.");
++ }
++ if (certs.length < 1) {
++ throw new CertificateException(
++ "No this-certs Certs.");
++ }
++ if (certs.length != trustsrvCerts.length) {
++ throw new CertificateException(
++ "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length);
++ }
++ boolean ok = true;
++ for (int i = 0; i < certs.length; i++) {
++ if (! trustsrvCerts[i].equals(certs[i])) {
++ ok = false;
++ dbg("SRV: cert mismatch at i=" + i);
++ dbg("SRV: cert mismatch cert" + certs[i]);
++ dbg("SRV: cert mismatch srv" + trustsrvCerts[i]);
++ if (cert_fail == null) {
++ cert_fail = "server-cert-mismatch";
++ }
++ }
++ if (debug_certs) {
++ dbg("\n***********************************************");
++ dbg("SRV: cert info at i=" + i);
++ dbg("SRV: cert info cert" + certs[i]);
++ dbg("===============================================");
++ dbg("SRV: cert info srv" + trustsrvCerts[i]);
++ dbg("***********************************************");
++ }
++ }
++ if (!ok) {
++ throw new CertificateException(
++ "Server Cert Chain != serverCert Applet Parameter Cert Chain.");
++ }
++ dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1));
++ }
++ }
++ };
++
++ trustOneCert = new TrustManager[] {
++ /*
++ * this one accepts only the retrieved server
++ * cert by SSLSocket by this applet we stored in
++ * trustallCerts that user has accepted or applet
++ * parameter trustAllVncCerts=yes is set. This is
++ * for when we reconnect after the user has manually
++ * accepted the trustall cert in the dialog (or set
++ * trustAllVncCerts=yes applet param.)
++ */
++ new X509TrustManager() {
++ public java.security.cert.X509Certificate[]
++ getAcceptedIssuers() {
++ return null;
++ }
++ public void checkClientTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) throws CertificateException {
++ throw new CertificateException("No Clients (ONE)");
++ }
++ public void checkServerTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) throws CertificateException {
++ /* we want to check 'certs' against 'trustallCerts' */
++ if (trustallCerts == null) {
++ throw new CertificateException(
++ "No Trust All Server Certs array.");
++ }
++ if (trustallCerts.length < 1) {
++ throw new CertificateException(
++ "No Trust All Server Certs.");
++ }
++ if (certs == null) {
++ throw new CertificateException(
++ "No this-certs array.");
++ }
++ if (certs.length < 1) {
++ throw new CertificateException(
++ "No this-certs Certs.");
++ }
++ if (certs.length != trustallCerts.length) {
++ throw new CertificateException(
++ "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length);
++ }
++ boolean ok = true;
++ for (int i = 0; i < certs.length; i++) {
++ if (! trustallCerts[i].equals(certs[i])) {
++ ok = false;
++ dbg("ONE: cert mismatch at i=" + i);
++ dbg("ONE: cert mismatch cert" + certs[i]);
++ dbg("ONE: cert mismatch all" + trustallCerts[i]);
++ }
++ if (debug_certs) {
++ dbg("\n***********************************************");
++ dbg("ONE: cert info at i=" + i);
++ dbg("ONE: cert info cert" + certs[i]);
++ dbg("===============================================");
++ dbg("ONE: cert info all" + trustallCerts[i]);
++ dbg("***********************************************");
++ }
++ }
++ if (!ok) {
++ throw new CertificateException(
++ "Server Cert Chain != TRUSTALL Cert Chain.");
++ }
++ dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1));
++ }
++ }
++ };
++
++ /*
++ * The above TrustManagers are used:
++ *
++ * 1) to retrieve the server cert in case of failure to
++ * display it to the user in a dialog.
++ * 2) to subsequently connect to the server if user agrees.
++ */
++
++ /*
++ * build oneTimeKey cert+key if supplied in applet parameter:
++ */
++ if (viewer.oneTimeKey != null && viewer.oneTimeKey.equals("PROMPT")) {
++ ClientCertDialog d = new ClientCertDialog();
++ viewer.oneTimeKey = d.queryUser();
++ }
++ if (viewer.oneTimeKey != null && viewer.oneTimeKey.indexOf(",") > 0) {
++ int idx = viewer.oneTimeKey.indexOf(",");
++
++ String onetimekey = viewer.oneTimeKey.substring(0, idx);
++ byte[] key = hex2bytes(onetimekey);
++ String onetimecert = viewer.oneTimeKey.substring(idx+1);
++ byte[] cert = hex2bytes(onetimecert);
++
++ KeyFactory kf = KeyFactory.getInstance("RSA");
++ PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key );
++ PrivateKey ff = kf.generatePrivate (keysp);
++ if (debug_certs) {
++ dbg("one time key " + ff);
++ }
++
++ CertificateFactory cf = CertificateFactory.getInstance("X.509");
++ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
++ Certificate[] certs = new Certificate[c.toArray().length];
++ if (c.size() == 1) {
++ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
++ if (debug_certs) {
++ dbg("one time cert" + tmpcert);
++ }
++ certs[0] = tmpcert;
++ } else {
++ certs = (Certificate[]) c.toArray();
++ }
++
++ KeyStore ks = KeyStore.getInstance("JKS");
++ ks.load(null, null);
++ ks.setKeyEntry("onetimekey", ff, "".toCharArray(), certs);
++ String da = KeyManagerFactory.getDefaultAlgorithm();
++ KeyManagerFactory kmf = KeyManagerFactory.getInstance(da);
++ kmf.init(ks, "".toCharArray());
++
++ mykey = kmf.getKeyManagers();
++ }
++
++ /*
++ * build serverCert cert if supplied in applet parameter:
++ */
++ if (viewer.serverCert != null) {
++ CertificateFactory cf = CertificateFactory.getInstance("X.509");
++ byte[] cert = hex2bytes(viewer.serverCert);
++ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
++ trustsrvCerts = new Certificate[c.toArray().length];
++ if (c.size() == 1) {
++ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
++ trustsrvCerts[0] = tmpcert;
++ } else {
++ trustsrvCerts = (Certificate[]) c.toArray();
++ }
++ }
++
++ /* the trust loc certs context: */
++ try {
++ trustloc_ctx = SSLContext.getInstance("SSL");
++
++ /*
++ * below is a failed attempt to get jvm's default
++ * trust manager using null (below) makes it so
++ * for HttpsURLConnection the server cannot be
++ * verified (no prompting.)
++ */
++ if (false) {
++ boolean didit = false;
++ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
++ tmf.init((KeyStore) null);
++ TrustManager [] tml = tmf.getTrustManagers();
++ for (int i = 0; i < tml.length; i++) {
++ TrustManager tm = tml[i];
++ if (tm instanceof X509TrustManager) {
++ TrustManager tm1[] = new TrustManager[1];
++ tm1[0] = tm;
++ trustloc_ctx.init(mykey, tm1, null);
++ didit = true;
++ break;
++ }
++ }
++ if (!didit) {
++ trustloc_ctx.init(mykey, null, null);
++ }
++ } else {
++ /* we have to set trust manager to null */
++ trustloc_ctx.init(mykey, null, null);
++ }
++
++ } catch (Exception e) {
++ String msg = "SSL trustloc_ctx FAILED.";
++ dbg(msg);
++ throw new Exception(msg);
++ }
++
++ /* the trust all certs context: */
++ try {
++ trustall_ctx = SSLContext.getInstance("SSL");
++ trustall_ctx.init(mykey, trustAllCerts, new
++ java.security.SecureRandom());
++
++ } catch (Exception e) {
++ String msg = "SSL trustall_ctx FAILED.";
++ dbg(msg);
++ throw new Exception(msg);
++ }
++
++ /* the trust url certs context: */
++ try {
++ trusturl_ctx = SSLContext.getInstance("SSL");
++ trusturl_ctx.init(mykey, trustUrlCert, new
++ java.security.SecureRandom());
++
++ } catch (Exception e) {
++ String msg = "SSL trusturl_ctx FAILED.";
++ dbg(msg);
++ throw new Exception(msg);
++ }
++
++ /* the trust srv certs context: */
++ try {
++ trustsrv_ctx = SSLContext.getInstance("SSL");
++ trustsrv_ctx.init(mykey, trustSrvCert, new
++ java.security.SecureRandom());
++
++ } catch (Exception e) {
++ String msg = "SSL trustsrv_ctx FAILED.";
++ dbg(msg);
++ throw new Exception(msg);
++ }
++
++ /* the trust the one cert from server context: */
++ try {
++ trustone_ctx = SSLContext.getInstance("SSL");
++ trustone_ctx.init(mykey, trustOneCert, new
++ java.security.SecureRandom());
++
++ } catch (Exception e) {
++ String msg = "SSL trustone_ctx FAILED.";
++ dbg(msg);
++ throw new Exception(msg);
++ }
++ }
++
++ /*
++ * we call this early on to 1) check for a proxy, 2) grab
++ * Browser/JVM accepted HTTPS cert.
++ */
++ public void check_for_proxy_and_grab_vnc_server_cert() {
++
++ trusturlCerts = null;
++ proxy_in_use = false;
++
++ if (viewer.ignoreProxy) {
++ /* applet param says skip it. */
++ /* the downside is we do not set trusturlCerts for comparison later... */
++ /* nor do we autodetect x11vnc for GET=1. */
++ return;
++ }
++
++ dbg("------------------------------------------------");
++ dbg("Into check_for_proxy_and_grab_vnc_server_cert():");
++
++ dbg("TRYING HTTPS:");
++ String ustr = "https://" + host + ":";
++ if (viewer.httpsPort != null) {
++ ustr += viewer.httpsPort;
++ } else {
++ ustr += port;
++ }
++ ustr += viewer.urlPrefix + "/check.https.proxy.connection";
++ dbg("ustr is: " + ustr);
++
++ try {
++ /* prepare for an HTTPS URL connection to host:port */
++ URL url = new URL(ustr);
++ HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
++
++ if (mykey != null) {
++ /* with oneTimeKey (mykey) we can't use the default SSL context */
++ if (trustsrvCerts != null) {
++ dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert.");
++ https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory());
++ } else if (trustloc_ctx != null) {
++ dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert.");
++ https.setSSLSocketFactory(trustloc_ctx.getSocketFactory());
++ }
++ }
++
++ https.setUseCaches(false);
++ https.setRequestMethod("GET");
++ https.setRequestProperty("Pragma", "No-Cache");
++ https.setRequestProperty("Proxy-Connection", "Keep-Alive");
++ https.setDoInput(true);
++
++ dbg("trying https.connect()");
++ https.connect();
++
++ dbg("trying https.getServerCertificates()");
++ trusturlCerts = https.getServerCertificates();
++
++ if (trusturlCerts == null) {
++ dbg("set trusturlCerts to null!");
++ } else {
++ dbg("set trusturlCerts to non-null");
++ }
++
++ if (https.usingProxy()) {
++ proxy_in_use = true;
++ dbg("An HTTPS proxy is in use. There may be connection problems.");
++ }
++
++ dbg("trying https.getContent()");
++ Object output = https.getContent();
++ dbg("trying https.disconnect()");
++ https.disconnect();
++ if (! viewer.GET) {
++ String header = https.getHeaderField("VNC-Server");
++ if (header != null && header.startsWith("x11vnc")) {
++ dbg("detected x11vnc server (1), setting GET=1");
++ viewer.GET = true;
++ }
++ }
++
++ } catch(Exception e) {
++ dbg("HttpsURLConnection: " + e.getMessage());
++ }
++
++ if (proxy_in_use) {
++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
++ dbg("------------------------------------------------");
++ return;
++ } else if (trusturlCerts != null && !viewer.forceProxy) {
++ /* Allow user to require HTTP check? use forceProxy for now. */
++ dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct.");
++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
++ dbg("------------------------------------------------");
++ return;
++ }
++
++ /*
++ * XXX need to remember scenario where this extra check
++ * gives useful info. User's Browser proxy settings?
++ */
++ dbg("TRYING HTTP:");
++ ustr = "http://" + host + ":" + port;
++ ustr += viewer.urlPrefix + "/index.vnc";
++ dbg("ustr is: " + ustr);
++
++ try {
++ /* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */
++ URL url = new URL(ustr);
++ HttpURLConnection http = (HttpURLConnection)
++ url.openConnection();
++
++ http.setUseCaches(false);
++ http.setRequestMethod("GET");
++ http.setRequestProperty("Pragma", "No-Cache");
++ http.setRequestProperty("Proxy-Connection", "Keep-Alive");
++ http.setDoInput(true);
++
++ dbg("trying http.connect()");
++ http.connect();
++
++ if (http.usingProxy()) {
++ proxy_in_use = true;
++ dbg("An HTTP proxy is in use. There may be connection problems.");
++ }
++ dbg("trying http.getContent()");
++ Object output = http.getContent();
++ dbg("trying http.disconnect()");
++ http.disconnect();
++ if (! viewer.GET) {
++ String header = http.getHeaderField("VNC-Server");
++ if (header != null && header.startsWith("x11vnc")) {
++ dbg("detected x11vnc server (2), setting GET=1");
++ viewer.GET = true;
++ }
++ }
++ } catch(Exception e) {
++ dbg("HttpURLConnection: " + e.getMessage());
++ }
++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
++ dbg("------------------------------------------------");
++ }
++
++ public Socket connectSock() throws IOException {
++ /*
++ * first try a https connection to detect a proxy, and
++ * grab the VNC server cert at the same time:
++ */
++ check_for_proxy_and_grab_vnc_server_cert();
++
++ boolean srv_cert = false;
++
++ if (trustsrvCerts != null) {
++ /* applet parameter suppled serverCert */
++ dbg("viewer.trustSrvCert-0 using trustsrv_ctx");
++ factory = trustsrv_ctx.getSocketFactory();
++ srv_cert = true;
++ } else if (viewer.trustAllVncCerts) {
++ /* trust all certs (no checking) */
++ dbg("viewer.trustAllVncCerts-0 using trustall_ctx");
++ factory = trustall_ctx.getSocketFactory();
++ } else if (trusturlCerts != null) {
++ /* trust certs the Browser/JVM accepted in check_for_proxy... */
++ dbg("using trusturl_ctx");
++ factory = trusturl_ctx.getSocketFactory();
++ } else {
++ /* trust the local defaults */
++ dbg("using trustloc_ctx");
++ factory = trustloc_ctx.getSocketFactory();
++ }
++
++ socket = null;
++
++ try {
++ if (proxy_in_use && viewer.forceProxy) {
++ throw new Exception("forcing proxy (forceProxy)");
++ } else if (viewer.CONNECT != null) {
++ throw new Exception("forcing CONNECT");
++ }
++
++ int timeout = 6;
++ if (timeout > 0) {
++ socket = (SSLSocket) factory.createSocket();
++ InetSocketAddress inetaddr = new InetSocketAddress(host, port);
++ dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port);
++ socket.connect(inetaddr, timeout * 1000);
++ } else {
++ socket = (SSLSocket) factory.createSocket(host, port);
++ }
++
++ } catch (Exception esock) {
++ dbg("socket error: " + esock.getMessage());
++ if (proxy_in_use || viewer.CONNECT != null) {
++ proxy_failure = true;
++ if (proxy_in_use) {
++ dbg("HTTPS proxy in use. Trying to go with it.");
++ } else {
++ dbg("viewer.CONNECT reverse proxy in use. Trying to go with it.");
++ }
++ try {
++ socket = proxy_socket(factory);
++ } catch (Exception e) {
++ dbg("proxy_socket error: " + e.getMessage());
++ }
++ } else {
++ /* n.b. socket is left in error state to cause ex. below. */
++ }
++ }
++
++ try {
++ socket.startHandshake();
++
++ dbg("The Server Connection Verified OK on 1st try.");
++
++ java.security.cert.Certificate[] currentTrustedCerts;
++ BrowserCertsDialog bcd;
++
++ SSLSession sess = socket.getSession();
++ currentTrustedCerts = sess.getPeerCertificates();
++
++ if (viewer.trustAllVncCerts) {
++ dbg("viewer.trustAllVncCerts-1 keeping socket.");
++ } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) {
++ try {
++ socket.close();
++ } catch (Exception e) {
++ dbg("socket is grumpy.");
++ }
++ socket = null;
++ throw new SSLHandshakeException("no current certs");
++ }
++
++ String serv = "";
++ try {
++ CertInfo ci = new CertInfo(currentTrustedCerts[0]);
++ serv = ci.get_certinfo("CN");
++ } catch (Exception e) {
++ ;
++ }
++
++ if (viewer.trustAllVncCerts) {
++ dbg("viewer.trustAllVncCerts-2 skipping browser certs dialog");
++ user_wants_to_see_cert = false;
++ } else if (viewer.serverCert != null && trustsrvCerts != null) {
++ dbg("viewer.serverCert-1 skipping browser certs dialog");
++ user_wants_to_see_cert = false;
++ } else if (viewer.trustUrlVncCert) {
++ dbg("viewer.trustUrlVncCert-1 skipping browser certs dialog");
++ user_wants_to_see_cert = false;
++ } else {
++ /* have a dialog with the user: */
++ bcd = new BrowserCertsDialog(serv, host + ":" + port);
++ dbg("browser certs dialog begin.");
++ bcd.queryUser();
++ dbg("browser certs dialog finished.");
++
++ if (bcd.showCertDialog) {
++ String msg = "user wants to see cert";
++ dbg(msg);
++ user_wants_to_see_cert = true;
++ if (cert_fail == null) {
++ cert_fail = "user-view";
++ }
++ throw new SSLHandshakeException(msg);
++ } else {
++ user_wants_to_see_cert = false;
++ dbg("browser certs dialog: user said yes, accept it");
++ }
++ }
++
++ } catch (SSLHandshakeException eh) {
++ dbg("SSLHandshakeException: could not automatically verify Server.");
++ dbg("msg: " + eh.getMessage());
++
++
++ /* send a cleanup string just in case: */
++ String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n";
++
++ try {
++ OutputStream os = socket.getOutputStream();
++ os.write(getoutstr.getBytes());
++ socket.close();
++ } catch (Exception e) {
++ dbg("socket is grumpy!");
++ }
++
++ /* reload */
++
++ socket = null;
++
++ String reason = null;
++
++ if (srv_cert) {
++ /* for serverCert usage we make this a fatal error. */
++ throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'");
++ /* see below in TrustDialog were we describe this case to user anyway */
++ }
++
++ /*
++ * Reconnect, trusting any cert, so we can grab
++ * the cert to show it to the user in a dialog
++ * for him to manually accept. This connection
++ * is not used for anything else.
++ */
++ factory = trustall_ctx.getSocketFactory();
++ if (proxy_failure) {
++ socket = proxy_socket(factory);
++ } else {
++ socket = (SSLSocket) factory.createSocket(host, port);
++ }
++
++ if (debug_certs) {
++ dbg("trusturlCerts: " + trusturlCerts);
++ dbg("trustsrvCerts: " + trustsrvCerts);
++ }
++ if (trusturlCerts == null && cert_fail == null) {
++ cert_fail = "missing-certs";
++ }
++
++ try {
++ socket.startHandshake();
++
++ dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK.");
++
++ /* grab the cert: */
++ try {
++ SSLSession sess = socket.getSession();
++ trustallCerts = sess.getPeerCertificates();
++ } catch (Exception e) {
++ throw new Exception("Could not get " +
++ "Peer Certificate");
++ }
++ if (debug_certs) {
++ dbg("trustallCerts: " + trustallCerts);
++ }
++
++ if (viewer.trustAllVncCerts) {
++ dbg("viewer.trustAllVncCerts-3. skipping dialog, trusting everything.");
++ } else if (! browser_cert_match()) {
++ /*
++ * close socket now, we will reopen after
++ * dialog if user agrees to use the cert.
++ */
++ try {
++ OutputStream os = socket.getOutputStream();
++ os.write(getoutstr.getBytes());
++ socket.close();
++ } catch (Exception e) {
++ dbg("socket is grumpy!!");
++ }
++ socket = null;
++
++ /* dialog with user to accept cert or not: */
++
++ TrustDialog td= new TrustDialog(host, port,
++ trustallCerts);
++
++ if (cert_fail == null) {
++ ;
++ } else if (cert_fail.equals("user-view")) {
++ reason = "Reason for this Dialog:\n\n"
++ + " You Asked to View the Certificate.";
++ } else if (cert_fail.equals("server-cert-mismatch")) {
++ /* this is now fatal error, see above. */
++ reason = "Reason for this Dialog:\n\n"
++ + " The VNC Server's Certificate does not match the Certificate\n"
++ + " specified in the supplied 'serverCert' Applet Parameter.";
++ } else if (cert_fail.equals("cert-mismatch")) {
++ reason = "Reason for this Dialog:\n\n"
++ + " The VNC Server's Certificate does not match the Website's\n"
++ + " HTTPS Certificate (that you previously accepted; either\n"
++ + " manually or automatically via Certificate Authority.)";
++ } else if (cert_fail.equals("missing-certs")) {
++ reason = "Reason for this Dialog:\n\n"
++ + " Not all Certificates could be obtained to check.";
++ }
++
++ if (! td.queryUser(reason)) {
++ String msg = "User decided against it.";
++ dbg(msg);
++ throw new IOException(msg);
++ }
++ }
++
++ } catch (Exception ehand2) {
++ dbg("** Could not TrustAll Verify Server!");
++
++ throw new IOException(ehand2.getMessage());
++ }
++
++ /* reload again: */
++
++ if (socket != null) {
++ try {
++ socket.close();
++ } catch (Exception e) {
++ dbg("socket is grumpy!!!");
++ }
++ socket = null;
++ }
++
++ /*
++ * Now connect a 3rd time, using the cert
++ * retrieved during connection 2 (sadly, that
++ * the user likely blindly agreed to...)
++ */
++
++ factory = trustone_ctx.getSocketFactory();
++ if (proxy_failure) {
++ socket = proxy_socket(factory);
++ } else {
++ socket = (SSLSocket) factory.createSocket(host, port);
++ }
++
++ try {
++ socket.startHandshake();
++ dbg("TrustAll/TrustOne Server Connection Verified #3.");
++
++ } catch (Exception ehand3) {
++ dbg("** Could not TrustAll/TrustOne Verify Server #3.");
++
++ throw new IOException(ehand3.getMessage());
++ }
++ }
++
++ /* we have socket (possibly null) at this point, so proceed: */
++
++ /* handle x11vnc GET=1, if applicable: */
++ if (socket != null && viewer.GET) {
++ String str = "GET ";
++ str += viewer.urlPrefix;
++ str += "/request.https.vnc.connection";
++ str += " HTTP/1.0\r\n";
++ str += "Pragma: No-Cache\r\n";
++ str += "\r\n";
++
++ System.out.println("sending: " + str);
++ OutputStream os = socket.getOutputStream();
++ String type = "os";
++
++ if (type == "os") {
++ os.write(str.getBytes());
++ os.flush();
++ System.out.println("used OutputStream");
++ } else if (type == "bs") {
++ BufferedOutputStream bs = new BufferedOutputStream(os);
++ bs.write(str.getBytes());
++ bs.flush();
++ System.out.println("used BufferedOutputStream");
++ } else if (type == "ds") {
++ DataOutputStream ds = new DataOutputStream(os);
++ ds.write(str.getBytes());
++ ds.flush();
++ System.out.println("used DataOutputStream");
++ }
++ if (false) {
++ String rep = "";
++ DataInputStream is = new DataInputStream(
++ new BufferedInputStream(socket.getInputStream(), 16384));
++ while (true) {
++ rep += readline(is);
++ if (rep.indexOf("\r\n\r\n") >= 0) {
++ break;
++ }
++ }
++ System.out.println("rep: " + rep);
++ }
++ }
++
++ dbg("SSL returning socket to caller.");
++ dbg("");
++
++ /* could be null, let caller handle that. */
++ return (Socket) socket;
++ }
++
++ boolean browser_cert_match() {
++ String msg = "Browser URL accept previously accepted cert";
++
++ if (user_wants_to_see_cert) {
++ return false;
++ }
++
++ if (viewer.serverCert != null || trustsrvCerts != null) {
++ if (cert_fail == null) {
++ cert_fail = "server-cert-mismatch";
++ }
++ }
++ if (trustallCerts != null && trusturlCerts != null) {
++ if (trustallCerts.length == trusturlCerts.length) {
++ boolean ok = true;
++ /* check toath trustallCerts (socket) equals trusturlCerts (browser) */
++ for (int i = 0; i < trusturlCerts.length; i++) {
++ if (! trustallCerts[i].equals(trusturlCerts[i])) {
++ dbg("BCM: cert mismatch at i=" + i);
++ dbg("BCM: cert mismatch url" + trusturlCerts[i]);
++ dbg("BCM: cert mismatch all" + trustallCerts[i]);
++ ok = false;
++ }
++ }
++ if (ok) {
++ System.out.println(msg);
++ if (cert_fail == null) {
++ cert_fail = "did-not-fail";
++ }
++ return true;
++ } else {
++ if (cert_fail == null) {
++ cert_fail = "cert-mismatch";
++ }
++ return false;
++ }
++ }
++ }
++ if (cert_fail == null) {
++ cert_fail = "missing-certs";
++ }
++ return false;
++ }
++
++ private void dbg(String s) {
++ if (debug) {
++ System.out.println(s);
++ }
++ }
++
++ private int gint(String s) {
++ int n = -1;
++ try {
++ Integer I = new Integer(s);
++ n = I.intValue();
++ } catch (Exception ex) {
++ return -1;
++ }
++ return n;
++ }
++
++ /* this will do the proxy CONNECT negotiation and hook us up. */
++
++ private void proxy_helper(String proxyHost, int proxyPort) {
++
++ boolean proxy_auth = false;
++ String proxy_auth_basic_realm = "";
++ String hp = host + ":" + port;
++ dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp);
++
++ /* we loop here a few times trying for the password case */
++ for (int k=0; k < 2; k++) {
++ dbg("proxy_in_use psocket: " + k);
++
++ if (proxySock != null) {
++ try {
++ proxySock.close();
++ } catch (Exception e) {
++ dbg("proxy socket is grumpy.");
++ }
++ }
++
++ proxySock = psocket(proxyHost, proxyPort);
++ if (proxySock == null) {
++ dbg("1-a sadly, returning a null socket");
++ return;
++ }
++
++ String req1 = "CONNECT " + hp + " HTTP/1.1\r\n"
++ + "Host: " + hp + "\r\n";
++
++ dbg("requesting via proxy: " + req1);
++
++ if (proxy_auth) {
++ if (proxy_auth_string == null) {
++ ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm);
++ pp.queryUser();
++ proxy_auth_string = pp.getAuth();
++ }
++ //dbg("auth1: " + proxy_auth_string);
++
++ String auth2 = Base64Coder.encodeString(proxy_auth_string);
++ //dbg("auth2: " + auth2);
++
++ req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n";
++ //dbg("req1: " + req1);
++
++ dbg("added Proxy-Authorization: Basic ... to request");
++ }
++ req1 += "\r\n";
++
++ try {
++ proxy_os.write(req1.getBytes());
++ String reply = readline(proxy_is);
++
++ dbg("proxy replied: " + reply.trim());
++
++ if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) {
++ proxy_auth = true;
++ proxySock.close();
++ } else if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) {
++ proxySock.close();
++ proxySock = psocket(proxyHost, proxyPort);
++ if (proxySock == null) {
++ dbg("2-a sadly, returning a null socket");
++ return;
++ }
++ }
++ } catch(Exception e) {
++ dbg("some proxy socket problem: " + e.getMessage());
++ }
++
++ /* read the rest of the HTTP headers */
++ while (true) {
++ String line = readline(proxy_is);
++ dbg("proxy line: " + line.trim());
++ if (proxy_auth) {
++ String uc = line.toLowerCase();
++ if (uc.indexOf("proxy-authenticate:") == 0) {
++ if (uc.indexOf(" basic ") >= 0) {
++ int idx = uc.indexOf(" realm");
++ if (idx >= 0) {
++ proxy_auth_basic_realm = uc.substring(idx+1);
++ }
++ }
++ }
++ }
++ if (line.equals("\r\n") || line.equals("\n")) {
++ break;
++ }
++ }
++ if (!proxy_auth || proxy_auth_basic_realm.equals("")) {
++ /* we only try once for the non-password case: */
++ break;
++ }
++ }
++ }
++
++ public SSLSocket proxy_socket(SSLSocketFactory factory) {
++ Properties props = null;
++ String proxyHost = null;
++ int proxyPort = 0;
++ String proxyHost_nossl = null;
++ int proxyPort_nossl = 0;
++ String str;
++
++ /* see if we can guess the proxy info from Properties: */
++ try {
++ props = System.getProperties();
++ } catch (Exception e) {
++ /* sandboxed applet might not be able to read it. */
++ dbg("props failed: " + e.getMessage());
++ }
++ if (viewer.proxyHost != null) {
++ dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters.");
++ proxyHost = viewer.proxyHost;
++ if (viewer.proxyPort != null) {
++ proxyPort = gint(viewer.proxyPort);
++ } else {
++ proxyPort = 8080;
++ }
++
++ } else if (props != null) {
++ dbg("\n---------------\nAll props:");
++ props.list(System.out);
++ dbg("\n---------------\n\n");
++
++ /* scrape throught properties looking for proxy info: */
++
++ for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) {
++ String s = (String) e.nextElement();
++ String v = System.getProperty(s);
++ String s2 = s.toLowerCase();
++ String v2 = v.toLowerCase();
++
++ if (s2.indexOf("proxy.https.host") >= 0) {
++ proxyHost = v2;
++ continue;
++ }
++ if (s2.indexOf("proxy.https.port") >= 0) {
++ proxyPort = gint(v2);
++ continue;
++ }
++ if (s2.indexOf("proxy.http.host") >= 0) {
++ proxyHost_nossl = v2;
++ continue;
++ }
++ if (s2.indexOf("proxy.http.port") >= 0) {
++ proxyPort_nossl = gint(v2);
++ continue;
++ }
++ }
++
++ for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) {
++ String s = (String) e.nextElement();
++ String v = System.getProperty(s);
++ String s2 = s.toLowerCase();
++ String v2 = v.toLowerCase();
++
++ if (proxyHost != null && proxyPort > 0) {
++ break;
++ }
++
++ // look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082
++ if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) {
++ continue;
++ }
++ if (v2.indexOf("http") < 0) {
++ continue;
++ }
++
++ String[] pieces = v.split("[,;]");
++ for (int i = 0; i < pieces.length; i++) {
++ String p = pieces[i];
++ int j = p.indexOf("https");
++ if (j < 0) {
++ j = p.indexOf("http");
++ if (j < 0) {
++ continue;
++ }
++ }
++ j = p.indexOf("=", j);
++ if (j < 0) {
++ continue;
++ }
++ p = p.substring(j+1);
++ String [] hp = p.split(":");
++ if (hp.length != 2) {
++ continue;
++ }
++ if (hp[0].length() > 1 && hp[1].length() > 1) {
++
++ proxyPort = gint(hp[1]);
++ if (proxyPort < 0) {
++ continue;
++ }
++ proxyHost = new String(hp[0]);
++ break;
++ }
++ }
++ }
++ }
++ if (proxyHost != null) {
++ if (proxyHost_nossl != null && proxyPort_nossl > 0) {
++ dbg("Using http proxy info instead of https.");
++ proxyHost = proxyHost_nossl;
++ proxyPort = proxyPort_nossl;
++ }
++ }
++
++ if (proxy_in_use) {
++ if (proxy_dialog_host != null && proxy_dialog_port > 0) {
++ proxyHost = proxy_dialog_host;
++ proxyPort = proxy_dialog_port;
++ }
++ if (proxyHost != null) {
++ dbg("Lucky us! we figured out the Proxy parameters: " + proxyHost + " " + proxyPort);
++ } else {
++ /* ask user to help us: */
++ ProxyDialog pd = new ProxyDialog(proxyHost, proxyPort);
++ pd.queryUser();
++ proxyHost = pd.getHost();
++ proxyPort = pd.getPort();
++ proxy_dialog_host = new String(proxyHost);
++ proxy_dialog_port = proxyPort;
++ dbg("User said host: " + pd.getHost() + " port: " + pd.getPort());
++ }
++
++ proxy_helper(proxyHost, proxyPort);
++ if (proxySock == null) {
++ return null;
++ }
++ } else if (viewer.CONNECT != null) {
++ dbg("viewer.CONNECT psocket:");
++ proxySock = psocket(host, port);
++ if (proxySock == null) {
++ dbg("1-b sadly, returning a null socket");
++ return null;
++ }
++ }
++
++ if (viewer.CONNECT != null) {
++ String hp = viewer.CONNECT;
++ String req2 = "CONNECT " + hp + " HTTP/1.1\r\n"
++ + "Host: " + hp + "\r\n\r\n";
++
++ dbg("requesting2: " + req2);
++
++ try {
++ proxy_os.write(req2.getBytes());
++ String reply = readline(proxy_is);
++
++ dbg("proxy replied2: " + reply.trim());
++
++ if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) {
++ proxySock.close();
++ proxySock = psocket(proxyHost, proxyPort);
++ if (proxySock == null) {
++ dbg("2-b sadly, returning a null socket");
++ return null;
++ }
++ }
++ } catch(Exception e) {
++ dbg("proxy socket problem-2: " + e.getMessage());
++ }
++
++ while (true) {
++ String line = readline(proxy_is);
++ dbg("proxy line2: " + line.trim());
++ if (line.equals("\r\n") || line.equals("\n")) {
++ break;
++ }
++ }
++ }
++
++ Socket sslsock = null;
++ try {
++ sslsock = factory.createSocket(proxySock, host, port, true);
++ } catch(Exception e) {
++ dbg("sslsock prob: " + e.getMessage());
++ dbg("3 sadly, returning a null socket");
++ }
++
++ return (SSLSocket) sslsock;
++ }
++
++ Socket psocket(String h, int p) {
++ Socket psock = null;
++ try {
++ psock = new Socket(h, p);
++ proxy_is = new DataInputStream(new BufferedInputStream(
++ psock.getInputStream(), 16384));
++ proxy_os = psock.getOutputStream();
++ } catch(Exception e) {
++ dbg("psocket prob: " + e.getMessage());
++ return null;
++ }
++
++ return psock;
++ }
++
++ String readline(DataInputStream i) {
++ byte[] ba = new byte[1];
++ String s = new String("");
++ ba[0] = 0;
++ try {
++ while (ba[0] != 0xa) {
++ ba[0] = (byte) i.readUnsignedByte();
++ s += new String(ba);
++ }
++ } catch (Exception e) {
++ ;
++ }
++ return s;
++ }
++}
++
++class TrustDialog implements ActionListener {
++ String msg, host, text;
++ int port;
++ java.security.cert.Certificate[] trustallCerts = null;
++ boolean viewing_cert = false;
++ boolean trust_this_session = false;
++
++ /*
++ * this is the gui to show the user the cert and info and ask
++ * them if they want to continue using this cert.
++ */
++
++ Button ok, cancel, viewcert;
++ TextArea textarea;
++ Checkbox accept, deny;
++ Dialog dialog;
++
++ String s1 = "Accept this certificate temporarily for this session";
++ String s2 = "Do not accept this certificate and do not connect to"
++ + " this VNC server";
++ String ln = "\n---------------------------------------------------\n\n";
++
++ TrustDialog (String h, int p, java.security.cert.Certificate[] s) {
++ host = h;
++ port = p;
++ trustallCerts = s;
++
++ msg = "VNC Server " + host + ":" + port + " Not Verified";
++ }
++
++ public boolean queryUser(String reason) {
++
++ /* create and display the dialog for unverified cert. */
++
++ Frame frame = new Frame(msg);
++
++ dialog = new Dialog(frame, true);
++
++ String infostr = "";
++ if (trustallCerts.length == 1) {
++ CertInfo ci = new CertInfo(trustallCerts[0]);
++ infostr = ci.get_certinfo("all");
++ }
++ if (reason != null) {
++ reason += "\n\n";
++ }
++
++ text = "\n"
+++ "Unable to verify the identity of\n"
+++ "\n"
+++ " " + host + ":" + port + "\n"
+++ "\n"
+++ infostr
+++ "\n"
+++ "as a trusted VNC server.\n"
+++ "\n"
+++ reason
+++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n"
+++ "is due to one of the following:\n"
+++ "\n"
+++ " - Your requesting to View the Certificate before accepting.\n"
+++ "\n"
+++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n"
+++ " Authority not recognized by your Web Browser or Java Plugin runtime.\n"
+++ "\n"
+++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n"
+++ " the Apache Web server has a certificate *different* from the VNC server's.\n"
+++ "\n"
+++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n"
+++ " obtained by this applet to compare the VNC Server Certificate against.\n"
+++ "\n"
+++ " - The VNC Server's Certificate does not match the one specified in the\n"
+++ " supplied 'serverCert' Java Applet Parameter.\n"
+++ "\n"
+++ " - A Man-In-The-Middle attack impersonating as the VNC server that you wish\n"
+++ " to connect to. (Wouldn't that be exciting!!)\n"
+++ "\n"
+++ "By safely copying the VNC server's Certificate (or using a common Certificate\n"
+++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n"
+++ "automatically authenticate this VNC Server.\n"
+++ "\n"
+++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n"
+++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n"
+++ "certificate (except for the Apache portal case above where they don't match.)\n"
+++ "\n"
+++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n"
+++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n"
+++ "and thereby see no dialog from this VNC Viewer applet.\n"
++;
++
++ /* the accept / do-not-accept radio buttons: */
++ CheckboxGroup checkbox = new CheckboxGroup();
++ accept = new Checkbox(s1, true, checkbox);
++ deny = new Checkbox(s2, false, checkbox);
++
++ /* put the checkboxes in a panel: */
++ Panel check = new Panel();
++ check.setLayout(new GridLayout(2, 1));
++
++ check.add(accept);
++ check.add(deny);
++
++ /* make the 3 buttons: */
++ ok = new Button("OK");
++ cancel = new Button("Cancel");
++ viewcert = new Button("View Certificate");
++
++ ok.addActionListener(this);
++ cancel.addActionListener(this);
++ viewcert.addActionListener(this);
++
++ /* put the buttons in their own panel: */
++ Panel buttonrow = new Panel();
++ buttonrow.setLayout(new FlowLayout(FlowLayout.LEFT));
++ buttonrow.add(viewcert);
++ buttonrow.add(ok);
++ buttonrow.add(cancel);
++
++ /* label at the top: */
++ Label label = new Label(msg, Label.CENTER);
++ label.setFont(new Font("Helvetica", Font.BOLD, 16));
++
++ /* textarea in the middle */
++ textarea = new TextArea(text, 38, 64,
++ TextArea.SCROLLBARS_VERTICAL_ONLY);
++ textarea.setEditable(false);
++
++ /* put the two panels in their own panel at bottom: */
++ Panel bot = new Panel();
++ bot.setLayout(new GridLayout(2, 1));
++ bot.add(check);
++ bot.add(buttonrow);
++
++ /* now arrange things inside the dialog: */
++ dialog.setLayout(new BorderLayout());
++
++ dialog.add("North", label);
++ dialog.add("South", bot);
++ dialog.add("Center", textarea);
++
++ dialog.pack();
++ dialog.resize(dialog.preferredSize());
++
++ dialog.show(); /* block here til OK or Cancel pressed. */
++
++ return trust_this_session;
++ }
++
++ public synchronized void actionPerformed(ActionEvent evt) {
++
++ if (evt.getSource() == viewcert) {
++ /* View Certificate button clicked */
++ if (viewing_cert) {
++ /* show the original info text: */
++ textarea.setText(text);
++ viewcert.setLabel("View Certificate");
++ viewing_cert = false;
++ } else {
++ int i;
++ /* show all (likely just one) certs: */
++ textarea.setText("");
++ for (i=0; i < trustallCerts.length; i++) {
++ int j = i + 1;
++ textarea.append("Certificate[" +
++ j + "]\n\n");
++ textarea.append(
++ trustallCerts[i].toString());
++ textarea.append(ln);
++ }
++ viewcert.setLabel("View Info");
++ viewing_cert = true;
++
++ textarea.setCaretPosition(0);
++ }
++
++ } else if (evt.getSource() == ok) {
++ /* OK button clicked */
++ if (accept.getState()) {
++ trust_this_session = true;
++ } else {
++ trust_this_session = false;
++ }
++ //dialog.dispose();
++ dialog.hide();
++
++ } else if (evt.getSource() == cancel) {
++ /* Cancel button clicked */
++ trust_this_session = false;
++
++ //dialog.dispose();
++ dialog.hide();
++ }
++ }
++
++ String get_certinfo() {
++ String all = "";
++ String fields[] = {"CN", "OU", "O", "L", "C"};
++ int i;
++ if (trustallCerts.length < 1) {
++ all = "";
++ return all;
++ }
++ String cert = trustallCerts[0].toString();
++
++ /*
++ * For now we simply scrape the cert string, there must
++ * be an API for this... perhaps optionValue?
++ */
++
++ for (i=0; i < fields.length; i++) {
++ int f, t, t1, t2;
++ String sub, mat = fields[i] + "=";
++
++ f = cert.indexOf(mat, 0);
++ if (f > 0) {
++ t1 = cert.indexOf(", ", f);
++ t2 = cert.indexOf("\n", f);
++ if (t1 < 0 && t2 < 0) {
++ continue;
++ } else if (t1 < 0) {
++ t = t2;
++ } else if (t2 < 0) {
++ t = t1;
++ } else if (t1 < t2) {
++ t = t1;
++ } else {
++ t = t2;
++ }
++ if (t > f) {
++ sub = cert.substring(f, t);
++ all = all + " " + sub + "\n";
++ }
++ }
++ }
++ return all;
++ }
++}
++
++class ProxyDialog implements ActionListener {
++ String guessedHost = null;
++ String guessedPort = null;
++ /*
++ * this is the gui to show the user the cert and info and ask
++ * them if they want to continue using this cert.
++ */
++
++ Button ok;
++ Dialog dialog;
++ TextField entry;
++ String reply = "";
++
++ ProxyDialog (String h, int p) {
++ guessedHost = h;
++ try {
++ guessedPort = Integer.toString(p);
++ } catch (Exception e) {
++ guessedPort = "8080";
++ }
++ }
++
++ public void queryUser() {
++
++ /* create and display the dialog for unverified cert. */
++
++ Frame frame = new Frame("Need Proxy host:port");
++
++ dialog = new Dialog(frame, true);
++
++
++ Label label = new Label("Please Enter your https Proxy info as host:port", Label.CENTER);
++ //label.setFont(new Font("Helvetica", Font.BOLD, 16));
++ entry = new TextField(30);
++ ok = new Button("OK");
++ ok.addActionListener(this);
++
++ String guess = "";
++ if (guessedHost != null) {
++ guess = guessedHost + ":" + guessedPort;
++ }
++ entry.setText(guess);
++
++ dialog.setLayout(new BorderLayout());
++ dialog.add("North", label);
++ dialog.add("Center", entry);
++ dialog.add("South", ok);
++ dialog.pack();
++ dialog.resize(dialog.preferredSize());
++
++ dialog.show(); /* block here til OK or Cancel pressed. */
++ return;
++ }
++
++ public String getHost() {
++ int i = reply.indexOf(":");
++ if (i < 0) {
++ return "unknown";
++ }
++ String h = reply.substring(0, i);
++ return h;
++ }
++
++ public int getPort() {
++ int i = reply.indexOf(":");
++ int p = 8080;
++ if (i < 0) {
++ return p;
++ }
++ i++;
++ String ps = reply.substring(i);
++ try {
++ Integer I = new Integer(ps);
++ p = I.intValue();
++ } catch (Exception e) {
++ ;
++ }
++ return p;
++ }
++
++ public synchronized void actionPerformed(ActionEvent evt) {
++ System.out.println(evt.getActionCommand());
++ if (evt.getSource() == ok) {
++ reply = entry.getText();
++ //dialog.dispose();
++ dialog.hide();
++ }
++ }
++}
++
++class ProxyPasswdDialog implements ActionListener {
++ String guessedHost = null;
++ String guessedPort = null;
++ String guessedUser = null;
++ String guessedPasswd = null;
++ String realm = null;
++ /*
++ * this is the gui to show the user the cert and info and ask
++ * them if they want to continue using this cert.
++ */
++
++ Button ok;
++ Dialog dialog;
++ TextField entry1;
++ TextField entry2;
++ String reply1 = "";
++ String reply2 = "";
++
++ ProxyPasswdDialog (String h, int p, String realm) {
++ guessedHost = h;
++ try {
++ guessedPort = Integer.toString(p);
++ } catch (Exception e) {
++ guessedPort = "8080";
++ }
++ this.realm = realm;
++ }
++
++ public void queryUser() {
++
++ /* create and display the dialog for unverified cert. */
++
++ Frame frame = new Frame("Proxy Requires Username and Password");
++
++ dialog = new Dialog(frame, true);
++
++ //Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER);
++ TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE);
++ entry1 = new TextField(30);
++ entry2 = new TextField(30);
++ entry2.setEchoChar('*');
++ ok = new Button("OK");
++ ok.addActionListener(this);
++
++ dialog.setLayout(new BorderLayout());
++ dialog.add("North", label);
++ dialog.add("Center", entry1);
++ dialog.add("South", entry2);
++ dialog.add("East", ok);
++ dialog.pack();
++ dialog.resize(dialog.preferredSize());
++
++ dialog.show(); /* block here til OK or Cancel pressed. */
++ return;
++ }
++
++ public String getAuth() {
++ return reply1 + ":" + reply2;
++ }
++
++ public synchronized void actionPerformed(ActionEvent evt) {
++ System.out.println(evt.getActionCommand());
++ if (evt.getSource() == ok) {
++ reply1 = entry1.getText();
++ reply2 = entry2.getText();
++ //dialog.dispose();
++ dialog.hide();
++ }
++ }
++}
++
++class ClientCertDialog implements ActionListener {
++
++ Button ok;
++ Dialog dialog;
++ TextField entry;
++ String reply = "";
++
++ ClientCertDialog() {
++ ;
++ }
++
++ public String queryUser() {
++
++ /* create and display the dialog for unverified cert. */
++
++ Frame frame = new Frame("Enter SSL Client Cert+Key String");
++
++ dialog = new Dialog(frame, true);
++
++
++ Label label = new Label("Please Enter the SSL Client Cert+Key String 308204c0...,...522d2d0a", Label.CENTER);
++ entry = new TextField(30);
++ ok = new Button("OK");
++ ok.addActionListener(this);
++
++ dialog.setLayout(new BorderLayout());
++ dialog.add("North", label);
++ dialog.add("Center", entry);
++ dialog.add("South", ok);
++ dialog.pack();
++ dialog.resize(dialog.preferredSize());
++
++ dialog.show(); /* block here til OK or Cancel pressed. */
++ return reply;
++ }
++
++ public synchronized void actionPerformed(ActionEvent evt) {
++ System.out.println(evt.getActionCommand());
++ if (evt.getSource() == ok) {
++ reply = entry.getText();
++ //dialog.dispose();
++ dialog.hide();
++ }
++ }
++}
++
++class BrowserCertsDialog implements ActionListener {
++ Button yes, no;
++ Dialog dialog;
++ String vncServer;
++ String hostport;
++ public boolean showCertDialog = true;
++
++ BrowserCertsDialog(String serv, String hp) {
++ vncServer = serv;
++ hostport = hp;
++ }
++
++ public void queryUser() {
++
++ /* create and display the dialog for unverified cert. */
++
++ Frame frame = new Frame("Use Browser/JVM Certs?");
++
++ dialog = new Dialog(frame, true);
++
++ String m = "";
++m += "\n";
++m += "This VNC Viewer applet does not have its own keystore to track\n";
++m += "SSL certificates, and so cannot authenticate the certificate\n";
++m += "of the VNC Server:\n";
++m += "\n";
++m += " " + hostport + "\n\n " + vncServer + "\n";
++m += "\n";
++m += "on its own.\n";
++m += "\n";
++m += "However, it has noticed that your Web Browser and/or Java VM Plugin\n";
++m += "has previously accepted the same certificate. You may have set\n";
++m += "this up permanently or just for this session, or the server\n";
++m += "certificate was signed by a CA cert that your Web Browser or\n";
++m += "Java VM Plugin has.\n";
++m += "\n";
++m += "If the VNC Server connection times out while you are reading this\n";
++m += "dialog, then restart the connection and try again.\n";
++m += "\n";
++m += "Should this VNC Viewer applet now connect to the above VNC server?\n";
++m += "\n";
++
++ TextArea textarea = new TextArea(m, 22, 64,
++ TextArea.SCROLLBARS_VERTICAL_ONLY);
++ textarea.setEditable(false);
++ yes = new Button("Yes");
++ yes.addActionListener(this);
++ no = new Button("No, Let Me See the Certificate.");
++ no.addActionListener(this);
++
++ dialog.setLayout(new BorderLayout());
++ dialog.add("North", textarea);
++ dialog.add("Center", yes);
++ dialog.add("South", no);
++ dialog.pack();
++ dialog.resize(dialog.preferredSize());
++
++ dialog.show(); /* block here til Yes or No pressed. */
++ System.out.println("done show()");
++ return;
++ }
++
++ public synchronized void actionPerformed(ActionEvent evt) {
++ System.out.println(evt.getActionCommand());
++ if (evt.getSource() == yes) {
++ showCertDialog = false;
++ //dialog.dispose();
++ dialog.hide();
++ } else if (evt.getSource() == no) {
++ showCertDialog = true;
++ //dialog.dispose();
++ dialog.hide();
++ }
++ System.out.println("done actionPerformed()");
++ }
++}
++
++class CertInfo {
++ String fields[] = {"CN", "OU", "O", "L", "C"};
++ java.security.cert.Certificate cert;
++ String certString = "";
++
++ CertInfo(java.security.cert.Certificate c) {
++ cert = c;
++ certString = cert.toString();
++ }
++
++ String get_certinfo(String which) {
++ int i;
++ String cs = new String(certString);
++ String all = "";
++
++ /*
++ * For now we simply scrape the cert string, there must
++ * be an API for this... perhaps optionValue?
++ */
++ for (i=0; i < fields.length; i++) {
++ int f, t, t1, t2;
++ String sub, mat = fields[i] + "=";
++
++ f = cs.indexOf(mat, 0);
++ if (f > 0) {
++ t1 = cs.indexOf(", ", f);
++ t2 = cs.indexOf("\n", f);
++ if (t1 < 0 && t2 < 0) {
++ continue;
++ } else if (t1 < 0) {
++ t = t2;
++ } else if (t2 < 0) {
++ t = t1;
++ } else if (t1 < t2) {
++ t = t1;
++ } else {
++ t = t2;
++ }
++ if (t > f) {
++ sub = cs.substring(f, t);
++ all = all + " " + sub + "\n";
++ if (which.equals(fields[i])) {
++ return sub;
++ }
++ }
++ }
++ }
++ if (which.equals("all")) {
++ return all;
++ } else {
++ return "";
++ }
++ }
++}
++
++class Base64Coder {
++
++ // Mapping table from 6-bit nibbles to Base64 characters.
++ private static char[] map1 = new char[64];
++ static {
++ int i=0;
++ for (char c='A'; c<='Z'; c++) map1[i++] = c;
++ for (char c='a'; c<='z'; c++) map1[i++] = c;
++ for (char c='0'; c<='9'; c++) map1[i++] = c;
++ map1[i++] = '+'; map1[i++] = '/'; }
++
++ // Mapping table from Base64 characters to 6-bit nibbles.
++ private static byte[] map2 = new byte[128];
++ static {
++ for (int i=0; i<map2.length; i++) map2[i] = -1;
++ for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; }
++
++ /**
++ * Encodes a string into Base64 format.
++ * No blanks or line breaks are inserted.
++ * @param s a String to be encoded.
++ * @return A String with the Base64 encoded data.
++ */
++ public static String encodeString (String s) {
++ return new String(encode(s.getBytes())); }
++
++ /**
++ * Encodes a byte array into Base64 format.
++ * No blanks or line breaks are inserted.
++ * @param in an array containing the data bytes to be encoded.
++ * @return A character array with the Base64 encoded data.
++ */
++ public static char[] encode (byte[] in) {
++ return encode(in,in.length); }
++
++ /**
++ * Encodes a byte array into Base64 format.
++ * No blanks or line breaks are inserted.
++ * @param in an array containing the data bytes to be encoded.
++ * @param iLen number of bytes to process in <code>in</code>.
++ * @return A character array with the Base64 encoded data.
++ */
++ public static char[] encode (byte[] in, int iLen) {
++ int oDataLen = (iLen*4+2)/3; // output length without padding
++ int oLen = ((iLen+2)/3)*4; // output length including padding
++ char[] out = new char[oLen];
++ int ip = 0;
++ int op = 0;
++ while (ip < iLen) {
++ int i0 = in[ip++] & 0xff;
++ int i1 = ip < iLen ? in[ip++] & 0xff : 0;
++ int i2 = ip < iLen ? in[ip++] & 0xff : 0;
++ int o0 = i0 >>> 2;
++ int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
++ int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
++ int o3 = i2 & 0x3F;
++ out[op++] = map1[o0];
++ out[op++] = map1[o1];
++ out[op] = op < oDataLen ? map1[o2] : '='; op++;
++ out[op] = op < oDataLen ? map1[o3] : '='; op++; }
++ return out; }
++
++ /**
++ * Decodes a string from Base64 format.
++ * @param s a Base64 String to be decoded.
++ * @return A String containing the decoded data.
++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data.
++ */
++ public static String decodeString (String s) {
++ return new String(decode(s)); }
++
++ /**
++ * Decodes a byte array from Base64 format.
++ * @param s a Base64 String to be decoded.
++ * @return An array containing the decoded data bytes.
++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data.
++ */
++ public static byte[] decode (String s) {
++ return decode(s.toCharArray()); }
++
++ /**
++ * Decodes a byte array from Base64 format.
++ * No blanks or line breaks are allowed within the Base64 encoded data.
++ * @param in a character array containing the Base64 encoded data.
++ * @return An array containing the decoded data bytes.
++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data.
++ */
++ public static byte[] decode (char[] in) {
++ int iLen = in.length;
++ if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
++ while (iLen > 0 && in[iLen-1] == '=') iLen--;
++ int oLen = (iLen*3) / 4;
++ byte[] out = new byte[oLen];
++ int ip = 0;
++ int op = 0;
++ while (ip < iLen) {
++ int i0 = in[ip++];
++ int i1 = in[ip++];
++ int i2 = ip < iLen ? in[ip++] : 'A';
++ int i3 = ip < iLen ? in[ip++] : 'A';
++ if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
++ throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
++ int b0 = map2[i0];
++ int b1 = map2[i1];
++ int b2 = map2[i2];
++ int b3 = map2[i3];
++ if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
++ throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
++ int o0 = ( b0 <<2) | (b1>>>4);
++ int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
++ int o2 = ((b2 & 3)<<6) | b3;
++ out[op++] = (byte)o0;
++ if (op<oLen) out[op++] = (byte)o1;
++ if (op<oLen) out[op++] = (byte)o2; }
++ return out; }
++
++ // Dummy constructor.
++ private Base64Coder() {}
++
++}
+diff -Naur vnc_javasrc.orig/VncCanvas.java vnc_javasrc/VncCanvas.java
+--- vnc_javasrc.orig/VncCanvas.java 2004-10-10 02:15:54.000000000 -0400
++++ vnc_javasrc/VncCanvas.java 2010-11-30 21:01:15.000000000 -0500
+@@ -28,13 +28,14 @@
+ import java.lang.*;
+ import java.util.zip.*;
+
++import java.util.Collections;
+
+ //
+ // VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
+ //
+
+ class VncCanvas extends Canvas
+- implements KeyListener, MouseListener, MouseMotionListener {
++ implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener {
+
+ VncViewer viewer;
+ RfbProto rfb;
+@@ -81,6 +82,20 @@
+ cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
+ cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
+
++ // kludge to not show any Java cursor in the canvas since we are
++ // showing the soft cursor (should be a user setting...)
++ Cursor dot = Toolkit.getDefaultToolkit().createCustomCursor(
++ Toolkit.getDefaultToolkit().createImage(new byte[4]), new Point(0,0),
++ "dot");
++ this.setCursor(dot);
++
++ // while we are at it... get rid of the keyboard traversals that
++ // make it so we can't type a Tab character:
++ this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
++ Collections.EMPTY_SET);
++ this.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
++ Collections.EMPTY_SET);
++
+ colors = new Color[256];
+ for (int i = 0; i < 256; i++)
+ colors[i] = new Color(cm8.getRGB(i));
+@@ -169,6 +184,7 @@
+ inputEnabled = true;
+ addMouseListener(this);
+ addMouseMotionListener(this);
++ addMouseWheelListener(this);
+ if (viewer.showControls) {
+ viewer.buttonPanel.enableRemoteAccessControls(true);
+ }
+@@ -177,6 +193,7 @@
+ inputEnabled = false;
+ removeMouseListener(this);
+ removeMouseMotionListener(this);
++ removeMouseWheelListener(this);
+ if (viewer.showControls) {
+ viewer.buttonPanel.enableRemoteAccessControls(false);
+ }
+@@ -1190,6 +1207,9 @@
+ public void mouseDragged(MouseEvent evt) {
+ processLocalMouseEvent(evt, true);
+ }
++ public void mouseWheelMoved(MouseWheelEvent evt) {
++ processLocalMouseWheelEvent(evt);
++ }
+
+ public void processLocalKeyEvent(KeyEvent evt) {
+ if (viewer.rfb != null && rfb.inNormalProtocol) {
+@@ -1221,6 +1241,19 @@
+ evt.consume();
+ }
+
++ public void processLocalMouseWheelEvent(MouseWheelEvent evt) {
++ if (viewer.rfb != null && rfb.inNormalProtocol) {
++ synchronized(rfb) {
++ try {
++ rfb.writeWheelEvent(evt);
++ } catch (Exception e) {
++ e.printStackTrace();
++ }
++ rfb.notify();
++ }
++ }
++ }
++
+ public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
+ if (viewer.rfb != null && rfb.inNormalProtocol) {
+ if (moved) {
+@@ -1387,9 +1420,9 @@
+ result = cm8.getRGB(pixBuf[i]);
+ } else {
+ result = 0xFF000000 |
+- (pixBuf[i * 4 + 1] & 0xFF) << 16 |
+- (pixBuf[i * 4 + 2] & 0xFF) << 8 |
+- (pixBuf[i * 4 + 3] & 0xFF);
++ (pixBuf[i * 4 + 2] & 0xFF) << 16 |
++ (pixBuf[i * 4 + 1] & 0xFF) << 8 |
++ (pixBuf[i * 4 + 0] & 0xFF);
+ }
+ } else {
+ result = 0; // Transparent pixel
+@@ -1403,9 +1436,9 @@
+ result = cm8.getRGB(pixBuf[i]);
+ } else {
+ result = 0xFF000000 |
+- (pixBuf[i * 4 + 1] & 0xFF) << 16 |
+- (pixBuf[i * 4 + 2] & 0xFF) << 8 |
+- (pixBuf[i * 4 + 3] & 0xFF);
++ (pixBuf[i * 4 + 2] & 0xFF) << 16 |
++ (pixBuf[i * 4 + 1] & 0xFF) << 8 |
++ (pixBuf[i * 4 + 0] & 0xFF);
+ }
+ } else {
+ result = 0; // Transparent pixel
+diff -Naur vnc_javasrc.orig/VncViewer.java vnc_javasrc/VncViewer.java
+--- vnc_javasrc.orig/VncViewer.java 2004-03-04 08:34:25.000000000 -0500
++++ vnc_javasrc/VncViewer.java 2010-03-27 17:57:04.000000000 -0400
+@@ -29,6 +29,7 @@
+ import java.awt.event.*;
+ import java.io.*;
+ import java.net.*;
++import java.util.*;
+
+ public class VncViewer extends java.applet.Applet
+ implements java.lang.Runnable, WindowListener {
+@@ -80,7 +81,7 @@
+ // Variables read from parameter values.
+ String socketFactory;
+ String host;
+- int port;
++ int port, vncserverport;
+ boolean showControls;
+ boolean offerRelogin;
+ boolean showOfflineDesktop;
+@@ -88,6 +89,24 @@
+ int deferCursorUpdates;
+ int deferUpdateRequests;
+
++ boolean disableSSL;
++ boolean GET;
++ String CONNECT;
++ String urlPrefix;
++ String httpsPort;
++ String oneTimeKey;
++ String serverCert;
++ String proxyHost;
++ String proxyPort;
++ boolean forceProxy;
++ boolean ignoreProxy;
++ boolean trustAllVncCerts;
++ boolean trustUrlVncCert;
++ boolean debugCerts;
++ boolean debugKeyboard;
++ boolean mapF5_to_atsign;
++ boolean forbid_Ctrl_Alt;
++
+ // Reference to this applet for inter-applet communication.
+ public static java.applet.Applet refApplet;
+
+@@ -282,10 +301,24 @@
+ validate();
+ }
+
+- while (!tryAuthenticate()) {
+- authenticator.retry();
+- authenticatorUnixLogin.retry();
+- }
++ if (false) {
++ /* a bug on retries: 'Error: bad position: 8' sun.awt.X11.XTextFieldPeer.setCaretPosition(XTextFieldPeer.java:215) */
++ while (!tryAuthenticate()) {
++ authenticator.retry();
++ authenticatorUnixLogin.retry();
++ }
++ } else {
++ /* just try once and not forever... */
++ if (!tryAuthenticate()) {
++ showConnectionStatus("Authentication Failed.");
++ showMessage("Authentication Failed.");
++ if (!offerRelogin) {
++ fatalError("auth failed.");
++ }
++ } else {
++ //showConnectionStatus("Authentication OK.");
++ }
++ }
+ }
+
+
+@@ -428,7 +461,10 @@
+ gbc.ipadx = 100;
+ gbc.ipady = 50;
+ gridbag.setConstraints(authPanel, gbc);
++ try {
+ vncContainer.add(authPanel);
++ } catch (Exception e) {
++ }
+
+ if (inSeparateFrame) {
+ vncFrame.pack();
+@@ -590,9 +626,28 @@
+ fatalError("HOST parameter not specified");
+ }
+ }
++ Date d = new Date();
++ System.out.println("-\nSSL VNC Java Applet starting. " + d);
+
+- String str = readParameter("PORT", true);
+- port = Integer.parseInt(str);
++ port = 0;
++ String str = readParameter("PORT", false);
++ if (str != null) {
++ port = Integer.parseInt(str);
++ }
++ // When there is a proxy VNCSERVERPORT may be inaccessible (inside firewall).
++ vncserverport = 0;
++ str = readParameter("VNCSERVERPORT", false);
++ if (str != null) {
++ vncserverport = Integer.parseInt(str);
++ }
++ if (port == 0 && vncserverport == 0) {
++ fatalError("Neither PORT nor VNCSERVERPORT parameters specified");
++ }
++ if (port == 0) {
++ // Nevertheless, fall back to vncserverport if we have to.
++ System.out.println("using vncserverport: '" + vncserverport + "' for PORT.");
++ port = vncserverport;
++ }
+
+ if (inAnApplet) {
+ str = readParameter("Open New Window", false);
+@@ -626,6 +681,121 @@
+
+ // SocketFactory.
+ socketFactory = readParameter("SocketFactory", false);
++
++ // SSL
++ disableSSL = false;
++ str = readParameter("DisableSSL", false);
++ if (str != null && str.equalsIgnoreCase("Yes"))
++ disableSSL = true;
++
++ httpsPort = readParameter("httpsPort", false);
++
++ // Extra GET, CONNECT string:
++ CONNECT = readParameter("CONNECT", false);
++ if (CONNECT != null) {
++ CONNECT = CONNECT.replaceAll(" ", ":");
++ }
++
++ GET = false;
++ str = readParameter("GET", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ GET = true;
++ }
++ if (str != null && str.equalsIgnoreCase("1")) {
++ GET = true;
++ }
++
++ urlPrefix = readParameter("urlPrefix", false);
++ if (urlPrefix != null) {
++ urlPrefix = urlPrefix.replaceAll("%2F", "/");
++ urlPrefix = urlPrefix.replaceAll("%2f", "/");
++ urlPrefix = urlPrefix.replaceAll("_2F_", "/");
++ if (urlPrefix.indexOf("/") != 0) {
++ urlPrefix = "/" + urlPrefix;
++ }
++ } else {
++ urlPrefix = "";
++ }
++ System.out.println("urlPrefix: '" + urlPrefix + "'");
++
++ oneTimeKey = readParameter("oneTimeKey", false);
++ if (oneTimeKey != null) {
++ System.out.println("oneTimeKey is set.");
++ }
++
++ serverCert = readParameter("serverCert", false);
++ if (serverCert != null) {
++ System.out.println("serverCert is set.");
++ }
++
++ forceProxy = false;
++ proxyHost = null;
++ proxyPort = null;
++ str = readParameter("forceProxy", false);
++ if (str != null) {
++ if (str.equalsIgnoreCase("Yes")) {
++ forceProxy = true;
++ } else if (str.equalsIgnoreCase("No")) {
++ forceProxy = false;
++ } else {
++ forceProxy = true;
++ String[] pieces = str.split(" ");
++ proxyHost = new String(pieces[0]);
++ if (pieces.length >= 2) {
++ proxyPort = new String(pieces[1]);
++ } else {
++ proxyPort = new String("8080");
++ }
++ }
++ }
++ str = readParameter("proxyHost", false);
++ if (str != null) {
++ proxyHost = new String(str);
++ }
++ str = readParameter("proxyPort", false);
++ if (str != null) {
++ proxyPort = new String(str);
++ }
++ if (proxyHost != null && proxyPort == null) {
++ proxyPort = new String("8080");
++ }
++
++ ignoreProxy = false;
++ str = readParameter("ignoreProxy", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ ignoreProxy = true;
++ }
++
++ trustAllVncCerts = false;
++ str = readParameter("trustAllVncCerts", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ trustAllVncCerts = true;
++ }
++ trustUrlVncCert = false;
++ str = readParameter("trustUrlVncCert", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ trustUrlVncCert = true;
++ }
++ debugCerts = false;
++ str = readParameter("debugCerts", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ debugCerts = true;
++ }
++ debugKeyboard = false;
++ str = readParameter("debugKeyboard", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ debugKeyboard = true;
++ }
++ mapF5_to_atsign = false;
++ str = readParameter("mapF5_to_atsign", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ mapF5_to_atsign = true;
++ }
++ forbid_Ctrl_Alt = false;
++ str = readParameter("forbid_Ctrl_Alt", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ forbid_Ctrl_Alt = true;
++ }
+ }
+
+ public String readParameter(String name, boolean required) {
diff --git a/webclients/java-applet/ssl/ultra.vnc b/webclients/java-applet/ssl/ultra.vnc
new file mode 100644
index 0000000..3c57445
--- /dev/null
+++ b/webclients/java-applet/ssl/ultra.vnc
@@ -0,0 +1,28 @@
+<!--
+ index.vnc - default HTML page for TightVNC Java viewer applet, to be
+ used with Xvnc. On any file ending in .vnc, the HTTP server embedded in
+ Xvnc will substitute the following variables when preceded by a dollar:
+ USER, DESKTOP, DISPLAY, APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT,
+ PARAMS. Use two dollar signs ($$) to get a dollar sign in the generated
+ HTML page.
+
+ NOTE: the $PARAMS variable is not supported by the standard VNC, so
+ make sure you have TightVNC on the server side, if you're using this
+ variable.
+-->
+
+<HTML>
+<TITLE>
+$USER's $DESKTOP desktop ($DISPLAY)
+</TITLE>
+<APPLET CODE=VncViewer.class ARCHIVE=UltraViewerSSL.jar
+ WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
+<param name=PORT value=$PORT>
+<param name="Open New Window" value=yes>
+<param name="ignoreMSLogonCheck" value=yes>
+<param name="delayAuthPanel" value=yes>
+$PARAMS
+</APPLET>
+<BR>
+<A href="http://www.karlrunge.com/x11vnc">x11vnc site</A>
+</HTML>
diff --git a/webclients/java-applet/ssl/ultraproxy.vnc b/webclients/java-applet/ssl/ultraproxy.vnc
new file mode 100644
index 0000000..fd842c4
--- /dev/null
+++ b/webclients/java-applet/ssl/ultraproxy.vnc
@@ -0,0 +1,28 @@
+<!--
+ index.vnc - default HTML page for TightVNC Java viewer applet, to be
+ used with Xvnc. On any file ending in .vnc, the HTTP server embedded in
+ Xvnc will substitute the following variables when preceded by a dollar:
+ USER, DESKTOP, DISPLAY, APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT,
+ PARAMS. Use two dollar signs ($$) to get a dollar sign in the generated
+ HTML page.
+
+ NOTE: the $PARAMS variable is not supported by the standard VNC, so
+ make sure you have TightVNC on the server side, if you're using this
+ variable.
+-->
+
+<HTML>
+<TITLE>
+$USER's $DESKTOP desktop ($DISPLAY)
+</TITLE>
+<APPLET CODE=VncViewer.class ARCHIVE=SignedUltraViewerSSL.jar
+ WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
+<param name="Open New Window" value=yes>
+<param name="ignoreMSLogonCheck" value=yes>
+<param name="delayAuthPanel" value=yes>
+<param name=VNCSERVERPORT value=$PORT>
+$PARAMS
+</APPLET>
+<BR>
+<A href="http://www.karlrunge.com/x11vnc">x11vnc site</A>
+</HTML>
diff --git a/webclients/java-applet/ssl/ultrasigned.vnc b/webclients/java-applet/ssl/ultrasigned.vnc
new file mode 100644
index 0000000..a711655
--- /dev/null
+++ b/webclients/java-applet/ssl/ultrasigned.vnc
@@ -0,0 +1,28 @@
+<!--
+ index.vnc - default HTML page for TightVNC Java viewer applet, to be
+ used with Xvnc. On any file ending in .vnc, the HTTP server embedded in
+ Xvnc will substitute the following variables when preceded by a dollar:
+ USER, DESKTOP, DISPLAY, APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT,
+ PARAMS. Use two dollar signs ($$) to get a dollar sign in the generated
+ HTML page.
+
+ NOTE: the $PARAMS variable is not supported by the standard VNC, so
+ make sure you have TightVNC on the server side, if you're using this
+ variable.
+-->
+
+<HTML>
+<TITLE>
+$USER's $DESKTOP desktop ($DISPLAY)
+</TITLE>
+<APPLET CODE=VncViewer.class ARCHIVE=SignedUltraViewerSSL.jar
+ WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
+<param name=PORT value=$PORT>
+<param name="Open New Window" value=yes>
+<param name="ignoreMSLogonCheck" value=yes>
+<param name="delayAuthPanel" value=yes>
+$PARAMS
+</APPLET>
+<BR>
+<A href="http://www.karlrunge.com/x11vnc">x11vnc site</A>
+</HTML>
diff --git a/webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch b/webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch
new file mode 100644
index 0000000..3309860
--- /dev/null
+++ b/webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch
@@ -0,0 +1,5494 @@
+diff -Naur JavaViewer.orig/ButtonPanel.java JavaViewer/ButtonPanel.java
+--- JavaViewer.orig/ButtonPanel.java 2004-12-12 20:51:02.000000000 -0500
++++ JavaViewer/ButtonPanel.java 2007-05-31 15:40:45.000000000 -0400
+@@ -43,30 +43,36 @@
+ viewer = v;
+
+ setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
+- disconnectButton = new Button("Disconnect");
++ if (v.ftpOnly) {
++ disconnectButton = new Button("Quit");
++ } else {
++ disconnectButton = new Button("Close");
++ }
+ disconnectButton.setEnabled(false);
+ add(disconnectButton);
+ disconnectButton.addActionListener(this);
+- optionsButton = new Button("Options");
+- add(optionsButton);
+- optionsButton.addActionListener(this);
+- clipboardButton = new Button("Clipboard");
+- clipboardButton.setEnabled(false);
+- add(clipboardButton);
+- clipboardButton.addActionListener(this);
+- if (viewer.rec != null) {
+- recordButton = new Button("Record");
+- add(recordButton);
+- recordButton.addActionListener(this);
+- }
+- ctrlAltDelButton = new Button("Send Ctrl-Alt-Del");
+- ctrlAltDelButton.setEnabled(false);
+- add(ctrlAltDelButton);
+- ctrlAltDelButton.addActionListener(this);
+- refreshButton = new Button("Refresh");
+- refreshButton.setEnabled(false);
+- add(refreshButton);
+- refreshButton.addActionListener(this);
++ if (!v.ftpOnly) {
++ optionsButton = new Button("Options");
++ add(optionsButton);
++ optionsButton.addActionListener(this);
++ clipboardButton = new Button("Clipboard");
++ clipboardButton.setEnabled(false);
++ add(clipboardButton);
++ clipboardButton.addActionListener(this);
++ if (viewer.rec != null) {
++ recordButton = new Button("Record");
++ add(recordButton);
++ recordButton.addActionListener(this);
++ }
++ ctrlAltDelButton = new Button("Send Ctrl-Alt-Del");
++ ctrlAltDelButton.setEnabled(false);
++ add(ctrlAltDelButton);
++ ctrlAltDelButton.addActionListener(this);
++ refreshButton = new Button("Refresh");
++ refreshButton.setEnabled(false);
++ add(refreshButton);
++ refreshButton.addActionListener(this);
++ }
+ ftpButton = new Button("File Transfer");
+ ftpButton.setEnabled(false);
+ add(ftpButton);
+@@ -79,9 +85,10 @@
+
+ public void enableButtons() {
+ disconnectButton.setEnabled(true);
++ ftpButton.setEnabled(true);
++ if (viewer.ftpOnly) {return;}
+ clipboardButton.setEnabled(true);
+ refreshButton.setEnabled(true);
+- ftpButton.setEnabled(true);
+ }
+
+ //
+@@ -89,6 +96,9 @@
+ //
+
+ public void disableButtonsOnDisconnect() {
++ ftpButton.setEnabled(false);
++ if (viewer.ftpOnly) {return;}
++
+ remove(disconnectButton);
+ disconnectButton = new Button("Hide desktop");
+ disconnectButton.setEnabled(true);
+@@ -99,7 +109,6 @@
+ clipboardButton.setEnabled(false);
+ ctrlAltDelButton.setEnabled(false);
+ refreshButton.setEnabled(false);
+- ftpButton.setEnabled(false);
+
+ validate();
+ }
+@@ -110,6 +119,7 @@
+ //
+
+ public void enableRemoteAccessControls(boolean enable) {
++ if (viewer.ftpOnly) {return;}
+ ctrlAltDelButton.setEnabled(enable);
+ }
+
+@@ -163,9 +173,19 @@
+ }
+ else if (evt.getSource() == ftpButton)
+ {
+- viewer.ftp.setVisible(!viewer.ftp.isVisible());
++// begin runge/x11vnc
++ if (viewer.ftpOnly) {
++ viewer.vncFrame.setVisible(false);
++ }
++ viewer.ftp.setSavedLocations();
++ if (viewer.ftp.isVisible()) {
++ viewer.ftp.doClose();
++ } else {
++ viewer.ftp.doOpen();
++ }
++// end runge/x11vnc
+ viewer.rfb.readServerDriveList();
+-
++
+ }
+ }
+ }
+diff -Naur JavaViewer.orig/FTPFrame.java JavaViewer/FTPFrame.java
+--- JavaViewer.orig/FTPFrame.java 2005-03-15 23:53:14.000000000 -0500
++++ JavaViewer/FTPFrame.java 2009-01-13 09:48:30.000000000 -0500
+@@ -24,8 +24,17 @@
+ import java.io.*;
+ import java.util.ArrayList;
+ import java.util.Vector;
++import java.util.Date;
+ import javax.swing.*;
+
++import java.nio.ByteBuffer;
++import java.nio.CharBuffer;
++import java.nio.charset.*;
++
++// begin runge/x11vnc
++import java.util.Arrays;
++// end runge/x11vnc
++
+
+ /*
+ * Created on Feb 25, 2004
+@@ -74,12 +83,31 @@
+ public javax.swing.JTextField connectionStatus = null;
+ public boolean updateDriveList;
+ private Vector remoteList = null;
++ private Vector remoteListInfo = null;
+ private Vector localList = null;
++ private Vector localListInfo = null;
+ private File currentLocalDirectory = null; // Holds the current local Directory
+ private File currentRemoteDirectory = null; // Holds the current remote Directory
+ private File localSelection = null; // Holds the currently selected local file
+ private String remoteSelection = null; // Holds the currently selected remote file
+ public String selectedTable = null;
++
++// begin runge/x11vnc
++ private javax.swing.JButton viewButton = null;
++ private javax.swing.JButton refreshButton = null;
++ public File saveLocalDirectory = null;
++ public long saveLocalDirectoryTime = 0;
++ public int saveLocalDirectoryCount = 0;
++ public String saveRemoteDirectory = null;
++ public long saveRemoteDirectoryTime = 0;
++ public int saveRemoteDirectoryCount = 0;
++ private boolean localCurrentIsDir = true;
++ private int lastRemoteIndex = -1;
++ private int lastLocalIndex = -1;
++ private boolean doingShortcutDir = false;
++ private boolean gotShortcutDir = false;
++ private boolean ignore_events = false;
++// end runge/x11vnc
+
+ // sf@2004 - Separate directories and files for better lisibility
+ private ArrayList DirsList;
+@@ -125,11 +153,61 @@
+
+ void refreshRemoteLocation()
+ {
++
++//System.out.println("refreshRemoteLocation1");
+ remoteList.clear();
++ remoteListInfo.clear();
+ remoteFileTable.setListData(remoteList);
++System.out.println("refreshRemoteLocation '" + remoteLocation.getText() + "'"); // runge/x11vnc
+ viewer.rfb.readServerDirectory(remoteLocation.getText());
+ }
+
++// begin runge/x11vnc
++ public void setSavedLocations() {
++ saveLocalDirectory = currentLocalDirectory;
++ saveLocalDirectoryTime = System.currentTimeMillis();
++ saveLocalDirectoryCount = 0;
++
++ if (remoteLocation != null) {
++ saveRemoteDirectory = remoteLocation.getText();
++System.out.println("RemoteSave '" + saveRemoteDirectory + "'");
++ }
++ saveRemoteDirectoryTime = System.currentTimeMillis();
++ saveRemoteDirectoryCount = 0;
++ }
++
++ private File saveLocalHack(File dir) {
++ saveLocalDirectoryCount++;
++//System.out.println("L " + saveLocalDirectoryCount + " dt: " + (System.currentTimeMillis() - saveLocalDirectoryTime) + " - " + saveLocalDirectory);
++ if (System.currentTimeMillis() > saveLocalDirectoryTime + 2000 || saveLocalDirectoryCount > 2) {
++ saveLocalDirectory = null;
++ }
++ if (saveLocalDirectory != null) {
++ currentLocalDirectory = saveLocalDirectory;
++ localLocation.setText(saveLocalDirectory.toString());
++ return saveLocalDirectory;
++ } else {
++ return dir;
++ }
++ }
++
++ private String saveRemoteHack(String indrive) {
++ saveRemoteDirectoryCount++;
++//System.out.println("R " + saveRemoteDirectoryCount + " - " + saveRemoteDirectory);
++ if (saveRemoteDirectory != null && saveRemoteDirectoryCount > 1) {
++ saveRemoteDirectory = null;
++ }
++ if (saveRemoteDirectory != null) {
++ if (! saveRemoteDirectory.equals("")) {
++System.out.println("saveRemoteHack setText + refreshRemoteLocation '" + saveRemoteDirectory + "'");
++ return saveRemoteDirectory;
++ }
++ }
++ return indrive;
++ }
++// end runge/x11vnc
++
++
+ /*
+ * Prints the list of drives on the remote directory and returns a String[].
+ * str takes as string like A:fC:lD:lE:lF:lG:cH:c
+@@ -143,6 +221,9 @@
+ int size = str.length();
+ String driveType = null;
+ String[] drive = new String[str.length() / 3];
++ int idx = 0, C_drive = -1, O_drive = -1;
++
++System.out.println("ComboBox: Str '" + str + "'");
+
+ // Loop through the string to create a String[]
+ for (int i = 0; i < size; i = i + 3) {
+@@ -150,26 +231,68 @@
+ driveType = str.substring(i + 2, i + 3);
+ if (driveType.compareTo("f") == 0)
+ drive[i / 3] += "\\ Floppy";
+- if (driveType.compareTo("l") == 0)
++ if (driveType.compareTo("l") == 0) {
+ drive[i / 3] += "\\ Local Disk";
++ if (drive[i/3].substring(0,1).toUpperCase().equals("C")) {
++ C_drive = idx;
++ } else if (O_drive < 0) {
++ O_drive = idx;
++ }
++ }
+ if (driveType.compareTo("c") == 0)
+ drive[i / 3] += "\\ CD-ROM";
+ if (driveType.compareTo("n") == 0)
+ drive[i / 3] += "\\ Network";
+
+ remoteDrivesComboBox.addItem(drive[i / 3]);
++System.out.println("ComboBox: Add " + idx + " '" + drive[i/3] + "'");
++ idx++;
++ }
++
++ // runge
++ if (viewer.ftpDropDown != null) {
++ String[] dd = viewer.ftpDropDown.split("\\.");
++ for (int i=0; i < dd.length; i++) {
++ if (!dd[i].equals("")) {
++ String s = dd[i];
++ if (s.startsWith("TOP_")) {
++ s = s.substring(4);
++ remoteDrivesComboBox.insertItemAt(" [" + s + "]", 0);
++ } else {
++ remoteDrivesComboBox.addItem(" [" + s + "]");
++ }
++ }
++ }
++ } else {
++ remoteDrivesComboBox.addItem(" [My Documents]");
++ remoteDrivesComboBox.addItem(" [Desktop]");
++ remoteDrivesComboBox.addItem(" [Home]");
+ }
++
+ //sf@ - Select Drive C:as default if possible
+ boolean bFound = false;
+- for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++)
+- {
+- if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C"))
+- {
+- remoteDrivesComboBox.setSelectedIndex(i);
++
++ if (false) {
++ for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++) {
++ if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C")) {
++ remoteDrivesComboBox.setSelectedIndex(i);
++ bFound = true;
++ }
++ }
++ } else {
++ if (C_drive >= 0) {
++ remoteDrivesComboBox.setSelectedIndex(C_drive);
++ bFound = true;
++System.out.println("ComboBox: C_drive index: " + C_drive);
++ } else if (O_drive >= 0) {
++ remoteDrivesComboBox.setSelectedIndex(O_drive);
+ bFound = true;
++System.out.println("ComboBox: Other_drive index: " + O_drive);
+ }
+ }
++
+ if (!bFound) remoteDrivesComboBox.setSelectedIndex(0);
++
+ updateDriveList = false;
+ return drive;
+ }
+@@ -185,6 +308,8 @@
+ stopButton.setVisible(true);
+ stopButton.setEnabled(true);
+ receiveButton.setEnabled(false);
++ viewButton.setEnabled(false); // runge/x11vnc
++ refreshButton.setEnabled(false);
+ remoteTopButton.setEnabled(false);
+ sendButton.setEnabled(false);
+ remoteFileTable.setEnabled(false);
+@@ -207,6 +332,8 @@
+ stopButton.setVisible(false);
+ stopButton.setEnabled(false);
+ receiveButton.setEnabled(true);
++ viewButton.setEnabled(true); // runge/x11vnc
++ refreshButton.setEnabled(true);
+ remoteTopButton.setEnabled(true);
+ sendButton.setEnabled(true);
+ remoteFileTable.setEnabled(true);
+@@ -221,10 +348,11 @@
+ /*
+ * Print Directory prints out all the contents of a directory
+ */
+- void printDirectory(ArrayList a) {
++ void printDirectory(ArrayList a, ArrayList b) {
+
+ for (int i = 0; i < a.size(); i++) {
+ remoteList.addElement(a.get(i));
++ remoteListInfo.addElement(b.get(i));
+ }
+ remoteFileTable.setListData(remoteList);
+ }
+@@ -235,10 +363,12 @@
+ * @return void
+ */
+ private void initialize() {
++ ignore_events = true;
+ this.setSize(794, 500);
+ this.setContentPane(getJContentPane());
++ ignore_events = false;
+ updateDriveList = true;
+- }
++ }
+ /**
+ * This method initializes jContentPane. This is the main content pane
+ *
+@@ -253,6 +383,33 @@
+ jContentPane.add(getRemotePanel(), java.awt.BorderLayout.EAST);
+ jContentPane.add(getLocalPanel(), java.awt.BorderLayout.WEST);
+ jContentPane.add(getButtonPanel(), java.awt.BorderLayout.CENTER);
++
++ KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
++ AbstractAction escapeAction = new AbstractAction() {
++ public void actionPerformed(ActionEvent actionEvent) {
++ System.out.println("Escape Pressed");
++ if (viewer.ftpOnly) {
++ System.out.println("exiting...");
++ System.exit(0);
++ } else {
++ doClose();
++ }
++ }
++ };
++ jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction");
++ jContentPane.getInputMap().put(stroke, "escapeAction");
++ jContentPane.getActionMap().put("escapeAction", escapeAction);
++
++ stroke = KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK);
++ AbstractAction resetAction = new AbstractAction() {
++ public void actionPerformed(ActionEvent actionEvent) {
++ System.out.println("Ctrl-R Pressed");
++ doReset();
++ }
++ };
++ jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "resetAction");
++ jContentPane.getInputMap().put(stroke, "resetAction");
++ jContentPane.getActionMap().put("resetAction", resetAction);
+ }
+ return jContentPane;
+ }
+@@ -270,6 +427,7 @@
+ topPanelLocal.add(getLocalMachineLabel(), java.awt.BorderLayout.CENTER);
+ topPanelLocal.add(getLocalTopButton(), java.awt.BorderLayout.EAST);
+ topPanelLocal.setBackground(java.awt.Color.lightGray);
++//System.out.println("getTopPanelLocal");
+ }
+ return topPanelLocal;
+ }
+@@ -288,6 +446,7 @@
+ topPanelRemote.add(getRemoteMachineLabel(), java.awt.BorderLayout.CENTER);
+ topPanelRemote.add(getRemoteTopButton(), java.awt.BorderLayout.EAST);
+ topPanelRemote.setBackground(java.awt.Color.lightGray);
++//System.out.println("getTopPanelRemote");
+ }
+ return topPanelRemote;
+ }
+@@ -301,6 +460,7 @@
+ if (topPanelCenter == null) {
+ topPanelCenter = new javax.swing.JPanel();
+ topPanelCenter.add(getDummyButton(), null);
++//System.out.println("getTopPanelCenter");
+ }
+ return topPanelCenter;
+ }
+@@ -328,6 +488,7 @@
+ topPanel.add(getRemoteTopButton(), null);
+ topPanel.setBackground(java.awt.Color.lightGray);
+ */
++//System.out.println("getTopPanel");
+ }
+ return topPanel;
+ }
+@@ -348,6 +509,7 @@
+ statusPanel.add(getJProgressBar(), null);
+ statusPanel.add(getConnectionStatus(), null);
+ statusPanel.setBackground(java.awt.Color.lightGray);
++//System.out.println("getStatusPanel");
+
+ }
+ return statusPanel;
+@@ -368,6 +530,7 @@
+ remotePanel.add(getRemoteScrollPane(), null);
+ remotePanel.add(getRemoteStatus(), null);
+ remotePanel.setBackground(java.awt.Color.lightGray);
++//System.out.println("getRemotePanel");
+ }
+ return remotePanel;
+ }
+@@ -390,6 +553,7 @@
+ localPanel.setComponentOrientation(
+ java.awt.ComponentOrientation.UNKNOWN);
+ localPanel.setName("localPanel");
++//System.out.println("getLocalPanel");
+ }
+ return localPanel;
+ }
+@@ -405,12 +569,15 @@
+ buttonPanel = new javax.swing.JPanel();
+ buttonPanel.setLayout(null);
+ buttonPanel.add(getReceiveButton(), null);
++ buttonPanel.add(getRefreshButton(), null); // runge/x11vnc
++ buttonPanel.add(getViewButton(), null); // runge/x11vnc
+ buttonPanel.add(getNewFolderButton(), null);
+ buttonPanel.add(getCloseButton(), null);
+ buttonPanel.add(getDeleteButton(), null);
+ buttonPanel.add(getSendButton(), null);
+ buttonPanel.add(getStopButton(), null);
+ buttonPanel.setBackground(java.awt.Color.lightGray);
++//System.out.println("getButtonPanel");
+ }
+ return buttonPanel;
+ }
+@@ -422,10 +589,11 @@
+ private javax.swing.JButton getSendButton() {
+ if (sendButton == null) {
+ sendButton = new javax.swing.JButton();
+- sendButton.setBounds(20, 30, 97, 25);
++ sendButton.setBounds(15, 30, 107, 25); // runge/x11vnc
+ sendButton.setText("Send >>");
+ sendButton.setName("sendButton");
+ sendButton.addActionListener(this);
++//System.out.println("getSendButton");
+
+ }
+ return sendButton;
+@@ -438,7 +606,7 @@
+ private javax.swing.JButton getReceiveButton() {
+ if (receiveButton == null) {
+ receiveButton = new javax.swing.JButton();
+- receiveButton.setBounds(20, 60, 97, 25);
++ receiveButton.setBounds(15, 60, 107, 25); // runge/x11vnc
+ receiveButton.setText("<< Receive");
+ receiveButton.setName("receiveButton");
+ receiveButton.addActionListener(this);
+@@ -453,7 +621,7 @@
+ private javax.swing.JButton getDeleteButton() {
+ if (deleteButton == null) {
+ deleteButton = new javax.swing.JButton();
+- deleteButton.setBounds(20, 110, 97, 25);
++ deleteButton.setBounds(15, 110, 107, 25); // runge/x11vnc
+ deleteButton.setText("Delete File");
+ deleteButton.setName("deleteButton");
+ deleteButton.addActionListener(this);
+@@ -468,7 +636,7 @@
+ private javax.swing.JButton getNewFolderButton() {
+ if (newFolderButton == null) {
+ newFolderButton = new javax.swing.JButton();
+- newFolderButton.setBounds(20, 140, 97, 25);
++ newFolderButton.setBounds(15, 140, 107, 25); // runge/x11vnc
+ newFolderButton.setText("New Folder");
+ newFolderButton.setName("newFolderButton");
+ newFolderButton.addActionListener(this);
+@@ -476,6 +644,39 @@
+ return newFolderButton;
+ }
+
++// begin runge/x11vnc
++ /**
++ * This method initializes refreshButton
++ *
++ * @return javax.swing.JButton
++ */
++ private javax.swing.JButton getRefreshButton() {
++ if (refreshButton == null) {
++ refreshButton = new javax.swing.JButton();
++ refreshButton.setBounds(15, 170, 107, 25);
++ refreshButton.setText("Refresh");
++ refreshButton.setName("refreshButton");
++ refreshButton.addActionListener(this);
++ }
++ return refreshButton;
++ }
++ /**
++ * This method initializes viewButton
++ *
++ * @return javax.swing.JButton
++ */
++ private javax.swing.JButton getViewButton() {
++ if (viewButton == null) {
++ viewButton = new javax.swing.JButton();
++ viewButton.setBounds(15, 200, 107, 25);
++ viewButton.setText("View File");
++ viewButton.setName("viewButton");
++ viewButton.addActionListener(this);
++ }
++ return viewButton;
++ }
++// end runge/x11vnc
++
+ /**
+ * This method initializes stopButton
+ *
+@@ -486,7 +687,7 @@
+ if (stopButton == null)
+ {
+ stopButton = new javax.swing.JButton();
+- stopButton.setBounds(20, 200, 97, 25);
++ stopButton.setBounds(15, 230, 107, 25); // runge/x11vnc
+ stopButton.setText("Stop");
+ stopButton.setName("stopButton");
+ stopButton.addActionListener(this);
+@@ -503,8 +704,12 @@
+ private javax.swing.JButton getCloseButton() {
+ if (closeButton == null) {
+ closeButton = new javax.swing.JButton();
+- closeButton.setBounds(20, 325, 97, 25);
+- closeButton.setText("Close");
++ closeButton.setBounds(15, 325, 107, 25); // runge/x11vnc
++ if (viewer.ftpOnly) {
++ closeButton.setText("Quit");
++ } else {
++ closeButton.setText("Close");
++ }
+ closeButton.setName("closeButton");
+ closeButton.addActionListener(this);
+ }
+@@ -551,6 +756,7 @@
+ //Select the second entry (e.g. C:\)
+ // localDrivesComboBox.setSelectedIndex(1);
+ localDrivesComboBox.addActionListener(this);
++//System.out.println("getLocalDrivesComboBox");
+ }
+ updateDriveList = false;
+ return localDrivesComboBox;
+@@ -567,6 +773,7 @@
+ remoteDrivesComboBox.setFont(
+ new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
+ remoteDrivesComboBox.addActionListener(this);
++//System.out.println("getRemoteDrivesComboBox");
+
+ }
+ return remoteDrivesComboBox;
+@@ -587,6 +794,7 @@
+ localMachineLabel.setFont(
+ new java.awt.Font("Dialog", java.awt.Font.BOLD, 11));
+ localMachineLabel.setEditable(false);
++//System.out.println("getLocalMachineLabel");
+ }
+ return localMachineLabel;
+ }
+@@ -622,6 +830,7 @@
+ localTopButton.setFont(
+ new java.awt.Font("Dialog", java.awt.Font.BOLD, 10));
+ localTopButton.addActionListener(this);
++//System.out.println("getLocalTopButton");
+ }
+ return localTopButton;
+ }
+@@ -638,6 +847,7 @@
+ remoteTopButton.setFont(
+ new java.awt.Font("Dialog", java.awt.Font.BOLD, 10));
+ remoteTopButton.addActionListener(this);
++//System.out.println("getRemoteTopButton");
+ }
+ return remoteTopButton;
+ }
+@@ -650,9 +860,24 @@
+ private javax.swing.JList getLocalFileTable() {
+ if (localFileTable == null) {
+ localList = new Vector(0);
++ localListInfo = new Vector(0);
+ localFileTable = new JList(localList);
++ MouseMotionListener mlisten = new MouseMotionAdapter() {
++ public void mouseMoved(MouseEvent e) {
++ int index = localFileTable.locationToIndex(e.getPoint());
++ if (index == lastLocalIndex) {
++ return;
++ } else if (index < 0) {
++ return;
++ }
++ lastLocalIndex = index;
++ connectionStatus.setText((String) localListInfo.get(index));
++ }
++ };
+ localFileTable.addMouseListener(this);
++ localFileTable.addMouseMotionListener(mlisten);
+ localFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
++//System.out.println("getLocalFileTable");
+ }
+ return localFileTable;
+ }
+@@ -669,6 +894,7 @@
+ localScrollPane.setFont(
+ new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
+ localScrollPane.setName("localFileList");
++//System.out.println("getLocalScrollPane");
+ }
+ return localScrollPane;
+ }
+@@ -680,10 +906,25 @@
+ private javax.swing.JList getRemoteFileTable() {
+ if (remoteFileTable == null) {
+ remoteList = new Vector(0);
++ remoteListInfo = new Vector(0);
+ remoteFileTable = new JList(remoteList);
++ MouseMotionListener mlisten = new MouseMotionAdapter() {
++ public void mouseMoved(MouseEvent e) {
++ int index = remoteFileTable.locationToIndex(e.getPoint());
++ if (index == lastRemoteIndex) {
++ return;
++ } else if (index < 0) {
++ return;
++ }
++ lastRemoteIndex = index;
++ connectionStatus.setText((String) remoteListInfo.get(index));
++ }
++ };
+ remoteFileTable.addMouseListener(this);
++ remoteFileTable.addMouseMotionListener(mlisten);
+ remoteFileTable.setSelectedValue("C:\\", false);
+ remoteFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
++//System.out.println("getRemoteFileTable");
+
+ }
+ return remoteFileTable;
+@@ -698,6 +939,7 @@
+ remoteScrollPane = new javax.swing.JScrollPane();
+ remoteScrollPane.setViewportView(getRemoteFileTable());
+ remoteScrollPane.setPreferredSize(new java.awt.Dimension(325, 418));
++//System.out.println("getRemoteScrollPane");
+ }
+ return remoteScrollPane;
+ }
+@@ -716,6 +958,7 @@
+ remoteLocation.setBackground(new Color(255,255,238));
+ remoteLocation.setFont(
+ new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
++//System.out.println("getRemoteLocation");
+ }
+ return remoteLocation;
+ }
+@@ -732,6 +975,7 @@
+ localLocation.setBackground( new Color(255,255,238));
+ localLocation.setFont(
+ new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
++//System.out.println("getLocalLocation");
+ }
+ return localLocation;
+ }
+@@ -748,6 +992,7 @@
+ localStatus.setFont(
+ new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
+ localStatus.setEditable(false);
++//System.out.println("getLocalStatus");
+ }
+ return localStatus;
+ }
+@@ -764,6 +1009,7 @@
+ remoteStatus.setFont(
+ new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
+ remoteStatus.setEditable(false);
++//System.out.println("getRemoteStatus");
+ }
+ return remoteStatus;
+ }
+@@ -777,9 +1023,10 @@
+ historyComboBox = new javax.swing.JComboBox();
+ historyComboBox.setFont(
+ new java.awt.Font("Dialog", java.awt.Font.BOLD, 10));
+- historyComboBox.insertItemAt(new String("Pulldown to view history ..."),0);
++ historyComboBox.insertItemAt(new String("Pulldown to view history; Press Escape to Close/Quit; Press Ctrl-R to Reset Panel."),0);
+ historyComboBox.setSelectedIndex(0);
+ historyComboBox.addActionListener(this);
++//System.out.println("getHistoryComboBox");
+ }
+ return historyComboBox;
+ }
+@@ -791,6 +1038,7 @@
+ private javax.swing.JProgressBar getJProgressBar() {
+ if (jProgressBar == null) {
+ jProgressBar = new javax.swing.JProgressBar();
++//System.out.println("getJProgressBar");
+ }
+ return jProgressBar;
+ }
+@@ -806,6 +1054,7 @@
+ connectionStatus.setBackground(java.awt.Color.lightGray);
+ connectionStatus.setFont(
+ new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
++//System.out.println("getConnectionStatus");
+ }
+ connectionStatus.setEditable(false);
+ return connectionStatus;
+@@ -815,7 +1064,12 @@
+ * Implements Action listener.
+ */
+ public void actionPerformed(ActionEvent evt) {
+- System.out.println(evt.getSource());
++// System.out.println(evt.getSource());
++
++ if (ignore_events) {
++ System.out.println("ignore_events: " + evt.getSource());
++ return;
++ }
+
+ if (evt.getSource() == closeButton)
+ { // Close Button
+@@ -829,15 +1083,27 @@
+ {
+ doReceive();
+ }
++// begin runge/x11vnc
++ else if (evt.getSource() == viewButton)
++ {
++ doView();
++ }
++// end runge/x11vnc
+ else if (evt.getSource() == localDrivesComboBox)
+ {
+ changeLocalDrive();
+ }
+ else if (evt.getSource() == remoteDrivesComboBox)
+ {
++//System.out.println("remoteDrivesComboBox"); // runge/x11vnc
+ changeRemoteDrive();
+- remoteList.clear();
+- remoteFileTable.setListData(remoteList);
++
++ // are these really needed? changeRemoteDrive() does them at the end.
++ if (false) {
++ remoteList.clear();
++ remoteListInfo.clear();
++ remoteFileTable.setListData(remoteList);
++ }
+ }
+ else if (evt.getSource() == localTopButton)
+ {
+@@ -845,12 +1111,17 @@
+ }
+ else if (evt.getSource() == remoteTopButton)
+ {
++//System.out.println("remoteTopButton"); // runge/x11vnc
+ changeRemoteDrive();
+ }
+ else if(evt.getSource() == deleteButton)
+ {
+ doDelete();
+ }
++ else if(evt.getSource() == refreshButton)
++ {
++ doRefresh();
++ }
+ else if(evt.getSource()==newFolderButton)
+ {
+ doNewFolder();
+@@ -864,7 +1135,7 @@
+
+ private void doNewFolder()
+ {
+- String name = JOptionPane.showInputDialog(null,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE);
++ String name = JOptionPane.showInputDialog(jContentPane,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE);
+ if(selectedTable.equals("remote"))
+ {
+ name = remoteLocation.getText()+name;
+@@ -880,34 +1151,106 @@
+ historyComboBox.setSelectedIndex(0);
+ }
+ }
+- private void doClose()
++ public void doClose()
+ {
++ if (viewer.ftpOnly) {
++ viewer.disconnect();
++ return;
++ }
+ try {
+ this.setVisible(false);
+- viewer.rfb.writeFramebufferUpdateRequest(
+- 0,
+- 0,
+- viewer.rfb.framebufferWidth,
+- viewer.rfb.framebufferHeight,
+- true);
++ viewer.rfb.writeFramebufferUpdateRequest(0, 0, viewer.rfb.framebufferWidth,
++ viewer.rfb.framebufferHeight, true);
++
++ if (false) {
++ this.dispose();
++ jContentPane = null;
++ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
++ private void unSwing() {
++ jContentPane = null;
++ topPanel = null;
++ topPanelLocal = null;
++ topPanelRemote = null;
++ topPanelCenter = null;
++ statusPanel = null;
++ remotePanel = null;
++ localPanel = null;
++ buttonPanel = null;
++ sendButton = null;
++ receiveButton = null;
++ deleteButton = null;
++ newFolderButton = null;
++ stopButton = null;
++ closeButton = null;
++ dummyButton = null;
++ localDrivesComboBox = null;
++ remoteDrivesComboBox = null;
++ localMachineLabel = null;
++ remoteMachineLabel = null;
++ localTopButton = null;
++ remoteTopButton = null;
++ localScrollPane = null;
++ localFileTable = null;
++ remoteScrollPane = null;
++ remoteFileTable = null;
++ remoteLocation = null;
++ localLocation = null;
++ localStatus = null;
++ remoteStatus = null;
++ historyComboBox = null;
++ jProgressBar = null;
++ connectionStatus = null;
++ viewButton = null;
++ refreshButton = null;
++ }
++
++ public void doReset()
++ {
++ try {
++ this.setVisible(false);
++ this.dispose();
++ jContentPane = null;
++ try {Thread.sleep(500);} catch (InterruptedException e) {}
++ viewer.ftp_init();
++ } catch (Exception e) {
++ // TODO Auto-generated catch block
++ e.printStackTrace();
++ }
++ }
+
++ public void doOpen()
++ {
++ try {
++ this.setVisible(true);
++ if (false) {
++ this.initialize();
++ }
++ } catch (Exception e) {
++ // TODO Auto-generated catch block
++ e.printStackTrace();
++ }
++ }
+ private void doDelete()
+ {
+- System.out.println("Delete Button Pressed");
++// System.out.println("Delete Button Pressed");
+ //Call this method to delete a file at server
+ if(selectedTable.equals("remote"))
+ {
+- String sFileName = ((String) this.remoteFileTable.getSelectedValue());
++ Object selected = this.remoteFileTable.getSelectedValue();
++ if (selected == null) {
++ return;
++ }
++ String sFileName = ((String) selected);
+
+ // sf@2004 - Directory can't be deleted
+ if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
+ {
+- JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
++ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
+ return;
+ }
+
+@@ -916,7 +1259,7 @@
+ // sf@2004 - Delete prompt
+ if (remoteList.contains(sFileName))
+ {
+- int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
++ int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
+ if (r == JOptionPane.NO_OPTION)
+ return;
+ }
+@@ -926,18 +1269,22 @@
+ }
+ else
+ {
+- String sFileName = ((String) this.localFileTable.getSelectedValue());
++ Object selected = this.localFileTable.getSelectedValue();
++ if (selected == null) {
++ return;
++ }
++ String sFileName = ((String) selected);
+
+ // sf@2004 - Directory can't be deleted
+ if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
+ {
+- JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
++ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
+ return;
+ }
+ // sf@2004 - Delete prompt
+ if (localList.contains(sFileName))
+ {
+- int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
++ int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
+ if (r == JOptionPane.NO_OPTION)
+ return;
+ }
+@@ -952,21 +1299,25 @@
+
+ private void doReceive()
+ {
+- System.out.println("Received Button Pressed");
++// System.out.println("Received Button Pressed");
+
+- String sFileName = ((String) this.remoteFileTable.getSelectedValue());
++ Object selected = this.remoteFileTable.getSelectedValue();
++ if (selected == null) {
++ return;
++ }
++ String sFileName = ((String) selected);
+
+ // sf@2004 - Directory can't be transfered
+ if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
+ {
+- JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
++ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
+ return;
+ }
+
+ // sf@2004 - Overwrite prompt
+ if (localList.contains(sFileName))
+ {
+- int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
++ int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
+ if (r == JOptionPane.NO_OPTION)
+ return;
+ }
+@@ -979,23 +1330,101 @@
+ viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath);
+ }
+
++// begin runge/x11vnc
++ private void doRefresh()
++ {
++ System.out.println("Refreshing Local and Remote.");
++ refreshLocalLocation();
++ refreshRemoteLocation();
++ }
++
++ private void doView()
++ {
++// System.out.println("View Button Pressed");
++
++ if (selectedTable == null) {
++ return;
++ }
++ if (selectedTable.equals("remote")) {
++ viewRemote();
++ } else if (selectedTable.equals("local")) {
++ viewLocal();
++ }
++ }
++
++ private File doReceiveTmp()
++ {
++
++ if (remoteFileTable == null) {
++ return null;
++ }
++ Object selected = this.remoteFileTable.getSelectedValue();
++ if (selected == null) {
++ return null;
++ }
++ String sFileName = ((String) selected);
++
++ if (sFileName == null) {
++ return null;
++ }
++
++ // sf@2004 - Directory can't be transfered
++ if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
++ {
++ return null;
++ }
++
++ File tmp = null;
++ try {
++ tmp = File.createTempFile("ULTRAFTP", ".txt");
++ } catch (Exception e) {
++ return null;
++ }
++
++ //updateHistory("Downloaded " + localSelection.toString());
++ String remoteFileName = this.remoteLocation.getText();
++ remoteFileName+= ((String) this.remoteFileTable.getSelectedValue()).substring(1);
++ System.out.println("remoteFileName: " + remoteFileName);
++if (false) {
++ char[] b = remoteFileName.toCharArray();
++ for (int n = 0; n < b.length; n++) {
++ System.out.print(Integer.toHexString(b[n]) + " ");
++ }
++ System.out.println("");
++ for (int n = 0; n < b.length; n++) {
++ System.out.print(b[n]);
++ }
++ System.out.println("");
++}
++
++ String localDestinationPath = tmp.getAbsolutePath();
++ viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath);
++ System.out.println("ReceiveTmp: " + localDestinationPath);
++ return tmp;
++ }
++// end runge/x11vnc
++
+ private void doSend()
+ {
+- System.out.println("Send Button Pressed");
++// System.out.println("Send Button Pressed");
+
+- String sFileName = ((String) this.localFileTable.getSelectedValue());
++ Object selected = this.localFileTable.getSelectedValue();
++ if (selected == null) {
++ return;
++ }
++ String sFileName = ((String) selected);
+
+ // sf@2004 - Directory can't be transfered
+ if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
+ {
+- JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
++ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
+ return;
+ }
+
+ // sf@2004 - Overwrite prompt
+ if (remoteList.contains(sFileName))
+ {
+- int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
++ int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
+ if (r == JOptionPane.NO_OPTION)
+ return;
+ }
+@@ -1013,6 +1442,7 @@
+ //
+ private void doStop()
+ {
++ System.out.println("** Current Transfer Aborted **");
+ viewer.rfb.fAbort = true;
+ }
+ /**
+@@ -1024,6 +1454,14 @@
+ System.out.println("History: " + message);
+ historyComboBox.insertItemAt(new String(message), 0);
+ }
++
++ public void receivedRemoteDirectoryName(String str) {
++ if (doingShortcutDir) {
++ if (str.length() > 1) {
++ remoteLocation.setText(str);
++ }
++ }
++ }
+
+ /**
+ * This method updates the file table to the current selection of the remoteComboBox
+@@ -1034,11 +1472,44 @@
+ remoteSelection = null;
+
+ if (!updateDriveList) {
+- String drive = remoteDrivesComboBox.getSelectedItem().toString().substring(0,1)+ ":\\";
+- viewer.rfb.readServerDirectory(drive);
+- remoteLocation.setText(drive);
++//System.out.println("changeRemoteDrive-A " + drive); // begin runge/x11vnc
++ Object selected = remoteDrivesComboBox.getSelectedItem();
++ if (selected != null) {
++ String instr = selected.toString();
++ if (instr != null) {
++System.out.println("changeRemoteDrive: instr='" + instr + "'");
++ String drive = instr.substring(0,1)+ ":\\";
++ if (instr.startsWith(" [")) {
++ int idx = instr.lastIndexOf(']');
++ if (idx > 2) {
++ drive = instr.substring(2, idx);
++ } else {
++ drive = instr.substring(2);
++ }
++ if (drive.equals("Home")) {
++ drive = "";
++ }
++ drive += "\\";
++ doingShortcutDir = true;
++ } else {
++ doingShortcutDir = false;
++ drive = saveRemoteHack(drive);
++ }
++ gotShortcutDir = false;
++ viewer.rfb.readServerDirectory(drive);
++ if (!gotShortcutDir) {
++ remoteLocation.setText(drive);
++ }
++ } else {
++System.out.println("changeRemoteDrive: instr null");
++ }
++ } else {
++System.out.println("changeRemoteDrive: selection null");
++ }
++//System.out.println("changeRemoteDrive-B " + drive); // end runge/x11vnc
+ }
+ remoteList.clear();
++ remoteListInfo.clear();
+ remoteFileTable.setListData(remoteList);
+ }
+ /**
+@@ -1048,6 +1519,7 @@
+ private void changeLocalDrive()
+ {
+ File currentDrive = new File(localDrivesComboBox.getSelectedItem().toString());
++System.out.println("changeLocalDrive " + currentDrive.toString()); // runge/x11vnc
+ if(currentDrive.canRead())
+ {
+ localSelection = null;
+@@ -1057,9 +1529,11 @@
+ else
+ {
+ localList.clear();
++ localListInfo.clear();
+ localStatus.setText("WARNING: Drive " + localDrivesComboBox.getSelectedItem().toString());
+ connectionStatus.setText(" > WARNING - Local Drive unavailable (possibly restricted access or media not present)");
+ }
++
+ }
+ /**
+ * Determines which FileTable was double-clicked and updates the table
+@@ -1098,10 +1572,18 @@
+ selectedTable = "remote";
+ localFileTable.setBackground(new Color(238, 238, 238));
+ remoteFileTable.setBackground(new Color(255, 255, 255));
+- String name = (remoteFileTable.getSelectedValue().toString()).substring(1);
++ Object selected = remoteFileTable.getSelectedValue();
++ if (selected == null) {
++ return;
++ }
++ String selstr = selected.toString();
++ if (selstr == null) {
++ return;
++ }
++ String name = selstr.substring(1);
+ if( !name.substring(0, 2).equals(" ["))
+ remoteSelection = remoteLocation.getText() + name.substring(0, name.length());
+-
++
+ }
+
+ /*
+@@ -1115,10 +1597,38 @@
+ localFileTable.setBackground(new Color(255, 255, 255));
+ File currentSelection = new File(currentLocalDirectory, getTrimmedSelection());
+
+- if(currentSelection.isFile())
++// begin runge/x11vnc
++ // localSelection = currentSelection.getAbsoluteFile();
++ if(currentSelection.isFile()) {
+ localSelection = currentSelection.getAbsoluteFile();
++ localCurrentIsDir = false;
++ } else {
++ localCurrentIsDir = true;
++ }
++// end runge/x11vnc
+
+ }
++
++// begin runge/x11vnc
++ private void viewRemote() {
++ File tmp = doReceiveTmp();
++ if (tmp == null) {
++ return;
++ }
++ TextViewer tv = new TextViewer("Remote: " + remoteSelection, tmp, true);
++ }
++ private void viewLocal() {
++ if (localSelection == null) {
++ return;
++ }
++ if (localCurrentIsDir) {
++ return;
++ }
++ File loc = new File(localSelection.toString());
++ TextViewer tv = new TextViewer("Local: " + localSelection.toString(), loc, false);
++ }
++// end runge/x11vnc
++
+ /**
+ * Updates the Remote File Table based on selection. Called from mouseClicked handler
+ */
+@@ -1126,20 +1636,29 @@
+ String name = null;
+ String action = null;
+ String drive = null;
+- name = (remoteFileTable.getSelectedValue().toString()).substring(1);
++ Object selected = remoteFileTable.getSelectedValue();
++ if (selected == null) {
++ return;
++ }
++ String sname = selected.toString();
++ if (sname == null) {
++ return;
++ }
++ name = sname.substring(1);
+
+ if (name.equals("[..]"))
+ {
+ action = "up";
+ remoteSelection = null;
+ drive = remoteLocation.getText().substring(0, remoteLocation.getText().length() - 1);
+- // JOptionPane.showMessageDialog(null, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE);
++ // JOptionPane.showMessageDialog(jContentPane, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE);
+ int index = drive.lastIndexOf("\\");
+ drive = drive.substring(0, index + 1);
+
+ remoteLocation.setText(drive);
+ viewer.rfb.readServerDirectory(drive);
+ remoteList.clear();
++ remoteListInfo.clear();
+ remoteFileTable.setListData(remoteList);
+ }
+ else if (!name.substring(0, 2).equals(" [") && !name.substring((name.length() - 1), name.length()).equals("]"))
+@@ -1149,6 +1668,7 @@
+ remoteSelection = remoteLocation.getText() + name.substring(0, name.length());
+ drive = remoteLocation.getText();
+ // ??
++ viewRemote(); // runge/x11vnc
+ }
+ else
+ {
+@@ -1159,10 +1679,12 @@
+ remoteLocation.setText(drive);
+ viewer.rfb.readServerDirectory(drive);
+ remoteList.clear();
++ remoteListInfo.clear();
+ remoteFileTable.setListData(remoteList);
+ }
+ //remoteLocation.setText(drive);
+ }
++
+ /**
+ * Updates the Local File Table based on selection. Called from MouseClicked handler
+ */
+@@ -1188,6 +1710,7 @@
+ else if (currentSelection.isFile())
+ {
+ localSelection = currentSelection.getAbsoluteFile();
++ viewLocal(); // runge/x11vnc
+ }
+ else if (currentSelection.isDirectory())
+ {
+@@ -1201,13 +1724,22 @@
+ *
+ */
+ private String getTrimmedSelection(){
+- String currentSelection = (localFileTable.getSelectedValue().toString()).substring(1);
+- if(currentSelection.substring(0,1).equals("[") &&
+- currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){
+- return currentSelection.substring(1,currentSelection.length()-1);
+- } else {
+- return currentSelection;
+- }
++ String currentSelection = "";
++ Object selected = localFileTable.getSelectedValue();
++ if (selected == null) {
++ return currentSelection;
++ }
++ String selstr = selected.toString();
++ if (selstr == null) {
++ return currentSelection;
++ }
++ currentSelection = selstr.substring(1);
++ if(currentSelection.substring(0,1).equals("[") &&
++ currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){
++ return currentSelection.substring(1,currentSelection.length()-1);
++ } else {
++ return currentSelection;
++ }
+ }
+
+ /*
+@@ -1241,36 +1773,148 @@
+ return null;
+ }
+
++ String timeStr(long t) {
++ Date date = new Date(t);
++ return date.toString();
++ }
++ String dotPast(double f, int n) {
++ String fs = "" + f;
++ int i = fs.lastIndexOf(".") + n;
++ if (i >= 0) {
++ int len = fs.length();
++ if (i >= len) {
++ i = len-1;
++ }
++ fs = fs.substring(0, i);
++ }
++ return fs;
++ }
++ String sizeStr(int s) {
++ if (s < 0) {
++ return s + "? B";
++ } else if (s < 1024) {
++ return s + " B";
++ } else if (s < 1024 * 1024) {
++ double k = s / 1024.0;
++ String ks = dotPast(k, 3);
++
++ return s + " (" + ks + " KB)";
++ } else {
++ double m = s / (1024.0*1024.0);
++ String ms = dotPast(m, 3);
++ return s + " (" + ms + " MB)";
++ }
++ }
++
++ int max_char(String text) {
++ int maxc = 0;
++ char chars[] = text.toCharArray();
++ for (int n = 0; n < chars.length; n++) {
++ if ((int) chars[n] > maxc) {
++ maxc = (int) chars[n];
++ }
++ }
++ return maxc;
++ }
+
+ /*
+ * Navigates the local file structure up or down one directory
+ */
+ public void changeLocalDirectory(File dir)
+ {
+- currentLocalDirectory = dir; // Updates Global
++ dir = saveLocalHack(dir); // runge/x11vnc
++
++ if (dir == null) {
++ connectionStatus.setText("Error changing local directory.");
++ historyComboBox.insertItemAt(new String("> Error changing local directory."), 0);
++ historyComboBox.setSelectedIndex(0);
++ return;
++ }
++
+ File allFiles[] = dir.listFiles(); // Reads files
+ String[] contents = dir.list();
+
++ if (contents == null || allFiles == null) {
++ connectionStatus.setText("Error changing local directory.");
++ historyComboBox.insertItemAt(new String("> Error changing local directory."), 0);
++ historyComboBox.setSelectedIndex(0);
++ return;
++ }
++
++ currentLocalDirectory = dir; // Updates Global
++// begin runge/x11vnc
++System.out.println("changeLocalDirectory: " + dir.toString());
++ if (contents != null) {
++ java.util.Arrays.sort(contents, String.CASE_INSENSITIVE_ORDER);
++ for (int i = 0; i < contents.length; i++) {
++ allFiles[i] = new File(dir, contents[i]);
++ }
++ } else {
++ return;
++ }
++// end runge/x11vnc
++
+ localList.clear();
++ localListInfo.clear();
+ localList.addElement(" [..]");
++ localListInfo.addElement(" [..]");
++
++ ArrayList DirInfo = new ArrayList();
++ ArrayList FilInfo = new ArrayList();
++
++ Charset charset = Charset.forName("ISO-8859-1");
++ CharsetDecoder decoder = charset.newDecoder();
++ CharsetEncoder encoder = charset.newEncoder();
+
+ // Populate the Lists
+ for (int i = 0; i < contents.length; i++)
+ {
+- if (allFiles[i].isDirectory())
++ String f1 = contents[i];
++
++if (false) {
++
++System.out.println("max_char: " + max_char(f1) + " " + f1);
++ if (max_char(f1) > 255) {
++ try {
++System.out.println("bbuf1");
++ ByteBuffer bbuf = encoder.encode(CharBuffer.wrap(f1.toCharArray()));
++System.out.println("bbuf2");
++ CharBuffer cbuf = decoder.decode(bbuf);
++System.out.println("bbuf3");
++ f1 = cbuf.toString();
++System.out.println("did bbuf: " + f1);
++ } catch (Exception e) {
++ ;
++ }
++ }
++}
++
++ String f2 = f1;
++ if (f2.length() < 24) {
++ for (int ik = f2.length(); ik < 24; ik++) {
++ f2 = f2 + " ";
++ }
++ }
++ String s = f2 + " \tLastmod: " + timeStr(allFiles[i].lastModified()) + " \t\tSize: " + sizeStr((int) allFiles[i].length());
++ if (allFiles[i].isDirectory()) {
+ // localList.addElement("[" + contents[i] + "]");
+- DirsList.add(" [" + contents[i] + "]"); // sf@2004
+- else
+- {
++ DirsList.add(" [" + f1 + "]"); // sf@2004
++ DirInfo.add(s);
++ } else {
+ // localList.addElement(contents[i]);
+- FilesList.add(" " + contents[i]); // sf@2004
++ FilesList.add(" " + f1); // sf@2004
++ FilInfo.add(s);
+ }
+ }
+ // sf@2004
+- for (int i = 0; i < DirsList.size(); i++)
++ for (int i = 0; i < DirsList.size(); i++) {
+ localList.addElement(DirsList.get(i));
+- for (int i = 0; i < FilesList.size(); i++)
++ localListInfo.addElement(DirInfo.get(i));
++ }
++ for (int i = 0; i < FilesList.size(); i++) {
+ localList.addElement(FilesList.get(i));
++ localListInfo.addElement(FilInfo.get(i));
++ }
+
+ FilesList.clear();
+ DirsList.clear();
+@@ -1296,3 +1940,147 @@
+ }
+
+ } // @jve:visual-info decl-index=0 visual-constraint="10,10"
++
++// begin runge/x11vnc
++class TextViewer extends JFrame implements ActionListener {
++
++ JTextArea textArea = new JTextArea(35, 80);
++ File file = null;
++ JButton refreshButton;
++ JButton dismissButton;
++ Timer tim = null;
++ int rcnt = 0;
++ int tms = 250;
++ boolean delete_it = false;
++ TextViewer me;
++
++ public TextViewer(String s, File f, boolean d) {
++
++ delete_it = d;
++ file = f;
++ me = this;
++
++ JScrollPane scrollPane = new JScrollPane(textArea,
++ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
++ JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
++
++ textArea.setEditable(false);
++ textArea.setFont(new Font("Monospaced", Font.PLAIN, 12));
++
++ KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, InputEvent.SHIFT_MASK);
++ AbstractAction escapeAction = new AbstractAction() {
++ public void actionPerformed(ActionEvent actionEvent) {
++ cleanse();
++ me.dispose();
++ }
++ };
++ textArea.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction");
++ textArea.getInputMap().put(stroke, "escapeAction");
++ textArea.getActionMap().put("escapeAction", escapeAction);
++
++ refreshButton = new JButton();
++ refreshButton.setText("Reload");
++ refreshButton.setName("refreshButton");
++ refreshButton.addActionListener(this);
++
++ dismissButton = new JButton();
++ dismissButton.setText("Dismiss");
++ dismissButton.setName("dismissButton");
++ dismissButton.addActionListener(this);
++
++ JPanel buttons = new JPanel();
++ buttons.setLayout(new BorderLayout());
++ buttons.add(refreshButton, BorderLayout.WEST);
++ buttons.add(dismissButton, BorderLayout.EAST);
++
++ JPanel content = new JPanel();
++ content.setLayout(new BorderLayout());
++ content.add(scrollPane, BorderLayout.CENTER);
++ content.add(buttons, BorderLayout.SOUTH);
++
++ ActionListener tsk = new ActionListener() {
++ public void actionPerformed(ActionEvent evt) {
++ // System.out.println("tsk");
++ refresh();
++ }
++ };
++ tim = new Timer(tms, tsk);
++ tim.start();
++
++ this.setContentPane(content);
++ this.setTitle("TextViewer - " + s);
++ this.pack();
++ this.setVisible(true);
++ }
++
++ private void refresh() {
++
++ rcnt++;
++ if (rcnt * tms > 3000 && tim != null) {
++ tim.stop();
++ tim = null;
++ }
++ BufferedReader input = null;
++ StringBuffer contents = new StringBuffer();
++ try {
++ if (input == null) {
++ input = new BufferedReader(new FileReader(file));
++ }
++ String line = null;
++ int i = 0;
++ while (( line = input.readLine()) != null) {
++ if (i == 0) {
++ // System.out.println("read");
++ }
++ i++;
++ contents.append(line);
++ contents.append(System.getProperty("line.separator"));
++ }
++ } catch (Exception e) {
++ ;
++ } finally {
++ try {
++ if (input != null) {
++ input.close();
++ input = null;
++ }
++ } catch (Exception e) {
++ ;
++ }
++ }
++
++ textArea.setText(contents.toString());
++ textArea.setCaretPosition(0);
++ }
++
++ public void actionPerformed(ActionEvent evt) {
++
++ if (evt.getSource() == refreshButton) {
++ refresh();
++ }
++ if (evt.getSource() == dismissButton) {
++ cleanse();
++ this.dispose();
++ }
++ }
++
++ private void cleanse() {
++ if (delete_it && file != null) {
++ try {
++ file.delete();
++ file = null;
++ } catch (Exception e) {
++ ;
++ }
++ }
++ }
++
++ protected void finalize() throws Throwable {
++ try {
++ cleanse();
++ } finally {
++ super.finalize();
++ }
++ }
++}
++// end runge/x11vnc
+diff -Naur JavaViewer.orig/Makefile JavaViewer/Makefile
+--- JavaViewer.orig/Makefile 2006-05-29 09:06:32.000000000 -0400
++++ JavaViewer/Makefile 2010-05-18 20:53:32.000000000 -0400
+@@ -4,6 +4,7 @@
+
+ CP = cp
+ JC = javac
++JC_ARGS = -target 1.4 -source 1.4
+ JAR = jar
+ ARCHIVE = VncViewer.jar
+ PAGES = index.vnc shared.vnc noshared.vnc hextile.vnc zlib.vnc tight.vnc
+@@ -20,7 +21,7 @@
+ all: $(CLASSES) $(ARCHIVE)
+
+ $(CLASSES): $(SOURCES)
+- $(JC) -O $(SOURCES)
++ $(JC) $(JC_ARGS) -O $(SOURCES)
+
+ $(ARCHIVE): $(CLASSES)
+ $(JAR) cf $(ARCHIVE) $(CLASSES)
+diff -Naur JavaViewer.orig/OptionsFrame.java JavaViewer/OptionsFrame.java
+--- JavaViewer.orig/OptionsFrame.java 2005-11-21 18:50:16.000000000 -0500
++++ JavaViewer/OptionsFrame.java 2007-05-13 22:18:30.000000000 -0400
+@@ -144,7 +144,10 @@
+ choices[jpegQualityIndex].select("6");
+ choices[cursorUpdatesIndex].select("Enable");
+ choices[useCopyRectIndex].select("Yes");
+- choices[eightBitColorsIndex].select("64");
++// begin runge/x11vnc
++// choices[eightBitColorsIndex].select("64");
++ choices[eightBitColorsIndex].select("Full");
++// end runge/x11vnc
+ choices[mouseButtonIndex].select("Normal");
+ choices[viewOnlyIndex].select("No");
+ choices[shareDesktopIndex].select("Yes");
+diff -Naur JavaViewer.orig/RfbProto.java JavaViewer/RfbProto.java
+--- JavaViewer.orig/RfbProto.java 2006-05-24 15:14:40.000000000 -0400
++++ JavaViewer/RfbProto.java 2010-11-30 22:13:58.000000000 -0500
+@@ -31,6 +31,7 @@
+ import java.net.Socket;
+ import java.util.*;
+ import java.util.zip.*;
++import java.text.DateFormat;
+
+
+ class RfbProto {
+@@ -86,8 +87,11 @@
+
+ // sf@2004 - FileTransfer part
+ ArrayList remoteDirsList;
++ ArrayList remoteDirsListInfo;
+ ArrayList remoteFilesList;
++ ArrayList remoteFilesListInfo;
+ ArrayList a;
++ ArrayList b;
+ boolean fFTInit = true; // sf@2004
+ boolean fFTAllowed = true;
+ boolean fAbort = false;
+@@ -199,6 +203,10 @@
+ // playback.
+ int numUpdatesInSession;
+
++// begin runge/x11vnc
++ int readServerDriveListCnt = -1;
++ long readServerDriveListTime = 0;
++// end runge/x11vnc
+ //
+ // Constructor. Make TCP connection to RFB server.
+ //
+@@ -207,7 +215,27 @@
+ viewer = v;
+ host = h;
+ port = p;
+- sock = new Socket(host, port);
++// begin runge/x11vnc
++// sock = new Socket(host, port);
++ if (! viewer.disableSSL) {
++ System.out.println("new SSLSocketToMe");
++ SSLSocketToMe ssl;
++ try {
++ ssl = new SSLSocketToMe(host, port, v);
++ } catch (Exception e) {
++ throw new IOException(e.getMessage());
++ }
++
++ try {
++ sock = ssl.connectSock();
++ } catch (Exception es) {
++ throw new IOException(es.getMessage());
++ }
++ } else {
++ sock = new Socket(host, port);
++ }
++// end runge/x11vnc
++
+ is =
+ new DataInputStream(
+ new BufferedInputStream(sock.getInputStream(), 16384));
+@@ -215,9 +243,12 @@
+ osw = new OutputStreamWriter(sock.getOutputStream());
+ inDirectory2 = false;
+ a = new ArrayList();
++ b = new ArrayList();
+ // sf@2004
+ remoteDirsList = new ArrayList();
++ remoteDirsListInfo = new ArrayList();
+ remoteFilesList = new ArrayList();
++ remoteFilesListInfo = new ArrayList();
+
+ sendFileSource = "";
+ }
+@@ -420,7 +451,13 @@
+ //
+
+ int readServerMessageType() throws IOException {
+- int msgType = is.readUnsignedByte();
++ int msgType;
++ try {
++ msgType = is.readUnsignedByte();
++ } catch (Exception e) {
++ viewer.disconnect();
++ return -1;
++ }
+
+ // If the session is being recorded:
+ if (rec != null) {
+@@ -600,6 +637,7 @@
+ contentParamT = is.readUnsignedByte();
+ contentParamT = contentParamT << 8;
+ contentParam = contentParam | contentParamT;
++//System.out.println("FTM: contentType " + contentType + " contentParam " + contentParam);
+ if (contentType == rfbRDrivesList || contentType == rfbDirPacket)
+ {
+ readDriveOrDirectory(contentParam);
+@@ -610,7 +648,7 @@
+ }
+ else if (contentType == rfbFilePacket)
+ {
+- receiveFileChunk();
++ receiveFileChunk();
+ }
+ else if (contentType == rfbEndOfFile)
+ {
+@@ -618,6 +656,10 @@
+ }
+ else if (contentType == rfbAbortFileTransfer)
+ {
++ System.out.println("rfbAbortFileTransfer: fFileReceptionRunning="
++ + fFileReceptionRunning + " fAbort="
++ + fAbort + " fFileReceptionError="
++ + fFileReceptionError);
+ if (fFileReceptionRunning)
+ {
+ endOfReceiveFile(false); // Error
+@@ -626,6 +668,11 @@
+ {
+ // sf@2004 - Todo: Add TestPermission
+ // System.out.println("File Transfer Aborted!");
++
++ // runge: seems like we must at least read the remaining
++ // 8 bytes of the header, right?
++ int size = is.readInt();
++ int length = is.readInt();
+ }
+
+ }
+@@ -645,6 +692,7 @@
+ {
+ System.out.println("ContentType: " + contentType);
+ }
++//System.out.println("FTM: done");
+ }
+
+ //Refactored from readRfbFileTransferMsg()
+@@ -662,6 +710,7 @@
+
+ //Refactored from readRfbFileTransferMsg()
+ public void readDriveOrDirectory(int contentParam) throws IOException {
++//System.out.println("RDOD: " + contentParam + " " + inDirectory2);
+ if (contentParam == rfbADrivesList)
+ {
+ readFTPMsgDriveList();
+@@ -688,13 +737,21 @@
+
+ // Internally used. Write an Rfb message to the server
+ void writeRfbFileTransferMsg(
+- int contentType,
+- int contentParam,
+- long size, // 0 : compression not supported - 1 : compression supported
+- long length,
+- String text) throws IOException
++ int contentType,
++ int contentParam,
++ long size, // 0 : compression not supported - 1 : compression supported
++ long length,
++ String text) throws IOException
+ {
+ byte b[] = new byte[12];
++ byte byteArray[];
++
++ if (viewer.dsmActive) {
++ // need to send the rfbFileTransfer msg type twice for the plugin...
++ byte b2[] = new byte[1];
++ b2[0] = (byte) rfbFileTransfer;
++ os.write(b2);
++ }
+
+ b[0] = (byte) rfbFileTransfer;
+ b[1] = (byte) contentType;
+@@ -702,7 +759,7 @@
+
+ byte by = 0;
+ long c = 0;
+- length++;
++
+ c = size & 0xFF000000;
+ by = (byte) (c >>> 24);
+ b[4] = by;
+@@ -716,6 +773,32 @@
+ by = (byte) c;
+ b[7] = by;
+
++ if (text != null) {
++ byte byteArray0[] = text.getBytes();
++ int maxc = max_char(text);
++ if (maxc > 255) {
++ System.out.println("writeRfbFileTransferMsg: using getBytes(\"UTF-8\")");
++ byteArray0 = text.getBytes("UTF-8");
++ } else if (maxc > 127) {
++ System.out.println("writeRfbFileTransferMsg: using getBytes(\"ISO-8859-1\")");
++ byteArray0 = text.getBytes("ISO-8859-1");
++ }
++ byteArray = new byte[byteArray0.length + 1];
++ for (int i = 0; i < byteArray0.length; i++) {
++ byteArray[i] = byteArray0[i];
++ }
++ byteArray[byteArray.length - 1] = 0;
++System.out.println("writeRfbFileTransferMsg: length: " + length + " -> byteArray.length: " + byteArray.length);
++
++ // will equal length for ascii, ISO-8859-1, more for UTF-8
++ length = byteArray.length;
++
++ //length++; // used to not include null byte at end.
++ } else {
++ String moo = "moo";
++ byteArray = moo.getBytes();
++ }
++
+ c = length & 0xFF000000;
+ by = (byte) (c >>> 24);
+ b[8] = by;
+@@ -729,29 +812,91 @@
+ by = (byte) c;
+ b[11] = by;
+ os.write(b);
++
++//System.out.println("size: " + size + " length: " + length + " text: " + text);
+
+
+ if (text != null)
+ {
+- byte byteArray[] = text.getBytes();
+- byte byteArray2[] = new byte[byteArray.length + 1];
+- for (int i = 0; i < byteArray.length; i++) {
+- byteArray2[i] = byteArray[i];
++ os.write(byteArray);
++ }
++ }
++
++ int max_char(String text) {
++ int maxc = 0;
++ char chars[] = text.toCharArray();
++ for (int n = 0; n < chars.length; n++) {
++ if ((int) chars[n] > maxc) {
++ maxc = (int) chars[n];
+ }
+- byteArray2[byteArray2.length - 1] = 0;
+- os.write(byteArray2);
+ }
+-
++ return maxc;
+ }
+
++ String guess_encoding(char[] chars) {
++ boolean saw_high_char = false;
++
++ for (int i = 0; i < chars.length; i++) {
++ if (chars[i] == '\0') {
++ break;
++ }
++ if (chars[i] >= 128) {
++ saw_high_char = true;
++ break;
++ }
++ }
++ if (!saw_high_char) {
++ return "ASCII";
++ }
++ char prev = 1;
++ boolean valid_utf8 = true;
++ int n = 0;
++ for (int i = 0; i < chars.length; i++) {
++ if (chars[i] == '\0') {
++ break;
++ }
++ char c = chars[i];
++ if (prev < 128 && c >= 128) {
++ if (c >> 5 == 0x6) {
++ n = 1;
++ } else if (c >> 4 == 0xe) {
++ n = 2;
++ } else if (c >> 3 == 0x1e) {
++ n = 3;
++ } else if (c >> 2 == 0x3e) {
++ n = 4;
++ } else {
++ valid_utf8 = false;
++ break;
++ }
++ } else {
++ if (n > 0) {
++ if (c < 128) {
++ valid_utf8 = false;
++ break;
++ }
++ n--;
++ }
++ }
++
++ prev = c;
++ }
++ if (valid_utf8) {
++ return "UTF-8";
++ } else {
++ return "ISO-8859-1";
++ }
++ }
++
++
+ //Internally used. Write an rfb message to the server for sending files ONLY
+ int writeRfbFileTransferMsgForSendFile(
+- int contentType,
+- int contentParam,
+- long size,
+- long length,
+- String source
+- ) throws IOException
++ int contentType,
++ int contentParam,
++ long size,
++ long length,
++ String source
++ ) throws IOException
+ {
+ File f = new File(source);
+ fis = new FileInputStream(f);
+@@ -768,50 +913,47 @@
+
+ while (bytesRead!=-1)
+ {
+- counter += bytesRead;
+- myDeflater.setInput(byteBuffer, 0, bytesRead);
+- myDeflater.finish();
+- compressedSize = myDeflater.deflate(CompressionBuffer);
+- myDeflater.reset();
+- // If the compressed data is larger than the original one, we're dealing with
+- // already compressed data
+- if (compressedSize > bytesRead)
+- fCompress = false;
+- this.writeRfbFileTransferMsg(
+- contentType,
+- contentParam,
+- (fCompress ? 1 : 0),
+- (fCompress ? compressedSize-1 : bytesRead-1),
+- null
+- );
+- // Todo: Test write error !
+- os.write(
+- fCompress ? CompressionBuffer : byteBuffer,
+- 0,
+- fCompress ? compressedSize : bytesRead
+- );
+-
+- // Todo: test read error !
+- bytesRead = fis.read(byteBuffer);
+-
+- // viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes");
+- viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length()));
+- viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%");
+-
+- if (fAbort == true)
+- {
+- fAbort = false;
+- fError = true;
+- break;
+- }
+- try
+- {
+- Thread.sleep(5);
+- }
+- catch(InterruptedException e)
+- {
+- System.err.println("Interrupted");
+- }
++ counter += bytesRead;
++ myDeflater.setInput(byteBuffer, 0, bytesRead);
++ myDeflater.finish();
++ compressedSize = myDeflater.deflate(CompressionBuffer);
++ myDeflater.reset();
++ // If the compressed data is larger than the original one, we're dealing with
++ // already compressed data
++ if (compressedSize > bytesRead)
++ fCompress = false;
++ this.writeRfbFileTransferMsg(
++ contentType,
++ contentParam,
++ (fCompress ? 1 : 0),
++// RUNGE (fCompress ? compressedSize-1 : bytesRead-1),
++ (fCompress ? compressedSize : bytesRead),
++ null
++ );
++ // Todo: Test write error !
++ os.write(fCompress ? CompressionBuffer : byteBuffer, 0, fCompress ? compressedSize : bytesRead);
++
++ // Todo: test read error !
++ bytesRead = fis.read(byteBuffer);
++
++ // viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes");
++ viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length()));
++ viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%");
++
++ if (fAbort == true)
++ {
++ fAbort = false;
++ fError = true;
++ break;
++ }
++ try
++ {
++ Thread.sleep(5);
++ }
++ catch(InterruptedException e)
++ {
++ System.err.println("Interrupted");
++ }
+ }
+
+ writeRfbFileTransferMsg(fError ? rfbAbortFileTransfer : rfbEndOfFile, 0, 0, 0, null);
+@@ -831,24 +973,30 @@
+ {
+ System.out.print((char) is.readUnsignedByte());
+ }
++ System.out.println("");
++
++ if (size == rfbRErrorCmd || size == -1) {
++ viewer.ftp.enableButtons();
++ viewer.ftp.connectionStatus.setText("Remote file not available for writing.");
++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for writing."), 0);
++ viewer.ftp.historyComboBox.setSelectedIndex(0);
++ return;
++ }
+
+- int ret = writeRfbFileTransferMsgForSendFile(
+- rfbFilePacket,
+- 0,
+- 0,
+- 0,
+- sendFileSource);
++ int ret = writeRfbFileTransferMsgForSendFile(rfbFilePacket, 0, 0, 0, sendFileSource);
+
+ viewer.ftp.refreshRemoteLocation();
+ if (ret != 1)
+ {
+ viewer.ftp.connectionStatus.setText(" > Error - File NOT sent");
+- viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource) + "> was not correctly sent (aborted by user or error)",0);
++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource)
++ + "> was not correctly sent (aborted or error). Data may still be buffered/in transit. Wait for remote listing...",0);
+ }
+ else
+ {
+ viewer.ftp.connectionStatus.setText(" > File sent");
+- viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource) + "> was sent to Remote Machine",0);
++ viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource)
++ + "> was sent to Remote Machine. Note: data may still be buffered/in transit. Wait for remote listing...",0);
+ }
+ viewer.ftp.historyComboBox.setSelectedIndex(0);
+ viewer.ftp.enableButtons();
+@@ -907,7 +1055,7 @@
+ //Handles acknowledgement that the file has been deleted on the server
+ void deleteRemoteFileFeedback() throws IOException
+ {
+- is.readInt();
++ int ret = is.readInt();
+ int length = is.readInt();
+ String f = "";
+ for (int i = 0; i < length; i++)
+@@ -916,7 +1064,11 @@
+ }
+
+ viewer.ftp.refreshRemoteLocation();
+- viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0);
++ if (ret == -1) {
++ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Delete File On Remote Machine: "),0);
++ } else {
++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0);
++ }
+ viewer.ftp.historyComboBox.setSelectedIndex(0);
+ }
+
+@@ -926,12 +1078,7 @@
+ try
+ {
+ String temp = text;
+- writeRfbFileTransferMsg(
+- rfbCommand,
+- rfbCFileDelete,
+- 0,
+- temp.length(),
+- temp);
++ writeRfbFileTransferMsg(rfbCommand, rfbCFileDelete, 0, temp.length(), temp);
+ }
+ catch (IOException e)
+ {
+@@ -943,7 +1090,7 @@
+ // Handles acknowledgement that the directory has been created on the server
+ void createRemoteDirectoryFeedback() throws IOException
+ {
+- is.readInt();
++ int ret = is.readInt();
+ int length = is.readInt();
+ String f="";
+ for (int i = 0; i < length; i++)
+@@ -951,7 +1098,11 @@
+ f += (char)is.readUnsignedByte();
+ }
+ viewer.ftp.refreshRemoteLocation();
+- viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0);
++ if (ret == -1) {
++ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Create Directory on Remote Machine."),0);
++ } else {
++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0);
++ }
+ viewer.ftp.historyComboBox.setSelectedIndex(0);
+ }
+
+@@ -961,12 +1112,7 @@
+ try
+ {
+ String temp = text;
+- writeRfbFileTransferMsg(
+- rfbCommand,
+- rfbCDirCreate,
+- 0,
+- temp.length(),
+- temp);
++ writeRfbFileTransferMsg(rfbCommand, rfbCDirCreate, 0, temp.length(), temp);
+ }
+ catch (IOException e)
+ {
+@@ -979,15 +1125,13 @@
+ {
+ try
+ {
++//System.out.println("requestRemoteFile text: " + text);
++//System.out.println("requestRemoteFile leng: " + text.length());
+ String temp = text;
+ receivePath = localPath;
+
+- writeRfbFileTransferMsg(
+- rfbFileTransferRequest,
+- 0,
+- 1, // 0 : compression not supported - 1 : compression supported
+- temp.length(),
+- temp);
++ // 0 : compression not supported - 1 : compression supported
++ writeRfbFileTransferMsg(rfbFileTransferRequest, 0, 1, temp.length(), temp);
+ }
+ catch (IOException e)
+ {
+@@ -1004,6 +1148,9 @@
+ viewer.ftp.disableButtons();
+ int size = is.readInt();
+ int length = is.readInt();
++
++//System.out.println("receiveFileHeader size: " + size);
++//System.out.println("receiveFileHeader leng: " + length);
+
+ String tempName = "";
+ for (int i = 0; i < length; i++)
+@@ -1011,6 +1158,15 @@
+ tempName += (char) is.readUnsignedByte();
+ }
+
++ if (size == rfbRErrorCmd || size == -1) {
++ fFileReceptionRunning = false;
++ viewer.ftp.enableButtons();
++ viewer.ftp.connectionStatus.setText("Remote file not available for reading.");
++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for reading."), 0);
++ viewer.ftp.historyComboBox.setSelectedIndex(0);
++ return;
++ }
++
+ // sf@2004 - Read the high part of file size (not yet in rfbFileTransferMsg for
+ // backward compatibility reasons...)
+ int sizeH = is.readInt();
+@@ -1021,7 +1177,16 @@
+ fileSize=0;
+ fileChunkCounter = 0;
+ String fileName = receivePath;
+- fos = new FileOutputStream(fileName);
++ try {
++ fos = new FileOutputStream(fileName);
++ } catch (Exception e) {
++ fFileReceptionRunning = false;
++ writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null);
++ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR opening Local File: <" + fileName ),0);
++ viewer.ftp.historyComboBox.setSelectedIndex(0);
++ viewer.ftp.enableButtons();
++ return;
++ }
+ writeRfbFileTransferMsg(rfbFileHeader, 0, 0, 0, null);
+ }
+
+@@ -1085,7 +1250,13 @@
+ fAbort = false;
+ fFileReceptionError = true;
+ writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null);
+-
++
++ //runge for use with x11vnc/libvncserver, no rfbAbortFileTransfer reply sent.
++ try {Thread.sleep(500);} catch (InterruptedException e) {}
++ viewer.ftp.enableButtons();
++ viewer.ftp.refreshLocalLocation();
++ viewer.ftp.connectionStatus.setText(" > Error - File NOT received");
++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + receivePath + "> not correctly received from Remote Machine (aborted by user or error)") ,0);
+ }
+ // sf@2004 - For old FT protocole only
+ /*
+@@ -1104,7 +1275,7 @@
+ int length = is.readInt();
+ fileSize=0;
+ fos.close();
+-
++
+ viewer.ftp.refreshLocalLocation();
+ if (fReceptionOk && !fFileReceptionError)
+ {
+@@ -1132,12 +1303,7 @@
+ try
+ {
+ String temp = text;
+- writeRfbFileTransferMsg(
+- rfbDirContentRequest,
+- rfbRDirContent,
+- 0,
+- temp.length(),
+- temp);
++ writeRfbFileTransferMsg(rfbDirContentRequest, rfbRDirContent, 0, temp.length(), temp);
+ }
+ catch (IOException e)
+ {
+@@ -1197,11 +1363,80 @@
+ str += temp;
+ }
+ }
++ // runge
++ viewer.ftp.receivedRemoteDirectoryName(str);
+ // viewer.ftp.changeRemoteDirectory(str);
+
+ }
+ }
+
++ int zogswap(int n) {
++ long l = n;
++ if (l < 0) {
++ l += 0x100000000L;
++ }
++ l = l & 0xFFFFFFFF;
++ l = (l >> 24) | ((l & 0x00ff0000) >> 8) | ((l & 0x0000ff00) << 8) | (l << 24);
++ return (int) l;
++ }
++
++ int windozeToUnix(int L, int H) {
++ long L2 = zogswap(L);
++ long H2 = zogswap(H);
++ long unix = (H2 << 32) + L2;
++ unix -= 11644473600L * 10000000L;
++ unix /= 10000000L;
++ //System.out.println("unix time: " + unix + " H2: " + H2 + " L2: " + L2);
++ return (int) unix;
++ }
++
++ String timeStr(int t, int h) {
++ if (h == 0) {
++ // x11vnc/libvncserver unix
++ t = zogswap(t);
++ } else {
++ // ultra (except if h==0 by chance)
++ t = windozeToUnix(t, h);
++ }
++ long tl = (long) t;
++ Date date = new Date(tl * 1000);
++ if (true) {
++ return date.toString();
++ } else {
++ return DateFormat.getDateTimeInstance().format(date);
++ }
++ }
++
++ String dotPast(double f, int n) {
++ String fs = "" + f;
++ int i = fs.lastIndexOf(".") + n;
++ if (i >= 0) {
++ int len = fs.length();
++ if (i >= len) {
++ i = len-1;
++ }
++ fs = fs.substring(0, i);
++ }
++ return fs;
++ }
++ String sizeStr(int s) {
++ s = zogswap(s);
++ if (s < 0) {
++ return s + "? B";
++ } else if (s < 1024) {
++ return s + " B";
++ } else if (s < 1024 * 1024) {
++ double k = s / 1024.0;
++ String ks = dotPast(k, 3);
++
++ return s + " (" + ks + " KB)";
++ } else {
++ double m = s / (1024.0*1024.0);
++ String ms = dotPast(m, 3);
++ return s + " (" + ms + " MB)";
++ }
++ }
++
+ //Internally used to receive directory content from server
+ //Here, the server sends one file/directory with it's attributes
+ void readFTPMsgDirectoryListContent() throws IOException
+@@ -1217,17 +1452,32 @@
+ dwReserved0,
+ dwReserved1;
+ long ftCreationTime, ftLastAccessTime, ftLastWriteTime;
++ int ftCreationTimeL, ftLastAccessTimeL, ftLastWriteTimeL;
++ int ftCreationTimeH, ftLastAccessTimeH, ftLastWriteTimeH;
+ char cFileName, cAlternateFileName;
+ int length = 0;
+ is.readInt();
+ length = is.readInt();
++
++ char[] chars = new char[4*length];
++ int char_cnt = 0;
++ for (int i = 0; i < chars.length; i++) {
++ chars[i] = '\0';
++ }
++
+ dwFileAttributes = is.readInt();
+ length -= 4;
+- ftCreationTime = is.readLong();
++ //ftCreationTime = is.readLong();
++ ftCreationTimeL = is.readInt();
++ ftCreationTimeH = is.readInt();
+ length -= 8;
+- ftLastAccessTime = is.readLong();
++ //ftLastAccessTime = is.readLong();
++ ftLastAccessTimeL = is.readInt();
++ ftLastAccessTimeH = is.readInt();
+ length -= 8;
+- ftLastWriteTime = is.readLong();
++ //ftLastWriteTime = is.readLong();
++ ftLastWriteTimeL = is.readInt();
++ ftLastWriteTimeH = is.readInt();
+ length -= 8;
+ nFileSizeHigh = is.readInt();
+ length -= 4;
+@@ -1239,10 +1489,12 @@
+ length -= 4;
+ cFileName = (char) is.readUnsignedByte();
+ length--;
++ chars[char_cnt++] = cFileName;
+ while (cFileName != '\0')
+ {
+ fileName += cFileName;
+ cFileName = (char) is.readUnsignedByte();
++ chars[char_cnt++] = cFileName;
+ length--;
+ }
+ cAlternateFileName = (char) is.readByte();
+@@ -1253,7 +1505,28 @@
+ cAlternateFileName = (char) is.readUnsignedByte();
+ length--;
+ }
+- if (dwFileAttributes == 268435456
++ String guessed = guess_encoding(chars);
++ if (!guessed.equals("ASCII")) {
++ System.out.println("guess: " + guessed + "\t" + fileName);
++ }
++ if (guessed.equals("UTF-8")) {
++ try {
++ byte[] bytes = new byte[char_cnt-1];
++ for (int i=0; i < char_cnt-1; i++) {
++ bytes[i] = (byte) chars[i];
++ }
++ String newstr = new String(bytes, "UTF-8");
++ fileName = newstr;
++ } catch (Exception e) {
++ System.out.println("failed to convert bytes to UTF-8 based string");
++ }
++ }
++ for (int i = 0; i < char_cnt; i++) {
++ //System.out.println("char[" + i + "]\t" + (int) chars[i]);
++ }
++ if (fileName.length() <= 0) {
++ ;
++ } else if (dwFileAttributes == 268435456
+ || dwFileAttributes == 369098752
+ || dwFileAttributes == 285212672
+ || dwFileAttributes == 271056896
+@@ -1263,11 +1536,74 @@
+ || dwFileAttributes == 369623040)
+ {
+ fileName = " [" + fileName + "]";
+- remoteDirsList.add(fileName); // sf@2004
+- }
+- else
+- {
+- remoteFilesList.add(" " + fileName); // sf@2004
++// begin runge/x11vnc
++// remoteDirsList.add(fileName); // sf@2004
++ int i = -1;
++ String t1 = fileName.toLowerCase();
++ for (int j = 0; j < remoteDirsList.size(); j++) {
++ String t = (String) remoteDirsList.get(j);
++ String t2 = t.toLowerCase();
++ if (t1.compareTo(t2) < 0) {
++ i = j;
++ break;
++ }
++ }
++ //String s = "Lastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " " + fileName;
++ String f2 = fileName;
++ if (f2.length() < 24) {
++ for (int ik = f2.length(); ik < 24; ik++) {
++ f2 = f2 + " ";
++ }
++ }
++ String s = f2 + " \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " \t\tSize: " + sizeStr(nFileSizeLow);
++ //s = fileName + " Lastmod: " + zogswap(ftLastWriteTimeL);
++ if (i >= 0) {
++ remoteDirsList.add(i, fileName);
++ remoteDirsListInfo.add(i, s);
++ } else {
++ remoteDirsList.add(fileName);
++ remoteDirsListInfo.add(s);
++ }
++// end runge/x11vnc
++ } else {
++// begin runge/x11vnc
++// remoteFilesList.add(" " + fileName); // sf@2004
++
++ fileName = " " + fileName;
++ int i = -1;
++ String t1 = fileName.toLowerCase();
++ for (int j = 0; j < remoteFilesList.size(); j++) {
++ String t = (String) remoteFilesList.get(j);
++ String t2 = t.toLowerCase();
++ if (t1.compareTo(t2) < 0) {
++ i = j;
++ break;
++ }
++ }
++ String f2 = fileName;
++ if (f2.length() < 24) {
++ for (int ik = f2.length(); ik < 24; ik++) {
++ f2 = f2 + " ";
++ }
++ }
++
++if (false) {
++System.out.println("fileName: " + f2);
++System.out.println("ftLastWriteTimeL: " + ftLastWriteTimeL);
++System.out.println("ftLastWriteTimeH: " + ftLastWriteTimeH);
++System.out.println("nFileSizeLow: " + nFileSizeLow);
++}
++
++ String s = f2 + " \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " \t\tSize: " + sizeStr(nFileSizeLow);
++ //s = fileName + " Lastmod: " + ftLastWriteTimeL + "/" + zogswap(ftLastWriteTimeL) + " Size: " + nFileSizeLow + "/" + zogswap(nFileSizeLow);
++ if (i >= 0) {
++ remoteFilesList.add(i, fileName);
++ remoteFilesListInfo.add(i, s);
++ } else {
++ remoteFilesList.add(fileName);
++ remoteFilesListInfo.add(s);
++ }
++// end runge/x11vnc
+ }
+
+ // a.add(fileName);
+@@ -1282,14 +1618,32 @@
+
+ // sf@2004
+ a.clear();
+- for (int i = 0; i < remoteDirsList.size(); i++)
++ b.clear();
++ for (int i = 0; i < remoteDirsList.size(); i++) {
+ a.add(remoteDirsList.get(i));
+- for (int i = 0; i < remoteFilesList.size(); i++)
++ b.add(remoteDirsListInfo.get(i));
++ }
++ for (int i = 0; i < remoteFilesList.size(); i++) {
+ a.add(remoteFilesList.get(i));
++
++ b.add(remoteFilesListInfo.get(i));
++ }
+ remoteDirsList.clear();
++ remoteDirsListInfo.clear();
+ remoteFilesList.clear();
++ remoteFilesListInfo.clear();
+
+- viewer.ftp.printDirectory(a);
++// begin runge/x11vnc
++ // Hack for double listing at startup... probably libvncserver bug..
++ readServerDriveListCnt++;
++ if (readServerDriveListCnt == 2) {
++ if (System.currentTimeMillis() - readServerDriveListTime < 2000) {
++//System.out.println("readServerDriveListCnt skip " + readServerDriveListCnt);
++ return;
++ }
++ }
++// end runge/x11vnc
++ viewer.ftp.printDirectory(a, b);
+ }
+
+ //Internally used to signify the drive requested is not ready
+@@ -1299,6 +1653,8 @@
+ System.out.println("Remote Drive unavailable");
+ viewer.ftp.connectionStatus.setText(" > WARNING - Remote Drive unavailable (possibly restricted access or media not present)");
+ viewer.ftp.remoteStatus.setText("WARNING: Remote Drive unavailable");
++ viewer.ftp.historyComboBox.insertItemAt(new String(" > WARNING: Remote Drive unavailable."), 0);
++ viewer.ftp.historyComboBox.setSelectedIndex(0);
+ }
+
+ //Call this method to request the list of drives on the server.
+@@ -1306,12 +1662,11 @@
+ {
+ try
+ {
+- viewer.rfb.writeRfbFileTransferMsg(
+- RfbProto.rfbDirContentRequest,
+- RfbProto.rfbRDrivesList,
+- 0,
+- 0,
+- null);
++ viewer.rfb.writeRfbFileTransferMsg(RfbProto.rfbDirContentRequest, RfbProto.rfbRDrivesList, 0, 0, null);
++// begin runge/x11vnc
++ readServerDriveListCnt = 0;
++ readServerDriveListTime = System.currentTimeMillis();
++// end runge/x11vnc
+ }
+ catch (IOException e)
+ {
+@@ -1355,21 +1710,21 @@
+ int h,
+ boolean incremental)
+ throws IOException {
+- if (!viewer.ftp.isVisible()) {
+- byte[] b = new byte[10];
++ if (!viewer.ftp.isVisible()) {
++ byte[] b = new byte[10];
+
+- b[0] = (byte) FramebufferUpdateRequest;
+- b[1] = (byte) (incremental ? 1 : 0);
+- b[2] = (byte) ((x >> 8) & 0xff);
+- b[3] = (byte) (x & 0xff);
+- b[4] = (byte) ((y >> 8) & 0xff);
+- b[5] = (byte) (y & 0xff);
+- b[6] = (byte) ((w >> 8) & 0xff);
+- b[7] = (byte) (w & 0xff);
+- b[8] = (byte) ((h >> 8) & 0xff);
+- b[9] = (byte) (h & 0xff);
++ b[0] = (byte) FramebufferUpdateRequest;
++ b[1] = (byte) (incremental ? 1 : 0);
++ b[2] = (byte) ((x >> 8) & 0xff);
++ b[3] = (byte) (x & 0xff);
++ b[4] = (byte) ((y >> 8) & 0xff);
++ b[5] = (byte) (y & 0xff);
++ b[6] = (byte) ((w >> 8) & 0xff);
++ b[7] = (byte) (w & 0xff);
++ b[8] = (byte) ((h >> 8) & 0xff);
++ b[9] = (byte) (h & 0xff);
+
+- os.write(b);
++ os.write(b);
+ }
+ }
+
+@@ -1482,7 +1837,13 @@
+ b[6] = (byte) ((text.length() >> 8) & 0xff);
+ b[7] = (byte) (text.length() & 0xff);
+
+- System.arraycopy(text.getBytes(), 0, b, 8, text.length());
++ if (false && max_char(text) > 255) {
++ System.arraycopy(text.getBytes("UTF-8"), 0, b, 8, text.length());
++ } else if (max_char(text) > 127) {
++ System.arraycopy(text.getBytes("ISO-8859-1"), 0, b, 8, text.length());
++ } else {
++ System.arraycopy(text.getBytes(), 0, b, 8, text.length());
++ }
+
+ os.write(b);
+ // }
+@@ -1506,6 +1867,37 @@
+ final static int META_MASK = InputEvent.META_MASK;
+ final static int ALT_MASK = InputEvent.ALT_MASK;
+
++ void writeWheelEvent(MouseWheelEvent evt) throws IOException {
++ eventBufLen = 0;
++
++ int x = evt.getX();
++ int y = evt.getY();
++
++ if (x < 0) x = 0;
++ if (y < 0) y = 0;
++
++ int ptrmask;
++
++ int clicks = evt.getWheelRotation();
++ System.out.println("writeWheelEvent: clicks: " + clicks);
++ if (clicks > 0) {
++ ptrmask = 16;
++ } else if (clicks < 0) {
++ ptrmask = 8;
++ } else {
++ return;
++ }
++
++ eventBuf[eventBufLen++] = (byte) PointerEvent;
++ eventBuf[eventBufLen++] = (byte) ptrmask;
++ eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff);
++ eventBuf[eventBufLen++] = (byte) (x & 0xff);
++ eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff);
++ eventBuf[eventBufLen++] = (byte) (y & 0xff);
++
++ os.write(eventBuf, 0, eventBufLen);
++ }
++
+ //
+ // Write a pointer event message. We may need to send modifier key events
+ // around it to set the correct modifier state.
+@@ -1610,6 +2002,21 @@
+
+ boolean down = (evt.getID() == KeyEvent.KEY_PRESSED);
+
++ if (viewer.debugKeyboard) {
++ System.out.println("----------------------------------------");
++ System.out.println("evt.getKeyChar: " + evt.getKeyChar());
++ System.out.println("getKeyText: " + KeyEvent.getKeyText(evt.getKeyCode()));
++ System.out.println("evt.getKeyCode: " + evt.getKeyCode());
++ System.out.println("evt.getID: " + evt.getID());
++ System.out.println("evt.getKeyLocation: " + evt.getKeyLocation());
++ System.out.println("evt.isActionKey: " + evt.isActionKey());
++ System.out.println("evt.isControlDown: " + evt.isControlDown());
++ System.out.println("evt.getModifiers: " + evt.getModifiers());
++ System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers()));
++ System.out.println("evt.paramString: " + evt.paramString());
++ }
++
++
+ int key;
+ if (evt.isActionKey()) {
+
+@@ -1685,6 +2092,9 @@
+ default :
+ return;
+ }
++ if (key == 0xffc2 && viewer.mapF5_to_atsign) {
++ key = 0x40;
++ }
+
+ } else {
+
+@@ -1794,6 +2204,16 @@
+ int oldModifiers = 0;
+
+ void writeModifierKeyEvents(int newModifiers) {
++ if(viewer.forbid_Ctrl_Alt) {
++ if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) {
++ int orig = newModifiers;
++ newModifiers &= ~ALT_MASK;
++ newModifiers &= ~CTRL_MASK;
++ if (viewer.debugKeyboard) {
++ System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers);
++ }
++ }
++ }
+ if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK))
+ writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0);
+
+diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+--- JavaViewer.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500
++++ JavaViewer/SSLSocketToMe.java 2010-07-10 19:18:06.000000000 -0400
+@@ -0,0 +1,2067 @@
++/*
++ * SSLSocketToMe.java: add SSL encryption to Java VNC Viewer.
++ *
++ * Copyright (c) 2006 Karl J. Runge <runge@karlrunge.com>
++ * 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; version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This software is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this software; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++ * USA.
++ *
++ */
++
++import java.net.*;
++import java.io.*;
++import javax.net.ssl.*;
++import java.util.*;
++
++import java.security.*;
++import java.security.cert.*;
++import java.security.spec.*;
++import java.security.cert.Certificate;
++import java.security.cert.CertificateFactory;
++
++import java.awt.*;
++import java.awt.event.*;
++
++public class SSLSocketToMe {
++
++ /* basic member data: */
++ String host;
++ int port;
++ VncViewer viewer;
++
++ boolean debug = true;
++ boolean debug_certs = false;
++
++ /* sockets */
++ SSLSocket socket = null;
++ SSLSocketFactory factory;
++
++ /* fallback for Proxy connection */
++ boolean proxy_in_use = false;
++ boolean proxy_failure = false;
++ public DataInputStream is = null;
++ public OutputStream os = null;
++
++ /* strings from user WRT proxy: */
++ String proxy_auth_string = null;
++ String proxy_dialog_host = null;
++ int proxy_dialog_port = 0;
++
++ Socket proxySock;
++ DataInputStream proxy_is;
++ OutputStream proxy_os;
++
++ /* trust contexts */
++ SSLContext trustloc_ctx;
++ SSLContext trustall_ctx;
++ SSLContext trustsrv_ctx;
++ SSLContext trusturl_ctx;
++ SSLContext trustone_ctx;
++
++ /* corresponding trust managers */
++ TrustManager[] trustAllCerts;
++ TrustManager[] trustSrvCert;
++ TrustManager[] trustUrlCert;
++ TrustManager[] trustOneCert;
++
++ /* client-side SSL auth key (oneTimeKey=...) */
++ KeyManager[] mykey = null;
++
++ boolean user_wants_to_see_cert = true;
++ String cert_fail = null;
++
++ /* cert(s) we retrieve from Web server, VNC server, or serverCert param: */
++ java.security.cert.Certificate[] trustallCerts = null;
++ java.security.cert.Certificate[] trustsrvCerts = null;
++ java.security.cert.Certificate[] trusturlCerts = null;
++
++ /* utility to decode hex oneTimeKey=... and serverCert=... */
++ byte[] hex2bytes(String s) {
++ byte[] bytes = new byte[s.length()/2];
++ for (int i=0; i<s.length()/2; i++) {
++ int j = 2*i;
++ try {
++ int val = Integer.parseInt(s.substring(j, j+2), 16);
++ if (val > 127) {
++ val -= 256;
++ }
++ Integer I = new Integer(val);
++ bytes[i] = Byte.decode(I.toString()).byteValue();
++
++ } catch (Exception e) {
++ ;
++ }
++ }
++ return bytes;
++ }
++
++ SSLSocketToMe(String h, int p, VncViewer v) throws Exception {
++ host = h;
++ port = p;
++ viewer = v;
++
++ debug_certs = v.debugCerts;
++
++ /* we will first try default factory for certification: */
++
++ factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
++
++ dbg("SSL startup: " + host + " " + port);
++
++
++ /* create trust managers to be used if initial handshake fails: */
++
++ trustAllCerts = new TrustManager[] {
++ /*
++ * this one accepts everything. Only used if user
++ * has disabled checking (trustAllVncCerts=yes)
++ * or when we grab the cert to show it to them in
++ * a dialog and ask them to manually verify/accept it.
++ */
++ new X509TrustManager() {
++ public java.security.cert.X509Certificate[]
++ getAcceptedIssuers() {
++ return null;
++ }
++ public void checkClientTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) {
++ /* empty */
++ }
++ public void checkServerTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) {
++ /* empty */
++ dbg("ALL: an untrusted connect to grab cert.");
++ }
++ }
++ };
++
++ trustUrlCert = new TrustManager[] {
++ /*
++ * this one accepts only the retrieved server
++ * cert by SSLSocket by this applet and stored in
++ * trusturlCerts.
++ */
++ new X509TrustManager() {
++ public java.security.cert.X509Certificate[]
++ getAcceptedIssuers() {
++ return null;
++ }
++ public void checkClientTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) throws CertificateException {
++ throw new CertificateException("No Clients (URL)");
++ }
++ public void checkServerTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) throws CertificateException {
++ /* we want to check 'certs' against 'trusturlCerts' */
++ if (trusturlCerts == null) {
++ throw new CertificateException(
++ "No Trust url Certs array.");
++ }
++ if (trusturlCerts.length < 1) {
++ throw new CertificateException(
++ "No Trust url Certs.");
++ }
++ if (certs == null) {
++ throw new CertificateException(
++ "No this-certs array.");
++ }
++ if (certs.length < 1) {
++ throw new CertificateException(
++ "No this-certs Certs.");
++ }
++ if (certs.length != trusturlCerts.length) {
++ throw new CertificateException(
++ "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length);
++ }
++ boolean ok = true;
++ for (int i = 0; i < certs.length; i++) {
++ if (! trusturlCerts[i].equals(certs[i])) {
++ ok = false;
++ dbg("URL: cert mismatch at i=" + i);
++ dbg("URL: cert mismatch cert" + certs[i]);
++ dbg("URL: cert mismatch url" + trusturlCerts[i]);
++ if (cert_fail == null) {
++ cert_fail = "cert-mismatch";
++ }
++ }
++ if (debug_certs) {
++ dbg("\n***********************************************");
++ dbg("URL: cert info at i=" + i);
++ dbg("URL: cert info cert" + certs[i]);
++ dbg("===============================================");
++ dbg("URL: cert info url" + trusturlCerts[i]);
++ dbg("***********************************************");
++ }
++ }
++ if (!ok) {
++ throw new CertificateException(
++ "Server Cert Chain != URL Cert Chain.");
++ }
++ dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1));
++ }
++ }
++ };
++
++ trustSrvCert = new TrustManager[] {
++ /*
++ * this one accepts cert given to us in the serverCert
++ * Applet Parameter we were started with. It is
++ * currently a fatal error if the VNC Server's cert
++ * doesn't match it.
++ */
++ new X509TrustManager() {
++ public java.security.cert.X509Certificate[]
++ getAcceptedIssuers() {
++ return null;
++ }
++ public void checkClientTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) throws CertificateException {
++ throw new CertificateException("No Clients (SRV)");
++ }
++ public void checkServerTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) throws CertificateException {
++ /* we want to check 'certs' against 'trustsrvCerts' */
++ if (trustsrvCerts == null) {
++ throw new CertificateException(
++ "No Trust srv Certs array.");
++ }
++ if (trustsrvCerts.length < 1) {
++ throw new CertificateException(
++ "No Trust srv Certs.");
++ }
++ if (certs == null) {
++ throw new CertificateException(
++ "No this-certs array.");
++ }
++ if (certs.length < 1) {
++ throw new CertificateException(
++ "No this-certs Certs.");
++ }
++ if (certs.length != trustsrvCerts.length) {
++ throw new CertificateException(
++ "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length);
++ }
++ boolean ok = true;
++ for (int i = 0; i < certs.length; i++) {
++ if (! trustsrvCerts[i].equals(certs[i])) {
++ ok = false;
++ dbg("SRV: cert mismatch at i=" + i);
++ dbg("SRV: cert mismatch cert" + certs[i]);
++ dbg("SRV: cert mismatch srv" + trustsrvCerts[i]);
++ if (cert_fail == null) {
++ cert_fail = "server-cert-mismatch";
++ }
++ }
++ if (debug_certs) {
++ dbg("\n***********************************************");
++ dbg("SRV: cert info at i=" + i);
++ dbg("SRV: cert info cert" + certs[i]);
++ dbg("===============================================");
++ dbg("SRV: cert info srv" + trustsrvCerts[i]);
++ dbg("***********************************************");
++ }
++ }
++ if (!ok) {
++ throw new CertificateException(
++ "Server Cert Chain != serverCert Applet Parameter Cert Chain.");
++ }
++ dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1));
++ }
++ }
++ };
++
++ trustOneCert = new TrustManager[] {
++ /*
++ * this one accepts only the retrieved server
++ * cert by SSLSocket by this applet we stored in
++ * trustallCerts that user has accepted or applet
++ * parameter trustAllVncCerts=yes is set. This is
++ * for when we reconnect after the user has manually
++ * accepted the trustall cert in the dialog (or set
++ * trustAllVncCerts=yes applet param.)
++ */
++ new X509TrustManager() {
++ public java.security.cert.X509Certificate[]
++ getAcceptedIssuers() {
++ return null;
++ }
++ public void checkClientTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) throws CertificateException {
++ throw new CertificateException("No Clients (ONE)");
++ }
++ public void checkServerTrusted(
++ java.security.cert.X509Certificate[] certs,
++ String authType) throws CertificateException {
++ /* we want to check 'certs' against 'trustallCerts' */
++ if (trustallCerts == null) {
++ throw new CertificateException(
++ "No Trust All Server Certs array.");
++ }
++ if (trustallCerts.length < 1) {
++ throw new CertificateException(
++ "No Trust All Server Certs.");
++ }
++ if (certs == null) {
++ throw new CertificateException(
++ "No this-certs array.");
++ }
++ if (certs.length < 1) {
++ throw new CertificateException(
++ "No this-certs Certs.");
++ }
++ if (certs.length != trustallCerts.length) {
++ throw new CertificateException(
++ "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length);
++ }
++ boolean ok = true;
++ for (int i = 0; i < certs.length; i++) {
++ if (! trustallCerts[i].equals(certs[i])) {
++ ok = false;
++ dbg("ONE: cert mismatch at i=" + i);
++ dbg("ONE: cert mismatch cert" + certs[i]);
++ dbg("ONE: cert mismatch all" + trustallCerts[i]);
++ }
++ if (debug_certs) {
++ dbg("\n***********************************************");
++ dbg("ONE: cert info at i=" + i);
++ dbg("ONE: cert info cert" + certs[i]);
++ dbg("===============================================");
++ dbg("ONE: cert info all" + trustallCerts[i]);
++ dbg("***********************************************");
++ }
++ }
++ if (!ok) {
++ throw new CertificateException(
++ "Server Cert Chain != TRUSTALL Cert Chain.");
++ }
++ dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1));
++ }
++ }
++ };
++
++ /*
++ * The above TrustManagers are used:
++ *
++ * 1) to retrieve the server cert in case of failure to
++ * display it to the user in a dialog.
++ * 2) to subsequently connect to the server if user agrees.
++ */
++
++ /*
++ * build oneTimeKey cert+key if supplied in applet parameter:
++ */
++ if (viewer.oneTimeKey != null && viewer.oneTimeKey.equals("PROMPT")) {
++ ClientCertDialog d = new ClientCertDialog();
++ viewer.oneTimeKey = d.queryUser();
++ }
++ if (viewer.oneTimeKey != null && viewer.oneTimeKey.indexOf(",") > 0) {
++ int idx = viewer.oneTimeKey.indexOf(",");
++
++ String onetimekey = viewer.oneTimeKey.substring(0, idx);
++ byte[] key = hex2bytes(onetimekey);
++ String onetimecert = viewer.oneTimeKey.substring(idx+1);
++ byte[] cert = hex2bytes(onetimecert);
++
++ KeyFactory kf = KeyFactory.getInstance("RSA");
++ PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key );
++ PrivateKey ff = kf.generatePrivate (keysp);
++ if (debug_certs) {
++ dbg("one time key " + ff);
++ }
++
++ CertificateFactory cf = CertificateFactory.getInstance("X.509");
++ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
++ Certificate[] certs = new Certificate[c.toArray().length];
++ if (c.size() == 1) {
++ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
++ if (debug_certs) {
++ dbg("one time cert" + tmpcert);
++ }
++ certs[0] = tmpcert;
++ } else {
++ certs = (Certificate[]) c.toArray();
++ }
++
++ KeyStore ks = KeyStore.getInstance("JKS");
++ ks.load(null, null);
++ ks.setKeyEntry("onetimekey", ff, "".toCharArray(), certs);
++ String da = KeyManagerFactory.getDefaultAlgorithm();
++ KeyManagerFactory kmf = KeyManagerFactory.getInstance(da);
++ kmf.init(ks, "".toCharArray());
++
++ mykey = kmf.getKeyManagers();
++ }
++
++ /*
++ * build serverCert cert if supplied in applet parameter:
++ */
++ if (viewer.serverCert != null) {
++ CertificateFactory cf = CertificateFactory.getInstance("X.509");
++ byte[] cert = hex2bytes(viewer.serverCert);
++ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
++ trustsrvCerts = new Certificate[c.toArray().length];
++ if (c.size() == 1) {
++ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
++ trustsrvCerts[0] = tmpcert;
++ } else {
++ trustsrvCerts = (Certificate[]) c.toArray();
++ }
++ }
++
++ /* the trust loc certs context: */
++ try {
++ trustloc_ctx = SSLContext.getInstance("SSL");
++
++ /*
++ * below is a failed attempt to get jvm's default
++ * trust manager using null (below) makes it so
++ * for HttpsURLConnection the server cannot be
++ * verified (no prompting.)
++ */
++ if (false) {
++ boolean didit = false;
++ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
++ tmf.init((KeyStore) null);
++ TrustManager [] tml = tmf.getTrustManagers();
++ for (int i = 0; i < tml.length; i++) {
++ TrustManager tm = tml[i];
++ if (tm instanceof X509TrustManager) {
++ TrustManager tm1[] = new TrustManager[1];
++ tm1[0] = tm;
++ trustloc_ctx.init(mykey, tm1, null);
++ didit = true;
++ break;
++ }
++ }
++ if (!didit) {
++ trustloc_ctx.init(mykey, null, null);
++ }
++ } else {
++ /* we have to set trust manager to null */
++ trustloc_ctx.init(mykey, null, null);
++ }
++
++ } catch (Exception e) {
++ String msg = "SSL trustloc_ctx FAILED.";
++ dbg(msg);
++ throw new Exception(msg);
++ }
++
++ /* the trust all certs context: */
++ try {
++ trustall_ctx = SSLContext.getInstance("SSL");
++ trustall_ctx.init(mykey, trustAllCerts, new
++ java.security.SecureRandom());
++
++ } catch (Exception e) {
++ String msg = "SSL trustall_ctx FAILED.";
++ dbg(msg);
++ throw new Exception(msg);
++ }
++
++ /* the trust url certs context: */
++ try {
++ trusturl_ctx = SSLContext.getInstance("SSL");
++ trusturl_ctx.init(mykey, trustUrlCert, new
++ java.security.SecureRandom());
++
++ } catch (Exception e) {
++ String msg = "SSL trusturl_ctx FAILED.";
++ dbg(msg);
++ throw new Exception(msg);
++ }
++
++ /* the trust srv certs context: */
++ try {
++ trustsrv_ctx = SSLContext.getInstance("SSL");
++ trustsrv_ctx.init(mykey, trustSrvCert, new
++ java.security.SecureRandom());
++
++ } catch (Exception e) {
++ String msg = "SSL trustsrv_ctx FAILED.";
++ dbg(msg);
++ throw new Exception(msg);
++ }
++
++ /* the trust the one cert from server context: */
++ try {
++ trustone_ctx = SSLContext.getInstance("SSL");
++ trustone_ctx.init(mykey, trustOneCert, new
++ java.security.SecureRandom());
++
++ } catch (Exception e) {
++ String msg = "SSL trustone_ctx FAILED.";
++ dbg(msg);
++ throw new Exception(msg);
++ }
++ }
++
++ /*
++ * we call this early on to 1) check for a proxy, 2) grab
++ * Browser/JVM accepted HTTPS cert.
++ */
++ public void check_for_proxy_and_grab_vnc_server_cert() {
++
++ trusturlCerts = null;
++ proxy_in_use = false;
++
++ if (viewer.ignoreProxy) {
++ /* applet param says skip it. */
++ /* the downside is we do not set trusturlCerts for comparison later... */
++ /* nor do we autodetect x11vnc for GET=1. */
++ return;
++ }
++
++ dbg("------------------------------------------------");
++ dbg("Into check_for_proxy_and_grab_vnc_server_cert():");
++
++ dbg("TRYING HTTPS:");
++ String ustr = "https://" + host + ":";
++ if (viewer.httpsPort != null) {
++ ustr += viewer.httpsPort;
++ } else {
++ ustr += port;
++ }
++ ustr += viewer.urlPrefix + "/check.https.proxy.connection";
++ dbg("ustr is: " + ustr);
++
++ try {
++ /* prepare for an HTTPS URL connection to host:port */
++ URL url = new URL(ustr);
++ HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
++
++ if (mykey != null) {
++ /* with oneTimeKey (mykey) we can't use the default SSL context */
++ if (trustsrvCerts != null) {
++ dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert.");
++ https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory());
++ } else if (trustloc_ctx != null) {
++ dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert.");
++ https.setSSLSocketFactory(trustloc_ctx.getSocketFactory());
++ }
++ }
++
++ https.setUseCaches(false);
++ https.setRequestMethod("GET");
++ https.setRequestProperty("Pragma", "No-Cache");
++ https.setRequestProperty("Proxy-Connection", "Keep-Alive");
++ https.setDoInput(true);
++
++ dbg("trying https.connect()");
++ https.connect();
++
++ dbg("trying https.getServerCertificates()");
++ trusturlCerts = https.getServerCertificates();
++
++ if (trusturlCerts == null) {
++ dbg("set trusturlCerts to null!");
++ } else {
++ dbg("set trusturlCerts to non-null");
++ }
++
++ if (https.usingProxy()) {
++ proxy_in_use = true;
++ dbg("An HTTPS proxy is in use. There may be connection problems.");
++ }
++
++ dbg("trying https.getContent()");
++ Object output = https.getContent();
++ dbg("trying https.disconnect()");
++ https.disconnect();
++ if (! viewer.GET) {
++ String header = https.getHeaderField("VNC-Server");
++ if (header != null && header.startsWith("x11vnc")) {
++ dbg("detected x11vnc server (1), setting GET=1");
++ viewer.GET = true;
++ }
++ }
++
++ } catch(Exception e) {
++ dbg("HttpsURLConnection: " + e.getMessage());
++ }
++
++ if (proxy_in_use) {
++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
++ dbg("------------------------------------------------");
++ return;
++ } else if (trusturlCerts != null && !viewer.forceProxy) {
++ /* Allow user to require HTTP check? use forceProxy for now. */
++ dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct.");
++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
++ dbg("------------------------------------------------");
++ return;
++ }
++
++ /*
++ * XXX need to remember scenario where this extra check
++ * gives useful info. User's Browser proxy settings?
++ */
++ dbg("TRYING HTTP:");
++ ustr = "http://" + host + ":" + port;
++ ustr += viewer.urlPrefix + "/index.vnc";
++ dbg("ustr is: " + ustr);
++
++ try {
++ /* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */
++ URL url = new URL(ustr);
++ HttpURLConnection http = (HttpURLConnection)
++ url.openConnection();
++
++ http.setUseCaches(false);
++ http.setRequestMethod("GET");
++ http.setRequestProperty("Pragma", "No-Cache");
++ http.setRequestProperty("Proxy-Connection", "Keep-Alive");
++ http.setDoInput(true);
++
++ dbg("trying http.connect()");
++ http.connect();
++
++ if (http.usingProxy()) {
++ proxy_in_use = true;
++ dbg("An HTTP proxy is in use. There may be connection problems.");
++ }
++ dbg("trying http.getContent()");
++ Object output = http.getContent();
++ dbg("trying http.disconnect()");
++ http.disconnect();
++ if (! viewer.GET) {
++ String header = http.getHeaderField("VNC-Server");
++ if (header != null && header.startsWith("x11vnc")) {
++ dbg("detected x11vnc server (2), setting GET=1");
++ viewer.GET = true;
++ }
++ }
++ } catch(Exception e) {
++ dbg("HttpURLConnection: " + e.getMessage());
++ }
++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
++ dbg("------------------------------------------------");
++ }
++
++ public Socket connectSock() throws IOException {
++ /*
++ * first try a https connection to detect a proxy, and
++ * grab the VNC server cert at the same time:
++ */
++ check_for_proxy_and_grab_vnc_server_cert();
++
++ boolean srv_cert = false;
++
++ if (trustsrvCerts != null) {
++ /* applet parameter suppled serverCert */
++ dbg("viewer.trustSrvCert-0 using trustsrv_ctx");
++ factory = trustsrv_ctx.getSocketFactory();
++ srv_cert = true;
++ } else if (viewer.trustAllVncCerts) {
++ /* trust all certs (no checking) */
++ dbg("viewer.trustAllVncCerts-0 using trustall_ctx");
++ factory = trustall_ctx.getSocketFactory();
++ } else if (trusturlCerts != null) {
++ /* trust certs the Browser/JVM accepted in check_for_proxy... */
++ dbg("using trusturl_ctx");
++ factory = trusturl_ctx.getSocketFactory();
++ } else {
++ /* trust the local defaults */
++ dbg("using trustloc_ctx");
++ factory = trustloc_ctx.getSocketFactory();
++ }
++
++ socket = null;
++
++ try {
++ if (proxy_in_use && viewer.forceProxy) {
++ throw new Exception("forcing proxy (forceProxy)");
++ } else if (viewer.CONNECT != null) {
++ throw new Exception("forcing CONNECT");
++ }
++
++ int timeout = 6;
++ if (timeout > 0) {
++ socket = (SSLSocket) factory.createSocket();
++ InetSocketAddress inetaddr = new InetSocketAddress(host, port);
++ dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port);
++ socket.connect(inetaddr, timeout * 1000);
++ } else {
++ socket = (SSLSocket) factory.createSocket(host, port);
++ }
++
++ } catch (Exception esock) {
++ dbg("socket error: " + esock.getMessage());
++ if (proxy_in_use || viewer.CONNECT != null) {
++ proxy_failure = true;
++ if (proxy_in_use) {
++ dbg("HTTPS proxy in use. Trying to go with it.");
++ } else {
++ dbg("viewer.CONNECT reverse proxy in use. Trying to go with it.");
++ }
++ try {
++ socket = proxy_socket(factory);
++ } catch (Exception e) {
++ dbg("proxy_socket error: " + e.getMessage());
++ }
++ } else {
++ /* n.b. socket is left in error state to cause ex. below. */
++ }
++ }
++
++ try {
++ socket.startHandshake();
++
++ dbg("The Server Connection Verified OK on 1st try.");
++
++ java.security.cert.Certificate[] currentTrustedCerts;
++ BrowserCertsDialog bcd;
++
++ SSLSession sess = socket.getSession();
++ currentTrustedCerts = sess.getPeerCertificates();
++
++ if (viewer.trustAllVncCerts) {
++ dbg("viewer.trustAllVncCerts-1 keeping socket.");
++ } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) {
++ try {
++ socket.close();
++ } catch (Exception e) {
++ dbg("socket is grumpy.");
++ }
++ socket = null;
++ throw new SSLHandshakeException("no current certs");
++ }
++
++ String serv = "";
++ try {
++ CertInfo ci = new CertInfo(currentTrustedCerts[0]);
++ serv = ci.get_certinfo("CN");
++ } catch (Exception e) {
++ ;
++ }
++
++ if (viewer.trustAllVncCerts) {
++ dbg("viewer.trustAllVncCerts-2 skipping browser certs dialog");
++ user_wants_to_see_cert = false;
++ } else if (viewer.serverCert != null && trustsrvCerts != null) {
++ dbg("viewer.serverCert-1 skipping browser certs dialog");
++ user_wants_to_see_cert = false;
++ } else if (viewer.trustUrlVncCert) {
++ dbg("viewer.trustUrlVncCert-1 skipping browser certs dialog");
++ user_wants_to_see_cert = false;
++ } else {
++ /* have a dialog with the user: */
++ bcd = new BrowserCertsDialog(serv, host + ":" + port);
++ dbg("browser certs dialog begin.");
++ bcd.queryUser();
++ dbg("browser certs dialog finished.");
++
++ if (bcd.showCertDialog) {
++ String msg = "user wants to see cert";
++ dbg(msg);
++ user_wants_to_see_cert = true;
++ if (cert_fail == null) {
++ cert_fail = "user-view";
++ }
++ throw new SSLHandshakeException(msg);
++ } else {
++ user_wants_to_see_cert = false;
++ dbg("browser certs dialog: user said yes, accept it");
++ }
++ }
++
++ } catch (SSLHandshakeException eh) {
++ dbg("SSLHandshakeException: could not automatically verify Server.");
++ dbg("msg: " + eh.getMessage());
++
++
++ /* send a cleanup string just in case: */
++ String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n";
++
++ try {
++ OutputStream os = socket.getOutputStream();
++ os.write(getoutstr.getBytes());
++ socket.close();
++ } catch (Exception e) {
++ dbg("socket is grumpy!");
++ }
++
++ /* reload */
++
++ socket = null;
++
++ String reason = null;
++
++ if (srv_cert) {
++ /* for serverCert usage we make this a fatal error. */
++ throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'");
++ /* see below in TrustDialog were we describe this case to user anyway */
++ }
++
++ /*
++ * Reconnect, trusting any cert, so we can grab
++ * the cert to show it to the user in a dialog
++ * for him to manually accept. This connection
++ * is not used for anything else.
++ */
++ factory = trustall_ctx.getSocketFactory();
++ if (proxy_failure) {
++ socket = proxy_socket(factory);
++ } else {
++ socket = (SSLSocket) factory.createSocket(host, port);
++ }
++
++ if (debug_certs) {
++ dbg("trusturlCerts: " + trusturlCerts);
++ dbg("trustsrvCerts: " + trustsrvCerts);
++ }
++ if (trusturlCerts == null && cert_fail == null) {
++ cert_fail = "missing-certs";
++ }
++
++ try {
++ socket.startHandshake();
++
++ dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK.");
++
++ /* grab the cert: */
++ try {
++ SSLSession sess = socket.getSession();
++ trustallCerts = sess.getPeerCertificates();
++ } catch (Exception e) {
++ throw new Exception("Could not get " +
++ "Peer Certificate");
++ }
++ if (debug_certs) {
++ dbg("trustallCerts: " + trustallCerts);
++ }
++
++ if (viewer.trustAllVncCerts) {
++ dbg("viewer.trustAllVncCerts-3. skipping dialog, trusting everything.");
++ } else if (! browser_cert_match()) {
++ /*
++ * close socket now, we will reopen after
++ * dialog if user agrees to use the cert.
++ */
++ try {
++ OutputStream os = socket.getOutputStream();
++ os.write(getoutstr.getBytes());
++ socket.close();
++ } catch (Exception e) {
++ dbg("socket is grumpy!!");
++ }
++ socket = null;
++
++ /* dialog with user to accept cert or not: */
++
++ TrustDialog td= new TrustDialog(host, port,
++ trustallCerts);
++
++ if (cert_fail == null) {
++ ;
++ } else if (cert_fail.equals("user-view")) {
++ reason = "Reason for this Dialog:\n\n"
++ + " You Asked to View the Certificate.";
++ } else if (cert_fail.equals("server-cert-mismatch")) {
++ /* this is now fatal error, see above. */
++ reason = "Reason for this Dialog:\n\n"
++ + " The VNC Server's Certificate does not match the Certificate\n"
++ + " specified in the supplied 'serverCert' Applet Parameter.";
++ } else if (cert_fail.equals("cert-mismatch")) {
++ reason = "Reason for this Dialog:\n\n"
++ + " The VNC Server's Certificate does not match the Website's\n"
++ + " HTTPS Certificate (that you previously accepted; either\n"
++ + " manually or automatically via Certificate Authority.)";
++ } else if (cert_fail.equals("missing-certs")) {
++ reason = "Reason for this Dialog:\n\n"
++ + " Not all Certificates could be obtained to check.";
++ }
++
++ if (! td.queryUser(reason)) {
++ String msg = "User decided against it.";
++ dbg(msg);
++ throw new IOException(msg);
++ }
++ }
++
++ } catch (Exception ehand2) {
++ dbg("** Could not TrustAll Verify Server!");
++
++ throw new IOException(ehand2.getMessage());
++ }
++
++ /* reload again: */
++
++ if (socket != null) {
++ try {
++ socket.close();
++ } catch (Exception e) {
++ dbg("socket is grumpy!!!");
++ }
++ socket = null;
++ }
++
++ /*
++ * Now connect a 3rd time, using the cert
++ * retrieved during connection 2 (sadly, that
++ * the user likely blindly agreed to...)
++ */
++
++ factory = trustone_ctx.getSocketFactory();
++ if (proxy_failure) {
++ socket = proxy_socket(factory);
++ } else {
++ socket = (SSLSocket) factory.createSocket(host, port);
++ }
++
++ try {
++ socket.startHandshake();
++ dbg("TrustAll/TrustOne Server Connection Verified #3.");
++
++ } catch (Exception ehand3) {
++ dbg("** Could not TrustAll/TrustOne Verify Server #3.");
++
++ throw new IOException(ehand3.getMessage());
++ }
++ }
++
++ /* we have socket (possibly null) at this point, so proceed: */
++
++ /* handle x11vnc GET=1, if applicable: */
++ if (socket != null && viewer.GET) {
++ String str = "GET ";
++ str += viewer.urlPrefix;
++ str += "/request.https.vnc.connection";
++ str += " HTTP/1.0\r\n";
++ str += "Pragma: No-Cache\r\n";
++ str += "\r\n";
++
++ System.out.println("sending: " + str);
++ OutputStream os = socket.getOutputStream();
++ String type = "os";
++
++ if (type == "os") {
++ os.write(str.getBytes());
++ os.flush();
++ System.out.println("used OutputStream");
++ } else if (type == "bs") {
++ BufferedOutputStream bs = new BufferedOutputStream(os);
++ bs.write(str.getBytes());
++ bs.flush();
++ System.out.println("used BufferedOutputStream");
++ } else if (type == "ds") {
++ DataOutputStream ds = new DataOutputStream(os);
++ ds.write(str.getBytes());
++ ds.flush();
++ System.out.println("used DataOutputStream");
++ }
++ if (false) {
++ String rep = "";
++ DataInputStream is = new DataInputStream(
++ new BufferedInputStream(socket.getInputStream(), 16384));
++ while (true) {
++ rep += readline(is);
++ if (rep.indexOf("\r\n\r\n") >= 0) {
++ break;
++ }
++ }
++ System.out.println("rep: " + rep);
++ }
++ }
++
++ dbg("SSL returning socket to caller.");
++ dbg("");
++
++ /* could be null, let caller handle that. */
++ return (Socket) socket;
++ }
++
++ boolean browser_cert_match() {
++ String msg = "Browser URL accept previously accepted cert";
++
++ if (user_wants_to_see_cert) {
++ return false;
++ }
++
++ if (viewer.serverCert != null || trustsrvCerts != null) {
++ if (cert_fail == null) {
++ cert_fail = "server-cert-mismatch";
++ }
++ }
++ if (trustallCerts != null && trusturlCerts != null) {
++ if (trustallCerts.length == trusturlCerts.length) {
++ boolean ok = true;
++ /* check toath trustallCerts (socket) equals trusturlCerts (browser) */
++ for (int i = 0; i < trusturlCerts.length; i++) {
++ if (! trustallCerts[i].equals(trusturlCerts[i])) {
++ dbg("BCM: cert mismatch at i=" + i);
++ dbg("BCM: cert mismatch url" + trusturlCerts[i]);
++ dbg("BCM: cert mismatch all" + trustallCerts[i]);
++ ok = false;
++ }
++ }
++ if (ok) {
++ System.out.println(msg);
++ if (cert_fail == null) {
++ cert_fail = "did-not-fail";
++ }
++ return true;
++ } else {
++ if (cert_fail == null) {
++ cert_fail = "cert-mismatch";
++ }
++ return false;
++ }
++ }
++ }
++ if (cert_fail == null) {
++ cert_fail = "missing-certs";
++ }
++ return false;
++ }
++
++ private void dbg(String s) {
++ if (debug) {
++ System.out.println(s);
++ }
++ }
++
++ private int gint(String s) {
++ int n = -1;
++ try {
++ Integer I = new Integer(s);
++ n = I.intValue();
++ } catch (Exception ex) {
++ return -1;
++ }
++ return n;
++ }
++
++ /* this will do the proxy CONNECT negotiation and hook us up. */
++
++ private void proxy_helper(String proxyHost, int proxyPort) {
++
++ boolean proxy_auth = false;
++ String proxy_auth_basic_realm = "";
++ String hp = host + ":" + port;
++ dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp);
++
++ /* we loop here a few times trying for the password case */
++ for (int k=0; k < 2; k++) {
++ dbg("proxy_in_use psocket: " + k);
++
++ if (proxySock != null) {
++ try {
++ proxySock.close();
++ } catch (Exception e) {
++ dbg("proxy socket is grumpy.");
++ }
++ }
++
++ proxySock = psocket(proxyHost, proxyPort);
++ if (proxySock == null) {
++ dbg("1-a sadly, returning a null socket");
++ return;
++ }
++
++ String req1 = "CONNECT " + hp + " HTTP/1.1\r\n"
++ + "Host: " + hp + "\r\n";
++
++ dbg("requesting via proxy: " + req1);
++
++ if (proxy_auth) {
++ if (proxy_auth_string == null) {
++ ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm);
++ pp.queryUser();
++ proxy_auth_string = pp.getAuth();
++ }
++ //dbg("auth1: " + proxy_auth_string);
++
++ String auth2 = Base64Coder.encodeString(proxy_auth_string);
++ //dbg("auth2: " + auth2);
++
++ req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n";
++ //dbg("req1: " + req1);
++
++ dbg("added Proxy-Authorization: Basic ... to request");
++ }
++ req1 += "\r\n";
++
++ try {
++ proxy_os.write(req1.getBytes());
++ String reply = readline(proxy_is);
++
++ dbg("proxy replied: " + reply.trim());
++
++ if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) {
++ proxy_auth = true;
++ proxySock.close();
++ } else if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) {
++ proxySock.close();
++ proxySock = psocket(proxyHost, proxyPort);
++ if (proxySock == null) {
++ dbg("2-a sadly, returning a null socket");
++ return;
++ }
++ }
++ } catch(Exception e) {
++ dbg("some proxy socket problem: " + e.getMessage());
++ }
++
++ /* read the rest of the HTTP headers */
++ while (true) {
++ String line = readline(proxy_is);
++ dbg("proxy line: " + line.trim());
++ if (proxy_auth) {
++ String uc = line.toLowerCase();
++ if (uc.indexOf("proxy-authenticate:") == 0) {
++ if (uc.indexOf(" basic ") >= 0) {
++ int idx = uc.indexOf(" realm");
++ if (idx >= 0) {
++ proxy_auth_basic_realm = uc.substring(idx+1);
++ }
++ }
++ }
++ }
++ if (line.equals("\r\n") || line.equals("\n")) {
++ break;
++ }
++ }
++ if (!proxy_auth || proxy_auth_basic_realm.equals("")) {
++ /* we only try once for the non-password case: */
++ break;
++ }
++ }
++ }
++
++ public SSLSocket proxy_socket(SSLSocketFactory factory) {
++ Properties props = null;
++ String proxyHost = null;
++ int proxyPort = 0;
++ String proxyHost_nossl = null;
++ int proxyPort_nossl = 0;
++ String str;
++
++ /* see if we can guess the proxy info from Properties: */
++ try {
++ props = System.getProperties();
++ } catch (Exception e) {
++ /* sandboxed applet might not be able to read it. */
++ dbg("props failed: " + e.getMessage());
++ }
++ if (viewer.proxyHost != null) {
++ dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters.");
++ proxyHost = viewer.proxyHost;
++ if (viewer.proxyPort != null) {
++ proxyPort = gint(viewer.proxyPort);
++ } else {
++ proxyPort = 8080;
++ }
++
++ } else if (props != null) {
++ dbg("\n---------------\nAll props:");
++ props.list(System.out);
++ dbg("\n---------------\n\n");
++
++ /* scrape throught properties looking for proxy info: */
++
++ for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) {
++ String s = (String) e.nextElement();
++ String v = System.getProperty(s);
++ String s2 = s.toLowerCase();
++ String v2 = v.toLowerCase();
++
++ if (s2.indexOf("proxy.https.host") >= 0) {
++ proxyHost = v2;
++ continue;
++ }
++ if (s2.indexOf("proxy.https.port") >= 0) {
++ proxyPort = gint(v2);
++ continue;
++ }
++ if (s2.indexOf("proxy.http.host") >= 0) {
++ proxyHost_nossl = v2;
++ continue;
++ }
++ if (s2.indexOf("proxy.http.port") >= 0) {
++ proxyPort_nossl = gint(v2);
++ continue;
++ }
++ }
++
++ for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) {
++ String s = (String) e.nextElement();
++ String v = System.getProperty(s);
++ String s2 = s.toLowerCase();
++ String v2 = v.toLowerCase();
++
++ if (proxyHost != null && proxyPort > 0) {
++ break;
++ }
++
++ // look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082
++ if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) {
++ continue;
++ }
++ if (v2.indexOf("http") < 0) {
++ continue;
++ }
++
++ String[] pieces = v.split("[,;]");
++ for (int i = 0; i < pieces.length; i++) {
++ String p = pieces[i];
++ int j = p.indexOf("https");
++ if (j < 0) {
++ j = p.indexOf("http");
++ if (j < 0) {
++ continue;
++ }
++ }
++ j = p.indexOf("=", j);
++ if (j < 0) {
++ continue;
++ }
++ p = p.substring(j+1);
++ String [] hp = p.split(":");
++ if (hp.length != 2) {
++ continue;
++ }
++ if (hp[0].length() > 1 && hp[1].length() > 1) {
++
++ proxyPort = gint(hp[1]);
++ if (proxyPort < 0) {
++ continue;
++ }
++ proxyHost = new String(hp[0]);
++ break;
++ }
++ }
++ }
++ }
++ if (proxyHost != null) {
++ if (proxyHost_nossl != null && proxyPort_nossl > 0) {
++ dbg("Using http proxy info instead of https.");
++ proxyHost = proxyHost_nossl;
++ proxyPort = proxyPort_nossl;
++ }
++ }
++
++ if (proxy_in_use) {
++ if (proxy_dialog_host != null && proxy_dialog_port > 0) {
++ proxyHost = proxy_dialog_host;
++ proxyPort = proxy_dialog_port;
++ }
++ if (proxyHost != null) {
++ dbg("Lucky us! we figured out the Proxy parameters: " + proxyHost + " " + proxyPort);
++ } else {
++ /* ask user to help us: */
++ ProxyDialog pd = new ProxyDialog(proxyHost, proxyPort);
++ pd.queryUser();
++ proxyHost = pd.getHost();
++ proxyPort = pd.getPort();
++ proxy_dialog_host = new String(proxyHost);
++ proxy_dialog_port = proxyPort;
++ dbg("User said host: " + pd.getHost() + " port: " + pd.getPort());
++ }
++
++ proxy_helper(proxyHost, proxyPort);
++ if (proxySock == null) {
++ return null;
++ }
++ } else if (viewer.CONNECT != null) {
++ dbg("viewer.CONNECT psocket:");
++ proxySock = psocket(host, port);
++ if (proxySock == null) {
++ dbg("1-b sadly, returning a null socket");
++ return null;
++ }
++ }
++
++ if (viewer.CONNECT != null) {
++ String hp = viewer.CONNECT;
++ String req2 = "CONNECT " + hp + " HTTP/1.1\r\n"
++ + "Host: " + hp + "\r\n\r\n";
++
++ dbg("requesting2: " + req2);
++
++ try {
++ proxy_os.write(req2.getBytes());
++ String reply = readline(proxy_is);
++
++ dbg("proxy replied2: " + reply.trim());
++
++ if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) {
++ proxySock.close();
++ proxySock = psocket(proxyHost, proxyPort);
++ if (proxySock == null) {
++ dbg("2-b sadly, returning a null socket");
++ return null;
++ }
++ }
++ } catch(Exception e) {
++ dbg("proxy socket problem-2: " + e.getMessage());
++ }
++
++ while (true) {
++ String line = readline(proxy_is);
++ dbg("proxy line2: " + line.trim());
++ if (line.equals("\r\n") || line.equals("\n")) {
++ break;
++ }
++ }
++ }
++
++ Socket sslsock = null;
++ try {
++ sslsock = factory.createSocket(proxySock, host, port, true);
++ } catch(Exception e) {
++ dbg("sslsock prob: " + e.getMessage());
++ dbg("3 sadly, returning a null socket");
++ }
++
++ return (SSLSocket) sslsock;
++ }
++
++ Socket psocket(String h, int p) {
++ Socket psock = null;
++ try {
++ psock = new Socket(h, p);
++ proxy_is = new DataInputStream(new BufferedInputStream(
++ psock.getInputStream(), 16384));
++ proxy_os = psock.getOutputStream();
++ } catch(Exception e) {
++ dbg("psocket prob: " + e.getMessage());
++ return null;
++ }
++
++ return psock;
++ }
++
++ String readline(DataInputStream i) {
++ byte[] ba = new byte[1];
++ String s = new String("");
++ ba[0] = 0;
++ try {
++ while (ba[0] != 0xa) {
++ ba[0] = (byte) i.readUnsignedByte();
++ s += new String(ba);
++ }
++ } catch (Exception e) {
++ ;
++ }
++ return s;
++ }
++}
++
++class TrustDialog implements ActionListener {
++ String msg, host, text;
++ int port;
++ java.security.cert.Certificate[] trustallCerts = null;
++ boolean viewing_cert = false;
++ boolean trust_this_session = false;
++
++ /*
++ * this is the gui to show the user the cert and info and ask
++ * them if they want to continue using this cert.
++ */
++
++ Button ok, cancel, viewcert;
++ TextArea textarea;
++ Checkbox accept, deny;
++ Dialog dialog;
++
++ String s1 = "Accept this certificate temporarily for this session";
++ String s2 = "Do not accept this certificate and do not connect to"
++ + " this VNC server";
++ String ln = "\n---------------------------------------------------\n\n";
++
++ TrustDialog (String h, int p, java.security.cert.Certificate[] s) {
++ host = h;
++ port = p;
++ trustallCerts = s;
++
++ msg = "VNC Server " + host + ":" + port + " Not Verified";
++ }
++
++ public boolean queryUser(String reason) {
++
++ /* create and display the dialog for unverified cert. */
++
++ Frame frame = new Frame(msg);
++
++ dialog = new Dialog(frame, true);
++
++ String infostr = "";
++ if (trustallCerts.length == 1) {
++ CertInfo ci = new CertInfo(trustallCerts[0]);
++ infostr = ci.get_certinfo("all");
++ }
++ if (reason != null) {
++ reason += "\n\n";
++ }
++
++ text = "\n"
+++ "Unable to verify the identity of\n"
+++ "\n"
+++ " " + host + ":" + port + "\n"
+++ "\n"
+++ infostr
+++ "\n"
+++ "as a trusted VNC server.\n"
+++ "\n"
+++ reason
+++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n"
+++ "is due to one of the following:\n"
+++ "\n"
+++ " - Your requesting to View the Certificate before accepting.\n"
+++ "\n"
+++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n"
+++ " Authority not recognized by your Web Browser or Java Plugin runtime.\n"
+++ "\n"
+++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n"
+++ " the Apache Web server has a certificate *different* from the VNC server's.\n"
+++ "\n"
+++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n"
+++ " obtained by this applet to compare the VNC Server Certificate against.\n"
+++ "\n"
+++ " - The VNC Server's Certificate does not match the one specified in the\n"
+++ " supplied 'serverCert' Java Applet Parameter.\n"
+++ "\n"
+++ " - A Man-In-The-Middle attack impersonating as the VNC server that you wish\n"
+++ " to connect to. (Wouldn't that be exciting!!)\n"
+++ "\n"
+++ "By safely copying the VNC server's Certificate (or using a common Certificate\n"
+++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n"
+++ "automatically authenticate this VNC Server.\n"
+++ "\n"
+++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n"
+++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n"
+++ "certificate (except for the Apache portal case above where they don't match.)\n"
+++ "\n"
+++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n"
+++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n"
+++ "and thereby see no dialog from this VNC Viewer applet.\n"
++;
++
++ /* the accept / do-not-accept radio buttons: */
++ CheckboxGroup checkbox = new CheckboxGroup();
++ accept = new Checkbox(s1, true, checkbox);
++ deny = new Checkbox(s2, false, checkbox);
++
++ /* put the checkboxes in a panel: */
++ Panel check = new Panel();
++ check.setLayout(new GridLayout(2, 1));
++
++ check.add(accept);
++ check.add(deny);
++
++ /* make the 3 buttons: */
++ ok = new Button("OK");
++ cancel = new Button("Cancel");
++ viewcert = new Button("View Certificate");
++
++ ok.addActionListener(this);
++ cancel.addActionListener(this);
++ viewcert.addActionListener(this);
++
++ /* put the buttons in their own panel: */
++ Panel buttonrow = new Panel();
++ buttonrow.setLayout(new FlowLayout(FlowLayout.LEFT));
++ buttonrow.add(viewcert);
++ buttonrow.add(ok);
++ buttonrow.add(cancel);
++
++ /* label at the top: */
++ Label label = new Label(msg, Label.CENTER);
++ label.setFont(new Font("Helvetica", Font.BOLD, 16));
++
++ /* textarea in the middle */
++ textarea = new TextArea(text, 38, 64,
++ TextArea.SCROLLBARS_VERTICAL_ONLY);
++ textarea.setEditable(false);
++
++ /* put the two panels in their own panel at bottom: */
++ Panel bot = new Panel();
++ bot.setLayout(new GridLayout(2, 1));
++ bot.add(check);
++ bot.add(buttonrow);
++
++ /* now arrange things inside the dialog: */
++ dialog.setLayout(new BorderLayout());
++
++ dialog.add("North", label);
++ dialog.add("South", bot);
++ dialog.add("Center", textarea);
++
++ dialog.pack();
++ dialog.resize(dialog.preferredSize());
++
++ dialog.show(); /* block here til OK or Cancel pressed. */
++
++ return trust_this_session;
++ }
++
++ public synchronized void actionPerformed(ActionEvent evt) {
++
++ if (evt.getSource() == viewcert) {
++ /* View Certificate button clicked */
++ if (viewing_cert) {
++ /* show the original info text: */
++ textarea.setText(text);
++ viewcert.setLabel("View Certificate");
++ viewing_cert = false;
++ } else {
++ int i;
++ /* show all (likely just one) certs: */
++ textarea.setText("");
++ for (i=0; i < trustallCerts.length; i++) {
++ int j = i + 1;
++ textarea.append("Certificate[" +
++ j + "]\n\n");
++ textarea.append(
++ trustallCerts[i].toString());
++ textarea.append(ln);
++ }
++ viewcert.setLabel("View Info");
++ viewing_cert = true;
++
++ textarea.setCaretPosition(0);
++ }
++
++ } else if (evt.getSource() == ok) {
++ /* OK button clicked */
++ if (accept.getState()) {
++ trust_this_session = true;
++ } else {
++ trust_this_session = false;
++ }
++ //dialog.dispose();
++ dialog.hide();
++
++ } else if (evt.getSource() == cancel) {
++ /* Cancel button clicked */
++ trust_this_session = false;
++
++ //dialog.dispose();
++ dialog.hide();
++ }
++ }
++
++ String get_certinfo() {
++ String all = "";
++ String fields[] = {"CN", "OU", "O", "L", "C"};
++ int i;
++ if (trustallCerts.length < 1) {
++ all = "";
++ return all;
++ }
++ String cert = trustallCerts[0].toString();
++
++ /*
++ * For now we simply scrape the cert string, there must
++ * be an API for this... perhaps optionValue?
++ */
++
++ for (i=0; i < fields.length; i++) {
++ int f, t, t1, t2;
++ String sub, mat = fields[i] + "=";
++
++ f = cert.indexOf(mat, 0);
++ if (f > 0) {
++ t1 = cert.indexOf(", ", f);
++ t2 = cert.indexOf("\n", f);
++ if (t1 < 0 && t2 < 0) {
++ continue;
++ } else if (t1 < 0) {
++ t = t2;
++ } else if (t2 < 0) {
++ t = t1;
++ } else if (t1 < t2) {
++ t = t1;
++ } else {
++ t = t2;
++ }
++ if (t > f) {
++ sub = cert.substring(f, t);
++ all = all + " " + sub + "\n";
++ }
++ }
++ }
++ return all;
++ }
++}
++
++class ProxyDialog implements ActionListener {
++ String guessedHost = null;
++ String guessedPort = null;
++ /*
++ * this is the gui to show the user the cert and info and ask
++ * them if they want to continue using this cert.
++ */
++
++ Button ok;
++ Dialog dialog;
++ TextField entry;
++ String reply = "";
++
++ ProxyDialog (String h, int p) {
++ guessedHost = h;
++ try {
++ guessedPort = Integer.toString(p);
++ } catch (Exception e) {
++ guessedPort = "8080";
++ }
++ }
++
++ public void queryUser() {
++
++ /* create and display the dialog for unverified cert. */
++
++ Frame frame = new Frame("Need Proxy host:port");
++
++ dialog = new Dialog(frame, true);
++
++
++ Label label = new Label("Please Enter your https Proxy info as host:port", Label.CENTER);
++ //label.setFont(new Font("Helvetica", Font.BOLD, 16));
++ entry = new TextField(30);
++ ok = new Button("OK");
++ ok.addActionListener(this);
++
++ String guess = "";
++ if (guessedHost != null) {
++ guess = guessedHost + ":" + guessedPort;
++ }
++ entry.setText(guess);
++
++ dialog.setLayout(new BorderLayout());
++ dialog.add("North", label);
++ dialog.add("Center", entry);
++ dialog.add("South", ok);
++ dialog.pack();
++ dialog.resize(dialog.preferredSize());
++
++ dialog.show(); /* block here til OK or Cancel pressed. */
++ return;
++ }
++
++ public String getHost() {
++ int i = reply.indexOf(":");
++ if (i < 0) {
++ return "unknown";
++ }
++ String h = reply.substring(0, i);
++ return h;
++ }
++
++ public int getPort() {
++ int i = reply.indexOf(":");
++ int p = 8080;
++ if (i < 0) {
++ return p;
++ }
++ i++;
++ String ps = reply.substring(i);
++ try {
++ Integer I = new Integer(ps);
++ p = I.intValue();
++ } catch (Exception e) {
++ ;
++ }
++ return p;
++ }
++
++ public synchronized void actionPerformed(ActionEvent evt) {
++ System.out.println(evt.getActionCommand());
++ if (evt.getSource() == ok) {
++ reply = entry.getText();
++ //dialog.dispose();
++ dialog.hide();
++ }
++ }
++}
++
++class ProxyPasswdDialog implements ActionListener {
++ String guessedHost = null;
++ String guessedPort = null;
++ String guessedUser = null;
++ String guessedPasswd = null;
++ String realm = null;
++ /*
++ * this is the gui to show the user the cert and info and ask
++ * them if they want to continue using this cert.
++ */
++
++ Button ok;
++ Dialog dialog;
++ TextField entry1;
++ TextField entry2;
++ String reply1 = "";
++ String reply2 = "";
++
++ ProxyPasswdDialog (String h, int p, String realm) {
++ guessedHost = h;
++ try {
++ guessedPort = Integer.toString(p);
++ } catch (Exception e) {
++ guessedPort = "8080";
++ }
++ this.realm = realm;
++ }
++
++ public void queryUser() {
++
++ /* create and display the dialog for unverified cert. */
++
++ Frame frame = new Frame("Proxy Requires Username and Password");
++
++ dialog = new Dialog(frame, true);
++
++ //Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER);
++ TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE);
++ entry1 = new TextField(30);
++ entry2 = new TextField(30);
++ entry2.setEchoChar('*');
++ ok = new Button("OK");
++ ok.addActionListener(this);
++
++ dialog.setLayout(new BorderLayout());
++ dialog.add("North", label);
++ dialog.add("Center", entry1);
++ dialog.add("South", entry2);
++ dialog.add("East", ok);
++ dialog.pack();
++ dialog.resize(dialog.preferredSize());
++
++ dialog.show(); /* block here til OK or Cancel pressed. */
++ return;
++ }
++
++ public String getAuth() {
++ return reply1 + ":" + reply2;
++ }
++
++ public synchronized void actionPerformed(ActionEvent evt) {
++ System.out.println(evt.getActionCommand());
++ if (evt.getSource() == ok) {
++ reply1 = entry1.getText();
++ reply2 = entry2.getText();
++ //dialog.dispose();
++ dialog.hide();
++ }
++ }
++}
++
++class ClientCertDialog implements ActionListener {
++
++ Button ok;
++ Dialog dialog;
++ TextField entry;
++ String reply = "";
++
++ ClientCertDialog() {
++ ;
++ }
++
++ public String queryUser() {
++
++ /* create and display the dialog for unverified cert. */
++
++ Frame frame = new Frame("Enter SSL Client Cert+Key String");
++
++ dialog = new Dialog(frame, true);
++
++
++ Label label = new Label("Please Enter the SSL Client Cert+Key String 308204c0...,...522d2d0a", Label.CENTER);
++ entry = new TextField(30);
++ ok = new Button("OK");
++ ok.addActionListener(this);
++
++ dialog.setLayout(new BorderLayout());
++ dialog.add("North", label);
++ dialog.add("Center", entry);
++ dialog.add("South", ok);
++ dialog.pack();
++ dialog.resize(dialog.preferredSize());
++
++ dialog.show(); /* block here til OK or Cancel pressed. */
++ return reply;
++ }
++
++ public synchronized void actionPerformed(ActionEvent evt) {
++ System.out.println(evt.getActionCommand());
++ if (evt.getSource() == ok) {
++ reply = entry.getText();
++ //dialog.dispose();
++ dialog.hide();
++ }
++ }
++}
++
++class BrowserCertsDialog implements ActionListener {
++ Button yes, no;
++ Dialog dialog;
++ String vncServer;
++ String hostport;
++ public boolean showCertDialog = true;
++
++ BrowserCertsDialog(String serv, String hp) {
++ vncServer = serv;
++ hostport = hp;
++ }
++
++ public void queryUser() {
++
++ /* create and display the dialog for unverified cert. */
++
++ Frame frame = new Frame("Use Browser/JVM Certs?");
++
++ dialog = new Dialog(frame, true);
++
++ String m = "";
++m += "\n";
++m += "This VNC Viewer applet does not have its own keystore to track\n";
++m += "SSL certificates, and so cannot authenticate the certificate\n";
++m += "of the VNC Server:\n";
++m += "\n";
++m += " " + hostport + "\n\n " + vncServer + "\n";
++m += "\n";
++m += "on its own.\n";
++m += "\n";
++m += "However, it has noticed that your Web Browser and/or Java VM Plugin\n";
++m += "has previously accepted the same certificate. You may have set\n";
++m += "this up permanently or just for this session, or the server\n";
++m += "certificate was signed by a CA cert that your Web Browser or\n";
++m += "Java VM Plugin has.\n";
++m += "\n";
++m += "If the VNC Server connection times out while you are reading this\n";
++m += "dialog, then restart the connection and try again.\n";
++m += "\n";
++m += "Should this VNC Viewer applet now connect to the above VNC server?\n";
++m += "\n";
++
++ TextArea textarea = new TextArea(m, 22, 64,
++ TextArea.SCROLLBARS_VERTICAL_ONLY);
++ textarea.setEditable(false);
++ yes = new Button("Yes");
++ yes.addActionListener(this);
++ no = new Button("No, Let Me See the Certificate.");
++ no.addActionListener(this);
++
++ dialog.setLayout(new BorderLayout());
++ dialog.add("North", textarea);
++ dialog.add("Center", yes);
++ dialog.add("South", no);
++ dialog.pack();
++ dialog.resize(dialog.preferredSize());
++
++ dialog.show(); /* block here til Yes or No pressed. */
++ System.out.println("done show()");
++ return;
++ }
++
++ public synchronized void actionPerformed(ActionEvent evt) {
++ System.out.println(evt.getActionCommand());
++ if (evt.getSource() == yes) {
++ showCertDialog = false;
++ //dialog.dispose();
++ dialog.hide();
++ } else if (evt.getSource() == no) {
++ showCertDialog = true;
++ //dialog.dispose();
++ dialog.hide();
++ }
++ System.out.println("done actionPerformed()");
++ }
++}
++
++class CertInfo {
++ String fields[] = {"CN", "OU", "O", "L", "C"};
++ java.security.cert.Certificate cert;
++ String certString = "";
++
++ CertInfo(java.security.cert.Certificate c) {
++ cert = c;
++ certString = cert.toString();
++ }
++
++ String get_certinfo(String which) {
++ int i;
++ String cs = new String(certString);
++ String all = "";
++
++ /*
++ * For now we simply scrape the cert string, there must
++ * be an API for this... perhaps optionValue?
++ */
++ for (i=0; i < fields.length; i++) {
++ int f, t, t1, t2;
++ String sub, mat = fields[i] + "=";
++
++ f = cs.indexOf(mat, 0);
++ if (f > 0) {
++ t1 = cs.indexOf(", ", f);
++ t2 = cs.indexOf("\n", f);
++ if (t1 < 0 && t2 < 0) {
++ continue;
++ } else if (t1 < 0) {
++ t = t2;
++ } else if (t2 < 0) {
++ t = t1;
++ } else if (t1 < t2) {
++ t = t1;
++ } else {
++ t = t2;
++ }
++ if (t > f) {
++ sub = cs.substring(f, t);
++ all = all + " " + sub + "\n";
++ if (which.equals(fields[i])) {
++ return sub;
++ }
++ }
++ }
++ }
++ if (which.equals("all")) {
++ return all;
++ } else {
++ return "";
++ }
++ }
++}
++
++class Base64Coder {
++
++ // Mapping table from 6-bit nibbles to Base64 characters.
++ private static char[] map1 = new char[64];
++ static {
++ int i=0;
++ for (char c='A'; c<='Z'; c++) map1[i++] = c;
++ for (char c='a'; c<='z'; c++) map1[i++] = c;
++ for (char c='0'; c<='9'; c++) map1[i++] = c;
++ map1[i++] = '+'; map1[i++] = '/'; }
++
++ // Mapping table from Base64 characters to 6-bit nibbles.
++ private static byte[] map2 = new byte[128];
++ static {
++ for (int i=0; i<map2.length; i++) map2[i] = -1;
++ for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; }
++
++ /**
++ * Encodes a string into Base64 format.
++ * No blanks or line breaks are inserted.
++ * @param s a String to be encoded.
++ * @return A String with the Base64 encoded data.
++ */
++ public static String encodeString (String s) {
++ return new String(encode(s.getBytes())); }
++
++ /**
++ * Encodes a byte array into Base64 format.
++ * No blanks or line breaks are inserted.
++ * @param in an array containing the data bytes to be encoded.
++ * @return A character array with the Base64 encoded data.
++ */
++ public static char[] encode (byte[] in) {
++ return encode(in,in.length); }
++
++ /**
++ * Encodes a byte array into Base64 format.
++ * No blanks or line breaks are inserted.
++ * @param in an array containing the data bytes to be encoded.
++ * @param iLen number of bytes to process in <code>in</code>.
++ * @return A character array with the Base64 encoded data.
++ */
++ public static char[] encode (byte[] in, int iLen) {
++ int oDataLen = (iLen*4+2)/3; // output length without padding
++ int oLen = ((iLen+2)/3)*4; // output length including padding
++ char[] out = new char[oLen];
++ int ip = 0;
++ int op = 0;
++ while (ip < iLen) {
++ int i0 = in[ip++] & 0xff;
++ int i1 = ip < iLen ? in[ip++] & 0xff : 0;
++ int i2 = ip < iLen ? in[ip++] & 0xff : 0;
++ int o0 = i0 >>> 2;
++ int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
++ int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
++ int o3 = i2 & 0x3F;
++ out[op++] = map1[o0];
++ out[op++] = map1[o1];
++ out[op] = op < oDataLen ? map1[o2] : '='; op++;
++ out[op] = op < oDataLen ? map1[o3] : '='; op++; }
++ return out; }
++
++ /**
++ * Decodes a string from Base64 format.
++ * @param s a Base64 String to be decoded.
++ * @return A String containing the decoded data.
++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data.
++ */
++ public static String decodeString (String s) {
++ return new String(decode(s)); }
++
++ /**
++ * Decodes a byte array from Base64 format.
++ * @param s a Base64 String to be decoded.
++ * @return An array containing the decoded data bytes.
++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data.
++ */
++ public static byte[] decode (String s) {
++ return decode(s.toCharArray()); }
++
++ /**
++ * Decodes a byte array from Base64 format.
++ * No blanks or line breaks are allowed within the Base64 encoded data.
++ * @param in a character array containing the Base64 encoded data.
++ * @return An array containing the decoded data bytes.
++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data.
++ */
++ public static byte[] decode (char[] in) {
++ int iLen = in.length;
++ if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
++ while (iLen > 0 && in[iLen-1] == '=') iLen--;
++ int oLen = (iLen*3) / 4;
++ byte[] out = new byte[oLen];
++ int ip = 0;
++ int op = 0;
++ while (ip < iLen) {
++ int i0 = in[ip++];
++ int i1 = in[ip++];
++ int i2 = ip < iLen ? in[ip++] : 'A';
++ int i3 = ip < iLen ? in[ip++] : 'A';
++ if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
++ throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
++ int b0 = map2[i0];
++ int b1 = map2[i1];
++ int b2 = map2[i2];
++ int b3 = map2[i3];
++ if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
++ throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
++ int o0 = ( b0 <<2) | (b1>>>4);
++ int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
++ int o2 = ((b2 & 3)<<6) | b3;
++ out[op++] = (byte)o0;
++ if (op<oLen) out[op++] = (byte)o1;
++ if (op<oLen) out[op++] = (byte)o2; }
++ return out; }
++
++ // Dummy constructor.
++ private Base64Coder() {}
++
++}
+diff -Naur JavaViewer.orig/VncCanvas.java JavaViewer/VncCanvas.java
+--- JavaViewer.orig/VncCanvas.java 2005-11-21 18:50:18.000000000 -0500
++++ JavaViewer/VncCanvas.java 2010-11-30 22:57:50.000000000 -0500
+@@ -27,6 +27,13 @@
+ import java.lang.*;
+ import java.util.zip.*;
+
++// begin runge/x11vnc
++import java.util.Collections;
++// end runge/x11vnc
++
++// begin runge/x11vnc
++// all the MouseWheel stuff below.
++// end runge/x11vnc
+
+ //
+ // VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
+@@ -34,7 +41,7 @@
+
+ class VncCanvas
+ extends Canvas
+- implements KeyListener, MouseListener, MouseMotionListener {
++ implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener {
+
+ VncViewer viewer;
+ RfbProto rfb;
+@@ -85,6 +92,22 @@
+
+ cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
+
++// begin runge/x11vnc
++// kludge to not show any Java cursor in the canvas since we are
++// showing the soft cursor (should be a user setting...)
++Cursor dot = Toolkit.getDefaultToolkit().createCustomCursor(
++ Toolkit.getDefaultToolkit().createImage(new byte[4]), new Point(0,0),
++ "dot");
++this.setCursor(dot);
++
++// while we are at it... get rid of the keyboard traversals that
++// make it so we can't type a Tab character:
++this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
++ Collections.EMPTY_SET);
++this.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
++ Collections.EMPTY_SET);
++// end runge/x11vnc
++
+ colors = new Color[256];
+ // sf@2005 - Now Default
+ for (int i = 0; i < 256; i++)
+@@ -186,6 +209,7 @@
+ inputEnabled = true;
+ addMouseListener(this);
+ addMouseMotionListener(this);
++ addMouseWheelListener(this);
+ if (viewer.showControls) {
+ viewer.buttonPanel.enableRemoteAccessControls(true);
+ }
+@@ -193,6 +217,7 @@
+ inputEnabled = false;
+ removeMouseListener(this);
+ removeMouseMotionListener(this);
++ removeMouseWheelListener(this);
+ if (viewer.showControls) {
+ viewer.buttonPanel.enableRemoteAccessControls(false);
+ }
+@@ -202,6 +227,9 @@
+
+ public void setPixelFormat() throws IOException {
+ // sf@2005 - Adding more color modes
++ if (viewer.graftFtp) {
++ return;
++ }
+ if (viewer.options.eightBitColors > 0)
+ {
+ viewer.options.oldEightBitColors = viewer.options.eightBitColors;
+@@ -237,6 +265,9 @@
+ }
+ else
+ {
++// begin runge/x11vnc
++ viewer.options.oldEightBitColors = viewer.options.eightBitColors;
++// end runge/x11vnc
+ rfb.writeSetPixelFormat(
+ 32,
+ 24,
+@@ -376,12 +407,14 @@
+ // Start/stop session recording if necessary.
+ viewer.checkRecordingStatus();
+
+- rfb.writeFramebufferUpdateRequest(
+- 0,
+- 0,
+- rfb.framebufferWidth,
+- rfb.framebufferHeight,
+- false);
++ if (!viewer.graftFtp) {
++ rfb.writeFramebufferUpdateRequest(
++ 0,
++ 0,
++ rfb.framebufferWidth,
++ rfb.framebufferHeight,
++ false);
++ }
+
+ //
+ // main dispatch loop
+@@ -390,6 +423,9 @@
+ while (true) {
+ // Read message type from the server.
+ int msgType = rfb.readServerMessageType();
++ if (viewer.ftpOnly && msgType != RfbProto.rfbFileTransfer) {
++ System.out.println("msgType:" + msgType);
++ }
+
+ // Process the message depending on its type.
+ switch (msgType) {
+@@ -1332,6 +1368,9 @@
+ public void mouseDragged(MouseEvent evt) {
+ processLocalMouseEvent(evt, true);
+ }
++ public void mouseWheelMoved(MouseWheelEvent evt) {
++ processLocalMouseWheelEvent(evt);
++ }
+
+ public void processLocalKeyEvent(KeyEvent evt) {
+ if (viewer.rfb != null && rfb.inNormalProtocol) {
+@@ -1367,6 +1406,19 @@
+ evt.consume();
+ }
+
++ public void processLocalMouseWheelEvent(MouseWheelEvent evt) {
++ if (viewer.rfb != null && rfb.inNormalProtocol) {
++ synchronized(rfb) {
++ try {
++ rfb.writeWheelEvent(evt);
++ } catch (Exception e) {
++ e.printStackTrace();
++ }
++ rfb.notify();
++ }
++ }
++ }
++
+ public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
+ if (viewer.rfb != null && rfb.inNormalProtocol) {
+ if (moved) {
+@@ -1532,9 +1584,14 @@
+ else
+ {
+ result =
+- 0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF)
+- << 16 | (pixBuf[i * 4 + 2] & 0xFF)
+- << 8 | (pixBuf[i * 4 + 3] & 0xFF);
++// begin runge/x11vnc
++// 0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF)
++// << 16 | (pixBuf[i * 4 + 2] & 0xFF)
++// << 8 | (pixBuf[i * 4 + 3] & 0xFF);
++ 0xFF000000 | (pixBuf[i * 4 + 2] & 0xFF)
++ << 16 | (pixBuf[i * 4 + 1] & 0xFF)
++ << 8 | (pixBuf[i * 4 + 0] & 0xFF);
++// end runge/x11vnc
+ }
+ } else {
+ result = 0; // Transparent pixel
+@@ -1565,9 +1622,14 @@
+ else
+ {
+ result =
+- 0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF)
+- << 16 | (pixBuf[i * 4 + 2] & 0xFF)
+- << 8 | (pixBuf[i * 4 + 3] & 0xFF);
++// begin runge/x11vnc
++// 0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF)
++// << 16 | (pixBuf[i * 4 + 2] & 0xFF)
++// << 8 | (pixBuf[i * 4 + 3] & 0xFF);
++ 0xFF000000 | (pixBuf[i * 4 + 2] & 0xFF)
++ << 16 | (pixBuf[i * 4 + 1] & 0xFF)
++ << 8 | (pixBuf[i * 4 + 0] & 0xFF);
++// end runge/x11vnc
+ }
+ } else {
+ result = 0; // Transparent pixel
+diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
+--- JavaViewer.orig/VncViewer.java 2006-05-24 15:14:40.000000000 -0400
++++ JavaViewer/VncViewer.java 2010-03-27 18:00:28.000000000 -0400
+@@ -41,6 +41,7 @@
+ import java.io.*;
+ import java.net.*;
+ import javax.swing.*;
++import java.util.Date;
+
+ public class VncViewer extends java.applet.Applet
+ implements java.lang.Runnable, WindowListener {
+@@ -80,11 +81,11 @@
+ GridBagLayout gridbag;
+ ButtonPanel buttonPanel;
+ AuthPanel authenticator;
+- VncCanvas vc;
++ VncCanvas vc = null;
+ OptionsFrame options;
+ ClipboardFrame clipboard;
+ RecordingFrame rec;
+- FTPFrame ftp; // KMC: FTP Frame declaration
++ FTPFrame ftp = null; // KMC: FTP Frame declaration
+
+ // Control session recording.
+ Object recordingSync;
+@@ -96,7 +97,7 @@
+
+ // Variables read from parameter values.
+ String host;
+- int port;
++ int port, vncserverport;
+ String passwordParam;
+ String encPasswordParam;
+ boolean showControls;
+@@ -115,28 +116,75 @@
+ int i;
+ // mslogon support 2 end
+
++// begin runge/x11vnc
++boolean disableSSL;
++boolean GET;
++String CONNECT;
++String urlPrefix;
++String httpsPort;
++String oneTimeKey;
++String serverCert;
++String ftpDropDown;
++String proxyHost;
++String proxyPort;
++boolean forceProxy;
++boolean ignoreProxy;
++boolean trustAllVncCerts;
++boolean trustUrlVncCert;
++boolean debugCerts;
++boolean debugKeyboard;
++boolean mapF5_to_atsign;
++boolean forbid_Ctrl_Alt;
++
++boolean ignoreMSLogonCheck;
++boolean delayAuthPanel;
++boolean ftpOnly;
++boolean graftFtp;
++boolean dsmActive;
++
++boolean gotAuth;
++int authGot;
++// end runge/x11vnc
++
++
+ //
+ // init()
+ //
+
++public void ftp_init() {
++ boolean show = false;
++ if (ftp != null) {
++ show = true;
++ }
++ ftp = null;
++
++ ftp = new FTPFrame(this); // KMC: FTPFrame creation
++
++ if (show) {
++ ftp.doOpen();
++ rfb.readServerDriveList();
++ }
++}
++
+ public void init() {
+
+ readParameters();
+
+ if (inSeparateFrame) {
+- vncFrame = new Frame("Ultr@VNC");
+- if (!inAnApplet) {
+- vncFrame.add("Center", this);
+- }
+- vncContainer = vncFrame;
++ vncFrame = new Frame("Ultr@VNC");
++ if (!inAnApplet) {
++ vncFrame.add("Center", this);
++ }
++ vncContainer = vncFrame;
+ } else {
+- vncContainer = this;
++ vncContainer = this;
+ }
+
+ recordingSync = new Object();
+
+ options = new OptionsFrame(this);
+ clipboard = new ClipboardFrame(this);
++
+ // authenticator = new AuthPanel(false); // mslogon support : go to connectAndAuthenticate()
+ if (RecordingFrame.checkSecurity())
+ rec = new RecordingFrame(this);
+@@ -147,10 +195,11 @@
+ cursorUpdatesDef = null;
+ eightBitColorsDef = null;
+
+- if (inSeparateFrame)
++ if (inSeparateFrame && vncFrame != null)
+ vncFrame.addWindowListener(this);
+
+- ftp = new FTPFrame(this); // KMC: FTPFrame creation
++ ftp_init();
++
+ rfbThread = new Thread(this);
+ rfbThread.start();
+ }
+@@ -186,6 +235,30 @@
+ gbc.weightx = 1.0;
+ gbc.weighty = 1.0;
+
++ if (ftpOnly) {
++ if (showControls) {
++ buttonPanel.enableButtons();
++ }
++ ActionListener taskPerformer = new ActionListener() {
++ public void actionPerformed(ActionEvent evt) {
++ vncFrame.setVisible(false);
++ ftp.setSavedLocations();
++ if (ftp.isVisible()) {
++ ftp.doClose();
++ } else {
++ ftp.doOpen();
++ }
++ rfb.readServerDriveList();
++ }
++ };
++ Timer t = new Timer(300, taskPerformer);
++ t.setRepeats(false);
++ t.start();
++
++ vc.processNormalProtocol();
++ return;
++ }
++
+ // Add ScrollPanel to applet mode
+
+ // Create a panel which itself is resizeable and can hold
+@@ -286,6 +359,24 @@
+
+ void connectAndAuthenticate() throws Exception {
+
++ if (graftFtp) {
++ rfb = new RfbProto(host, port, this);
++ rfb.desktopName = "ftponly";
++ rfb.framebufferWidth = 12;
++ rfb.framebufferHeight = 12;
++ rfb.bitsPerPixel = 32;
++ rfb.depth = 24;
++ rfb.trueColour = true;
++ rfb.redMax = 255;
++ rfb.greenMax = 255;
++ rfb.blueMax = 255;
++ rfb.redShift = 16;
++ rfb.greenShift = 8;
++ rfb.blueShift = 0;
++ rfb.inNormalProtocol = true;
++ return;
++ }
++
+ // If "ENCPASSWORD" parameter is set, decrypt the password into
+ // the passwordParam string.
+
+@@ -336,7 +427,22 @@
+ //
+
+
+- prologueDetectAuthProtocol() ;
++// begin runge/x11vnc
++ gotAuth = false;
++ if (delayAuthPanel) {
++ if (tryAuthenticate(null, null)) {
++ if (inSeparateFrame) {
++ vncFrame.pack();
++ vncFrame.show();
++ }
++ return;
++ }
++ }
++// prologueDetectAuthProtocol() ;
++ if (ignoreMSLogonCheck == false) {
++ prologueDetectAuthProtocol() ;
++ }
++// end runge/x11vnc
+
+ authenticator = new AuthPanel(mslogon);
+
+@@ -371,6 +477,7 @@
+ //mslogon support end
+ }
+
++ int tries = 0;
+ while (true) {
+ // Wait for user entering a password, or a username and a password
+ synchronized(authenticator) {
+@@ -390,6 +497,13 @@
+ break;
+ //mslogon support end
+
++// begin runge/x11vnc
++ gotAuth = false;
++ if (++tries > 2) {
++ throw new Exception("Incorrect password entered " + tries + " times.");
++ }
++// end runge/x11vnc
++
+ // Retry on authentication failure.
+ authenticator.retry();
+ }
+@@ -405,9 +519,11 @@
+
+ void prologueDetectAuthProtocol() throws Exception {
+
+- rfb = new RfbProto(host, port, this);
++ if (!gotAuth) {
++ rfb = new RfbProto(host, port, this);
+
+- rfb.readVersionMsg();
++ rfb.readVersionMsg();
++ }
+
+ System.out.println("RFB server supports protocol version " +
+ rfb.serverMajor + "." + rfb.serverMinor);
+@@ -431,16 +547,36 @@
+
+ boolean tryAuthenticate(String us, String pw) throws Exception {
+
+- rfb = new RfbProto(host, port, this);
++ int authScheme;
+
+- rfb.readVersionMsg();
++ if (!gotAuth) {
++ rfb = new RfbProto(host, port, this);
+
+- System.out.println("RFB server supports protocol version " +
+- rfb.serverMajor + "." + rfb.serverMinor);
++ rfb.readVersionMsg();
+
+- rfb.writeVersionMsg();
++ System.out.println("RFB server supports protocol version: " +
++ rfb.serverMajor + "." + rfb.serverMinor);
+
+- int authScheme = rfb.readAuthScheme();
++ rfb.writeVersionMsg();
++
++ authScheme = rfb.readAuthScheme();
++
++ gotAuth = true;
++ authGot = authScheme;
++ } else {
++ authScheme = authGot;
++ }
++// begin runge/x11vnc
++ if (delayAuthPanel && pw == null) {
++ if (authScheme == RfbProto.NoAuth) {
++ System.out.println("No authentication needed");
++ return true;
++ } else {
++ return false;
++ }
++ }
++System.out.println("as: " + authScheme);
++// end runge/x11vnc
+
+ switch (authScheme) {
+
+@@ -629,6 +765,10 @@
+
+ void doProtocolInitialisation() throws IOException {
+
++ if (graftFtp) {
++ return;
++ }
++
+ rfb.writeClientInit();
+
+ rfb.readServerInit();
+@@ -774,9 +914,28 @@
+ fatalError("HOST parameter not specified");
+ }
+ }
++ Date d = new Date();
++ System.out.println("-\nSSL VNC Java Applet starting. " + d);
+
+- String str = readParameter("PORT", true);
+- port = Integer.parseInt(str);
++ port = 0;
++ String str = readParameter("PORT", false);
++ if (str != null) {
++ port = Integer.parseInt(str);
++ }
++ // When there is a proxy VNCSERVERPORT may be inaccessible (inside firewall).
++ vncserverport = 0;
++ str = readParameter("VNCSERVERPORT", false);
++ if (str != null) {
++ vncserverport = Integer.parseInt(str);
++ }
++ if (port == 0 && vncserverport == 0) {
++ fatalError("Neither PORT nor VNCSERVERPORT parameters specified");
++ }
++ if (port == 0) {
++ // Nevertheless, fall back to vncserverport if we have to.
++ System.out.println("using vncserverport: '" + vncserverport + "' for PORT.");
++ port = vncserverport;
++ }
+
+ if (inAnApplet) {
+ str = readParameter("Open New Window", false);
+@@ -804,6 +963,158 @@
+ deferScreenUpdates = readIntParameter("Defer screen updates", 20);
+ deferCursorUpdates = readIntParameter("Defer cursor updates", 10);
+ deferUpdateRequests = readIntParameter("Defer update requests", 50);
++
++// begin runge/x11vnc
++ // SSL
++ disableSSL = false;
++ str = readParameter("DisableSSL", false);
++ if (str != null && str.equalsIgnoreCase("Yes"))
++ disableSSL = true;
++
++ httpsPort = readParameter("httpsPort", false);
++
++ // Extra GET, CONNECT string:
++ CONNECT = readParameter("CONNECT", false);
++ if (CONNECT != null) {
++ CONNECT = CONNECT.replaceAll(" ", ":");
++ }
++
++ GET = false;
++ str = readParameter("GET", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ GET = true;
++ }
++ if (str != null && str.equalsIgnoreCase("1")) {
++ GET = true;
++ }
++
++ urlPrefix = readParameter("urlPrefix", false);
++ if (urlPrefix != null) {
++ urlPrefix = urlPrefix.replaceAll("%2F", "/");
++ urlPrefix = urlPrefix.replaceAll("%2f", "/");
++ urlPrefix = urlPrefix.replaceAll("_2F_", "/");
++ if (urlPrefix.indexOf("/") != 0) {
++ urlPrefix = "/" + urlPrefix;
++ }
++ } else {
++ urlPrefix = "";
++ }
++ System.out.println("urlPrefix: '" + urlPrefix + "'");
++
++ ftpDropDown = readParameter("ftpDropDown", false);
++ if (ftpDropDown != null) {
++ ftpDropDown = ftpDropDown.replaceAll("%2F", "/");
++ ftpDropDown = ftpDropDown.replaceAll("%2f", "/");
++ ftpDropDown = ftpDropDown.replaceAll("_2F_", "/");
++ ftpDropDown = ftpDropDown.replaceAll("%20", " ");
++ System.out.println("ftpDropDown: '" + ftpDropDown + "'");
++ }
++
++
++ oneTimeKey = readParameter("oneTimeKey", false);
++ if (oneTimeKey != null) {
++ System.out.println("oneTimeKey is set.");
++ }
++
++ serverCert = readParameter("serverCert", false);
++ if (serverCert != null) {
++ System.out.println("serverCert is set.");
++ }
++
++ forceProxy = false;
++ proxyHost = null;
++ proxyPort = null;
++ str = readParameter("forceProxy", false);
++ if (str != null) {
++ if (str.equalsIgnoreCase("Yes")) {
++ forceProxy = true;
++ } else if (str.equalsIgnoreCase("No")) {
++ forceProxy = false;
++ } else {
++ forceProxy = true;
++ String[] pieces = str.split(" ");
++ proxyHost = new String(pieces[0]);
++ if (pieces.length >= 2) {
++ proxyPort = new String(pieces[1]);
++ } else {
++ proxyPort = new String("8080");
++ }
++ }
++ }
++ str = readParameter("proxyHost", false);
++ if (str != null) {
++ proxyHost = new String(str);
++ }
++ str = readParameter("proxyPort", false);
++ if (str != null) {
++ proxyPort = new String(str);
++ }
++ if (proxyHost != null && proxyPort == null) {
++ proxyPort = new String("8080");
++ }
++
++ ignoreProxy = false;
++ str = readParameter("ignoreProxy", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ ignoreProxy = true;
++ }
++
++ trustAllVncCerts = false;
++ str = readParameter("trustAllVncCerts", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ trustAllVncCerts = true;
++ }
++ trustUrlVncCert = false;
++ str = readParameter("trustUrlVncCert", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ trustUrlVncCert = true;
++ }
++ debugCerts = false;
++ str = readParameter("debugCerts", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ debugCerts = true;
++ }
++ debugKeyboard = false;
++ str = readParameter("debugKeyboard", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ debugKeyboard = true;
++ }
++ mapF5_to_atsign = false;
++ str = readParameter("mapF5_to_atsign", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ mapF5_to_atsign = true;
++ }
++ forbid_Ctrl_Alt = false;
++ str = readParameter("forbid_Ctrl_Alt", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ forbid_Ctrl_Alt = true;
++ }
++ ignoreMSLogonCheck = false;
++ str = readParameter("ignoreMSLogonCheck", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ ignoreMSLogonCheck = true;
++ }
++ ftpOnly = false;
++ str = readParameter("ftpOnly", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ ftpOnly = true;
++ }
++ graftFtp = false;
++ str = readParameter("graftFtp", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ graftFtp = true;
++ }
++ dsmActive = false;
++ str = readParameter("dsmActive", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ dsmActive = true;
++ }
++ delayAuthPanel = false;
++ str = readParameter("delayAuthPanel", false);
++ if (str != null && str.equalsIgnoreCase("Yes")) {
++ delayAuthPanel = true;
++ }
++// end runge/x11vnc
+ }
+
+ public String readParameter(String name, boolean required) {
OpenPOWER on IntegriCloud