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) {