+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,122 @@
++/*
++ * 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,262 @@
++/*
++ * Written by Dawid Kurzyniec and released to the public domain, as explained
++ * at http://creativecommons.org/licenses/publicdomain
++ */
++
++//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);
++ }
++}
++