Index: invirt-vnc-client/InvirtTrustManager.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ invirt-vnc-client/InvirtTrustManager.java	2008-10-31 06:09:10.000000000 -0400
@@ -0,0 +1,123 @@
+// This code is based on http://svntrac.hanhuy.com/repo/browser/hanhuy/trunk/cms/src/com/hanhuy/ria/client/RIATrustManager.java
+/*
+ * Copyright 2006 Perry Nguyen <pfnguyen@hanhuy.com>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+public class InvirtTrustManager implements X509TrustManager {
+    private X509TrustManager trustManager;
+    private final static char[] KEY_STORE_PASSWORD =
+        { 'f', 'o', 'o', 'b', 'a', 'r' };
+    private final static String KEY_STORE_RESOURCE =
+        "trust.store";
+
+    private KeyStore loadKeyStore() throws Exception {
+        InputStream in = getClass().getClassLoader().getResourceAsStream(
+                KEY_STORE_RESOURCE);
+        KeyStore ks = null;
+        try {
+            if (in == null) {
+                //log.severe("Unable to open KeyStore");
+                throw new NullPointerException();
+            }
+            ks = KeyStore.getInstance(KeyStore.getDefaultType());
+            ks.load(in, KEY_STORE_PASSWORD);
+	    /*if (log.isLoggable(Level.FINEST)) {
+                for (Enumeration<String> aliases = ks.aliases();
+                aliases.hasMoreElements();) {
+                    String alias = aliases.nextElement();
+                    log.finest("ALIAS: " + alias);
+                }
+		}*/
+        } catch (NoSuchAlgorithmException e) {
+            throwError(e);
+        } catch (CertificateException e) {
+            throwError(e);
+        } catch (IOException e) {
+            throwError(e);
+        } catch (KeyStoreException e) {
+            throwError(e);
+        } finally {
+            try {
+                if (in != null)
+                    in.close();
+            }
+            catch (IOException e) { } // ignore
+        }
+        return ks;
+    }
+    private void createTrustManager() {
+	try {
+	    try {
+		KeyStore keystore = loadKeyStore();
+		TrustManagerFactory factory = TrustManagerFactory.getInstance(
+									      TrustManagerFactory.getDefaultAlgorithm());
+		factory.init(keystore);
+		TrustManager[] trustManagers = factory.getTrustManagers();
+		if (trustManagers.length == 0)
+		    throw new IllegalStateException("No trust manager found");
+		setTrustManager((X509TrustManager) trustManagers[0]);
+	    } catch (NoSuchAlgorithmException e) {
+		throwError(e);
+	    } catch (KeyStoreException e) {
+		throwError(e);
+	    }
+	} catch (Exception e) {
+	    e.printStackTrace();
+	}
+    }
+    private void throwError(Exception e) throws Exception {
+        //HttpClientError error = new HttpClientError(e.getMessage());
+        //error.initCause(e);
+        throw e;
+    }
+    public X509TrustManager getTrustManager() {
+        if (trustManager == null)
+            createTrustManager();
+        return trustManager;
+    }
+
+    public void setTrustManager(X509TrustManager trustManager) {
+        this.trustManager = trustManager;
+    }
+
+    public void checkClientTrusted(X509Certificate[] chain, String authType)
+            throws CertificateException {
+        getTrustManager().checkClientTrusted(chain, authType);
+    }
+
+    public void checkServerTrusted(X509Certificate[] chain, String authType)
+            throws CertificateException {
+        getTrustManager().checkServerTrusted(chain, authType);
+
+    }
+
+    public X509Certificate[] getAcceptedIssuers() {
+        return getTrustManager().getAcceptedIssuers();
+    }
+
+}
\ No newline at end of file
Index: invirt-vnc-client/Makefile
===================================================================
--- invirt-vnc-client.orig/Makefile	2008-10-31 06:09:10.000000000 -0400
+++ invirt-vnc-client/Makefile	2008-10-31 06:09:10.000000000 -0400
@@ -17,8 +17,10 @@
 	  DesCipher.class CapabilityInfo.class CapsContainer.class \
 	  RecordingFrame.class SessionRecorder.class \
 	  SocketFactory.class HTTPConnectSocketFactory.class \
+	  VNCProxyConnectSocketFactory.class VNCProxyConnectSocket.class \
 	  HTTPConnectSocket.class ReloginPanel.class \
