diff options
Diffstat (limited to 'webclients/java-applet/ssl')
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.jarBinary files differ new file mode 100644 index 0000000..6c18737 --- /dev/null +++ b/webclients/java-applet/ssl/SignedUltraViewerSSL.jar diff --git a/webclients/java-applet/ssl/SignedVncViewer.jar b/webclients/java-applet/ssl/SignedVncViewer.jarBinary files differ new file mode 100644 index 0000000..95c0b0b --- /dev/null +++ b/webclients/java-applet/ssl/SignedVncViewer.jar diff --git a/webclients/java-applet/ssl/UltraViewerSSL.jar b/webclients/java-applet/ssl/UltraViewerSSL.jarBinary files differ new file mode 100644 index 0000000..45259fd --- /dev/null +++ b/webclients/java-applet/ssl/UltraViewerSSL.jar diff --git a/webclients/java-applet/ssl/VncViewer.jar b/webclients/java-applet/ssl/VncViewer.jarBinary files differ new file mode 100644 index 0000000..9453c6f --- /dev/null +++ b/webclients/java-applet/ssl/VncViewer.jar 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) { | 