-	  InStream.class MemInStream.class ZlibInStream.class
+	  InStream.class MemInStream.class ZlibInStream.class \
+	  VNCProxyConnectSocketWrapper.class SocketWrapper.class SocketWrapper\$$WrappingSocketImpl.class InvirtTrustManager.class
 
 SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \
 	  VncCanvas2.java \
@@ -26,8 +28,10 @@
 	  DesCipher.java CapabilityInfo.java CapsContainer.java \
 	  RecordingFrame.java SessionRecorder.java \
 	  SocketFactory.java HTTPConnectSocketFactory.java \
+	  VNCProxyConnectSocketFactory.java VNCProxyConnectSocket.java \
 	  HTTPConnectSocket.java ReloginPanel.java \
-	  InStream.java MemInStream.java ZlibInStream.java
+	  InStream.java MemInStream.java ZlibInStream.java \
+	  VNCProxyConnectSocketWrapper.java SocketWrapper.java InvirtTrustManager.java
 
 all: $(CLASSES) $(ARCHIVE)
 
Index: invirt-vnc-client/RfbProto.java
===================================================================
--- invirt-vnc-client.orig/RfbProto.java	2007-04-26 22:36:00.000000000 -0400
+++ invirt-vnc-client/RfbProto.java	2008-10-31 06:09:10.000000000 -0400
@@ -208,11 +208,13 @@
     port = p;
 
     if (viewer.socketFactory == null) {
+	System.out.println("Null socketFactory");
       sock = new Socket(host, port);
     } else {
       try {
 	Class factoryClass = Class.forName(viewer.socketFactory);
 	SocketFactory factory = (SocketFactory)factoryClass.newInstance();
+	System.out.println("Using socketFactory " + factory);
 	if (viewer.inAnApplet)
 	  sock = factory.createSocket(host, port, viewer);
 	else
@@ -236,7 +238,7 @@
     try {
       sock.close();
       closed = true;
-      System.out.println("RFB socket closed");
+      System.out.println("RFB socket closed " + sock);
       if (rec != null) {
 	rec.close();
 	rec = null;
Index: invirt-vnc-client/SocketWrapper.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ invirt-vnc-client/SocketWrapper.java	2008-10-31 06:09:10.000000000 -0400
@@ -0,0 +1,263 @@
+/*
+ * Written by Dawid Kurzyniec and released to the public domain, as explained
+ * at http://creativecommons.org/licenses/publicdomain
+ */
+// Upstream is at http://www.dcl.mathcs.emory.edu/downloads/h2o/doc/api/edu/emory/mathcs/util/net/SocketWrapper.html
+
+//package edu.emory.mathcs.util.net;
+
+import java.io.*;
+import java.net.*;
+import java.nio.channels.*;
+
+/**
+ * Wrapper for sockets which enables to add functionality in subclasses
+ * on top of existing, connected sockets. It is useful when direct subclassing
+ * of delegate socket class is not possible, e.g. if the delegate socket is
+ * created by a library. Possible usage example is socket factory chaining.
+ * This class delegates all socket-related requests to the wrapped delegate,
+ * as of JDK 1.4.
+ *
+ * @author Dawid Kurzyniec
+ * @version 1.4
+ */
+public abstract class SocketWrapper extends Socket {
+
+    /**
+     * the wrapped delegate socket.
+     */
+    protected final Socket delegate;
+
+    /**
+     * Creates new socket wrapper for a given socket. The delegate
+     * must be connected and bound and it must not be closed.
+     * @param delegate the delegate socket to wrap
+     * @throws SocketException if the delegate socket is closed, not bound,
+     *                         or not connected
+     */
+    protected SocketWrapper(Socket delegate) throws SocketException {
+        super(new WrappingSocketImpl(delegate));
+        this.delegate = delegate;
+	System.out.println("Creating SocketWrapper $Rev$");
+    }
+
+    public SocketChannel getChannel() {
+        return delegate.getChannel();
+    }
+
+    /**
+     * Returns true, indicating that the socket is bound.
+     *
+     * @return true
+     */
+    public boolean isBound() {
+        return true;
+    }
+
+    public boolean isClosed() {
+        return super.isClosed() || delegate.isClosed();
+    }
+
+    /**
+     * Returns true, indicating that the socket is connected.
+     *
+     * @return true
+     */
+    public boolean isConnected() {
+        return true;
+    }
+
+    public boolean isInputShutdown() {
+        return super.isInputShutdown() || delegate.isInputShutdown();
+    }
+
+    public boolean isOutputShutdown() {
+        return super.isInputShutdown() || delegate.isOutputShutdown();
+    }
+
+    private static class WrappingSocketImpl extends SocketImpl {
+        private final Socket delegate;
+        WrappingSocketImpl(Socket delegate) throws SocketException {
+            if (delegate == null) {
+                throw new NullPointerException();
+            }
+            if (delegate.isClosed()) {
+                throw new SocketException("Delegate server socket is closed");
+            }
+            if (!(delegate.isBound())) {
+                throw new SocketException("Delegate server socket is not bound");
+            }
+            if (!(delegate.isConnected())) {
+                throw new SocketException("Delegate server socket is not connected");
+            }
+            this.delegate = delegate;
+        }
+
+        protected void create(boolean stream) {}
+
+        protected void connect(String host, int port) {
+            // delegate is always connected, thus this method is never called
+            throw new UnsupportedOperationException();
+        }
+
+        protected void connect(InetAddress address, int port) {
+            // delegate is always connected, thus this method is never called
+            throw new UnsupportedOperationException();
+        }
+
+        protected void connect(SocketAddress address, int timeout) {
+            // delegate is always connected, thus this method is never called
+            throw new UnsupportedOperationException();
+        }
+
+        protected void bind(InetAddress host, int port) {
+            // delegate is always bound, thus this method is never called
+            throw new UnsupportedOperationException();
+        }
+
+        protected void listen(int backlog) {
+            // this wrapper is never used by a ServerSocket
+            throw new UnsupportedOperationException();
+        }
+
+        protected void accept(SocketImpl s) {
+            // this wrapper is never used by a ServerSocket
+            throw new UnsupportedOperationException();
+        }
+
+        protected InputStream getInputStream() throws IOException {
+            return delegate.getInputStream();
+        }
+
+        protected OutputStream getOutputStream() throws IOException {
+            return delegate.getOutputStream();
+        }
+
+        protected int available() throws IOException {
+            return getInputStream().available();
+        }
+
+        protected void close() throws IOException {
+	    System.out.println("Calling delegate.close");
+            delegate.close();
+        }
+
+        protected void shutdownInput() throws IOException {
+            delegate.shutdownInput();
+        }
+
+        protected void shutdownOutput() throws IOException {
+            delegate.shutdownOutput();
+        }
+
+        protected FileDescriptor getFileDescriptor() {
+            // this wrapper is never used by a ServerSocket
+            throw new UnsupportedOperationException();
+        }
+
+        protected InetAddress getInetAddress() {
+            return delegate.getInetAddress();
+        }
+
+        protected int getPort() {
+            return delegate.getPort();
+        }
+
+        protected boolean supportsUrgentData() {
+            return false; // must be overridden in sub-class
+        }
+
+        protected void sendUrgentData (int data) throws IOException {
+            delegate.sendUrgentData(data);
+        }
+
+        protected int getLocalPort() {
+            return delegate.getLocalPort();
+        }
+
+        public Object getOption(int optID) throws SocketException {
+            switch (optID) {
+                case SocketOptions.IP_TOS:
+                    return new Integer(delegate.getTrafficClass());
+                case SocketOptions.SO_BINDADDR:
+                    return delegate.getLocalAddress();
+                case SocketOptions.SO_KEEPALIVE:
+                    return Boolean.valueOf(delegate.getKeepAlive());
+                case SocketOptions.SO_LINGER:
+                    return new Integer(delegate.getSoLinger());
+                case SocketOptions.SO_OOBINLINE:
+                    return Boolean.valueOf(delegate.getOOBInline());
+                case SocketOptions.SO_RCVBUF:
+                    return new Integer(delegate.getReceiveBufferSize());
+                case SocketOptions.SO_REUSEADDR:
+                    return Boolean.valueOf(delegate.getReuseAddress());
+                case SocketOptions.SO_SNDBUF:
+                    return new Integer(delegate.getSendBufferSize());
+                case SocketOptions.SO_TIMEOUT:
+                    return new Integer(delegate.getSoTimeout());
+                case SocketOptions.TCP_NODELAY:
+                    return Boolean.valueOf(delegate.getTcpNoDelay());
+                case SocketOptions.SO_BROADCAST:
+                default:
+                    throw new IllegalArgumentException("Unsupported option type");
+            }
+        }
+
+        public void setOption(int optID, Object value) throws SocketException {
+            switch (optID) {
+                case SocketOptions.SO_BINDADDR:
+                    throw new IllegalArgumentException("Socket is bound");
+                case SocketOptions.SO_KEEPALIVE:
+                    delegate.setKeepAlive(((Boolean)value).booleanValue());
+                    break;
+                case SocketOptions.SO_LINGER:
+                    if (value instanceof Boolean) {
+                        delegate.setSoLinger(((Boolean)value).booleanValue(), 0);
+                    }
+                    else {
+                        delegate.setSoLinger(true, ((Integer)value).intValue());
+                    }
+                    break;
+                case SocketOptions.SO_OOBINLINE:
+                    delegate.setOOBInline(((Boolean)value).booleanValue());
+                    break;
+                case SocketOptions.SO_RCVBUF:
+                    delegate.setReceiveBufferSize(((Integer)value).intValue());
+                    break;
+                case SocketOptions.SO_REUSEADDR:
+                    delegate.setReuseAddress(((Boolean)value).booleanValue());
+                    break;
+                case SocketOptions.SO_SNDBUF:
+                    delegate.setSendBufferSize(((Integer)value).intValue());
+                    break;
+                case SocketOptions.SO_TIMEOUT:
+                    delegate.setSoTimeout(((Integer)value).intValue());
+                    break;
+                case SocketOptions.TCP_NODELAY:
+                    delegate.setTcpNoDelay(((Boolean)value).booleanValue());
+                    break;
+                case SocketOptions.SO_BROADCAST:
+                default:
+                    throw new IllegalArgumentException("Unsupported option type");
+            }
+        }
+    }
+
+    public void close() throws IOException {
+	System.out.println("Calling SocketWrapper.delegate.close");
+	delegate.close();
+    }
+
+    public boolean equals(Object obj) {
+        if (!(obj instanceof SocketWrapper)) return false;
+        SocketWrapper that = (SocketWrapper)obj;
+        return this.delegate.equals(that.delegate);
+    }
+
+    public int hashCode() {
+        return delegate.hashCode() ^ 0x01010101;
+    }
+    public String toString() {
+	return "<SocketWrapper " + super.toString() + "(delegating to " + delegate.toString() +  ")" + ">";
+    }
+}
\ No newline at end of file
Index: invirt-vnc-client/VNCProxyConnectSocket.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ invirt-vnc-client/VNCProxyConnectSocket.java	2008-10-31 06:09:10.000000000 -0400
@@ -0,0 +1,61 @@
+//
+//  Copyright (C) 2002 Constantin Kaplinsky, Inc.  All Rights Reserved.
+//  Copyright 2007 MIT Student Information Processing Board
+//
+//  This is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This software is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this software; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+//  USA.
+//
+
+//
+// VNCProxySocket.java together with VNCProxySocketFactory.java
+// implement an alternate way to connect to VNC servers via one or two
+// VNCProxy proxies supporting the VNCProxy VNCCONNECT method.
+//
+
+import java.net.*;
+import java.io.*;
+
+class VNCProxyConnectSocket extends Socket {
+
+  public VNCProxyConnectSocket(String host, int port,
+                               String vmname, String authtoken)
+    throws IOException {
+
+    // Connect to the specified HTTP proxy
+    super(host, port);
+
+    // Send the CONNECT request
+    getOutputStream().write(("CONNECTVNC " + vmname +
+                             " VNCProxy/1.0\r\nAuth-token: " + authtoken +
+                             "\r\n\r\n").getBytes());
+
+    // Read the first line of the response
+    DataInputStream is = new DataInputStream(getInputStream());
+    String str = is.readLine();
+
+    // Check the HTTP error code -- it should be "200" on success
+    if (!str.startsWith("VNCProxy/1.0 200 ")) {
+      if (str.startsWith("VNCProxy/1.0 "))
+        str = str.substring(13);
+      throw new IOException("Proxy reports \"" + str + "\"");
+    }
+
+    // Success -- skip remaining HTTP headers
+    do {
+      str = is.readLine();
+    } while (str.length() != 0);
+  }
+}
+
Index: invirt-vnc-client/VNCProxyConnectSocketFactory.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ invirt-vnc-client/VNCProxyConnectSocketFactory.java	2008-10-31 06:09:10.000000000 -0400
@@ -0,0 +1,98 @@
+//
+//  Copyright (C) 2002 Constantin Kaplinsky, Inc.  All Rights Reserved.
+//  Copyright 2007 MIT Student Information Processing Board
+//
+//  This is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This software is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this software; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+//  USA.
+//
+
+//
+// VNCProxyConnectSocketFactory.java together with VNCProxyConnectSocket.java
+// implement an alternate way to connect to VNC servers via one or two
+// VNCProxy proxies supporting the VNCProxy CONNECT method.
+//
+
+import java.applet.*;
+import java.net.*;
+import javax.net.ssl.*;
+import java.io.*;
+
+class VNCProxyConnectSocketFactory implements SocketFactory {
+
+    SSLSocketFactory factory;
+    
+    public VNCProxyConnectSocketFactory() {
+	try {
+	    SSLContext c = SSLContext.getInstance("SSL");
+	    c.init(null,
+		   new TrustManager[] { new InvirtTrustManager() },
+		   null);
+	    factory =
+		(SSLSocketFactory)c.getSocketFactory();
+	} catch (Exception e) {
+	    e.printStackTrace();
+	}
+    }
+
+  public Socket createSocket(String host, int port, Applet applet)
+    throws IOException {
+
+    return createSocket(host, port,
+			applet.getParameter("VMNAME"),
+			applet.getParameter("AUTHTOKEN"));
+  }
+
+  public Socket createSocket(String host, int port, String[] args)
+    throws IOException {
+
+    return createSocket(host, port,
+			readArg(args, "VMNAME"),
+			readArg(args, "AUTHTOKEN"));
+  }
+
+  public Socket createSocket(String host, int port,
+			     String vmname, String authtoken)
+    throws IOException {
+
+    if (vmname == null || authtoken == null) {
+      System.out.println("Incomplete parameter list for VNCProxyConnectSocket");
+      return new Socket(host, port);
+    }
+
+    System.out.println("VNCProxy CONNECT via proxy " + host +
+		       " port " + port + " to vm " + vmname);
+    SSLSocket ssls = (SSLSocket)factory.createSocket(host, port);
+    ssls.startHandshake();
+    VNCProxyConnectSocketWrapper s =
+      new VNCProxyConnectSocketWrapper(ssls, vmname, authtoken);
+
+    return (Socket)s;
+  }
+
+  private String readArg(String[] args, String name) {
+
+    for (int i = 0; i < args.length; i += 2) {
+      if (args[i].equalsIgnoreCase(name)) {
+	try {
+	  return args[i+1];
+	} catch (Exception e) {
+	  return null;
+	}
+      }
+    }
+    return null;
+  }
+}
+
Index: invirt-vnc-client/VNCProxyConnectSocketWrapper.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ invirt-vnc-client/VNCProxyConnectSocketWrapper.java	2008-10-31 06:09:10.000000000 -0400
@@ -0,0 +1,60 @@
+//
+//  Copyright (C) 2002 Constantin Kaplinsky, Inc.  All Rights Reserved.
+//  Copyright 2007 MIT Student Information Processing Board
+//
+//  This is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This software is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this software; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+//  USA.
+//
+
+//
+// VNCProxySocket.java together with VNCProxySocketFactory.java
+// implement an alternate way to connect to VNC servers via one or two
+// VNCProxy proxies supporting the VNCProxy VNCCONNECT method.
+//
+
+import java.net.*;
+import java.io.*;
+
+class VNCProxyConnectSocketWrapper extends SocketWrapper {
+
+  public VNCProxyConnectSocketWrapper(Socket sock,
+                               String vmname, String authtoken)
+    throws IOException {
+
+    super(sock);
+
+    // Send the CONNECT request
+    getOutputStream().write(("CONNECTVNC " + vmname +
+                             " VNCProxy/1.0\r\nAuth-token: " + authtoken +
+                             "\r\n\r\n").getBytes());
+
+    // Read the first line of the response
+    DataInputStream is = new DataInputStream(getInputStream());
+    String str = is.readLine();
+
+    // Check the HTTP error code -- it should be "200" on success
+    if (!str.startsWith("VNCProxy/1.0 200 ")) {
+      if (str.startsWith("VNCProxy/1.0 "))
+        str = str.substring(13);
+      throw new IOException("Proxy reports \"" + str + "\"");
+    }
+
+    // Success -- skip remaining HTTP headers
+    do {
+      str = is.readLine();
+    } while (str.length() != 0);
+  }
+}
+
