diff options
| author | Christian Beier <dontmind@freeshell.org> | 2011-11-17 17:03:45 +0100 | 
|---|---|---|
| committer | Christian Beier <dontmind@freeshell.org> | 2011-11-17 17:03:45 +0100 | 
| commit | 49e59435e32401dcaa7885bab1d2e49b5b2efaf9 (patch) | |
| tree | df0b4dbc4cc52942df7e629483efb8c2c327f00b /webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch | |
| parent | efdb2f06d4803c76871e4b5b5958584c0be9cc3a (diff) | |
| parent | bdd7e25d2dd47c3a5d8c95366bc111dc0a176128 (diff) | |
| download | libtdevnc-49e59435e32401dcaa7885bab1d2e49b5b2efaf9.tar.gz libtdevnc-49e59435e32401dcaa7885bab1d2e49b5b2efaf9.zip | |
Merge branch 'included-novnc'
Diffstat (limited to 'webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch')
| -rw-r--r-- | webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch | 5494 | 
1 files changed, 5494 insertions, 0 deletions
| diff --git a/webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch b/webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch new file mode 100644 index 0000000..3309860 --- /dev/null +++ b/webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch @@ -0,0 +1,5494 @@ +diff -Naur JavaViewer.orig/ButtonPanel.java JavaViewer/ButtonPanel.java +--- JavaViewer.orig/ButtonPanel.java	2004-12-12 20:51:02.000000000 -0500 ++++ JavaViewer/ButtonPanel.java	2007-05-31 15:40:45.000000000 -0400 +@@ -43,30 +43,36 @@ +     viewer = v; +  +     setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); +-    disconnectButton = new Button("Disconnect"); ++	if (v.ftpOnly) { ++		disconnectButton = new Button("Quit"); ++	} else { ++		disconnectButton = new Button("Close"); ++	} +     disconnectButton.setEnabled(false); +     add(disconnectButton); +     disconnectButton.addActionListener(this); +-    optionsButton = new Button("Options"); +-    add(optionsButton); +-    optionsButton.addActionListener(this); +-    clipboardButton = new Button("Clipboard"); +-    clipboardButton.setEnabled(false); +-    add(clipboardButton); +-    clipboardButton.addActionListener(this); +-    if (viewer.rec != null) { +-      recordButton = new Button("Record"); +-      add(recordButton); +-      recordButton.addActionListener(this); +-    } +-    ctrlAltDelButton = new Button("Send Ctrl-Alt-Del"); +-    ctrlAltDelButton.setEnabled(false); +-    add(ctrlAltDelButton); +-    ctrlAltDelButton.addActionListener(this); +-    refreshButton = new Button("Refresh"); +-    refreshButton.setEnabled(false); +-    add(refreshButton); +-    refreshButton.addActionListener(this); ++	if (!v.ftpOnly) { ++		optionsButton = new Button("Options"); ++		add(optionsButton); ++		optionsButton.addActionListener(this); ++		clipboardButton = new Button("Clipboard"); ++		clipboardButton.setEnabled(false); ++		add(clipboardButton); ++		clipboardButton.addActionListener(this); ++		if (viewer.rec != null) { ++			recordButton = new Button("Record"); ++			add(recordButton); ++			recordButton.addActionListener(this); ++		} ++		ctrlAltDelButton = new Button("Send Ctrl-Alt-Del"); ++		ctrlAltDelButton.setEnabled(false); ++		add(ctrlAltDelButton); ++		ctrlAltDelButton.addActionListener(this); ++		refreshButton = new Button("Refresh"); ++		refreshButton.setEnabled(false); ++		add(refreshButton); ++		refreshButton.addActionListener(this); ++	} +     ftpButton = new Button("File Transfer"); +     ftpButton.setEnabled(false); +     add(ftpButton); +@@ -79,9 +85,10 @@ +  +   public void enableButtons() { +     disconnectButton.setEnabled(true); ++    ftpButton.setEnabled(true); ++    if (viewer.ftpOnly) {return;} +     clipboardButton.setEnabled(true); +     refreshButton.setEnabled(true); +-    ftpButton.setEnabled(true); +   } +  +   // +@@ -89,6 +96,9 @@ +   // +  +   public void disableButtonsOnDisconnect() { ++    ftpButton.setEnabled(false); ++    if (viewer.ftpOnly) {return;} ++ +     remove(disconnectButton); +     disconnectButton = new Button("Hide desktop"); +     disconnectButton.setEnabled(true); +@@ -99,7 +109,6 @@ +     clipboardButton.setEnabled(false); +     ctrlAltDelButton.setEnabled(false); +     refreshButton.setEnabled(false); +-    ftpButton.setEnabled(false); +  +     validate(); +   } +@@ -110,6 +119,7 @@ +   // +  +   public void enableRemoteAccessControls(boolean enable) { ++    if (viewer.ftpOnly) {return;} +     ctrlAltDelButton.setEnabled(enable); +   } +  +@@ -163,9 +173,19 @@ +     } +     else if (evt.getSource() == ftpButton) +     { +-		viewer.ftp.setVisible(!viewer.ftp.isVisible()); ++// begin runge/x11vnc ++		if (viewer.ftpOnly) { ++			viewer.vncFrame.setVisible(false); ++		} ++		viewer.ftp.setSavedLocations(); ++		if (viewer.ftp.isVisible()) { ++			viewer.ftp.doClose(); ++		} else { ++			viewer.ftp.doOpen(); ++		} ++// end runge/x11vnc + 		viewer.rfb.readServerDriveList(); +-	 ++ +     } +   } + } +diff -Naur JavaViewer.orig/FTPFrame.java JavaViewer/FTPFrame.java +--- JavaViewer.orig/FTPFrame.java	2005-03-15 23:53:14.000000000 -0500 ++++ JavaViewer/FTPFrame.java	2009-01-13 09:48:30.000000000 -0500 +@@ -24,8 +24,17 @@ + import java.io.*; + import java.util.ArrayList; + import java.util.Vector; ++import java.util.Date; + import javax.swing.*; +  ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.*; ++ ++// begin runge/x11vnc ++import java.util.Arrays; ++// end runge/x11vnc ++ +  + /* +  * Created on Feb 25, 2004 +@@ -74,12 +83,31 @@ + 	public javax.swing.JTextField connectionStatus = null; + 	public boolean updateDriveList; + 	private Vector remoteList = null; ++	private Vector remoteListInfo = null; + 	private Vector localList = null; ++	private Vector localListInfo = null; + 	private File currentLocalDirectory = null;	// Holds the current local Directory + 	private File currentRemoteDirectory = null;	// Holds the current remote Directory + 	private File localSelection = null;		// Holds the currently selected local file   + 	private String remoteSelection = null;	// Holds the currently selected remote file + 	public String selectedTable = null; ++ ++// begin runge/x11vnc ++	private javax.swing.JButton viewButton = null; ++	private javax.swing.JButton refreshButton = null; ++	public File saveLocalDirectory = null; ++	public long saveLocalDirectoryTime = 0; ++	public int saveLocalDirectoryCount = 0; ++	public String saveRemoteDirectory = null; ++	public long saveRemoteDirectoryTime = 0; ++	public int saveRemoteDirectoryCount = 0; ++	private boolean localCurrentIsDir = true; ++	private int lastRemoteIndex = -1; ++	private int lastLocalIndex = -1; ++	private boolean doingShortcutDir = false; ++	private boolean gotShortcutDir = false; ++	private boolean ignore_events = false; ++// end   runge/x11vnc + 	 + //	 sf@2004 - Separate directories and files for better lisibility + 	private ArrayList DirsList; +@@ -125,11 +153,61 @@ + 	  + 	 void refreshRemoteLocation() + 	 { ++ ++//System.out.println("refreshRemoteLocation1"); + 		remoteList.clear(); ++		remoteListInfo.clear(); + 		remoteFileTable.setListData(remoteList);	 ++System.out.println("refreshRemoteLocation '" + remoteLocation.getText() + "'");	// runge/x11vnc + 		viewer.rfb.readServerDirectory(remoteLocation.getText()); + 	 } + 	  ++// begin runge/x11vnc ++	 public void setSavedLocations() { ++		saveLocalDirectory = currentLocalDirectory; ++		saveLocalDirectoryTime = System.currentTimeMillis(); ++		saveLocalDirectoryCount = 0; ++ ++		if (remoteLocation != null) { ++			saveRemoteDirectory = remoteLocation.getText(); ++System.out.println("RemoteSave '" + saveRemoteDirectory + "'"); ++		} ++		saveRemoteDirectoryTime = System.currentTimeMillis(); ++		saveRemoteDirectoryCount = 0; ++	 } ++ ++	private File saveLocalHack(File dir) { ++		saveLocalDirectoryCount++; ++//System.out.println("L " + saveLocalDirectoryCount + " dt: " + (System.currentTimeMillis() - saveLocalDirectoryTime) + " - " + saveLocalDirectory); ++		if (System.currentTimeMillis() > saveLocalDirectoryTime + 2000 || saveLocalDirectoryCount > 2) { ++			saveLocalDirectory = null; ++		} ++		if (saveLocalDirectory != null) { ++			currentLocalDirectory = saveLocalDirectory; ++			localLocation.setText(saveLocalDirectory.toString()); ++			return saveLocalDirectory; ++		} else { ++			return dir; ++		} ++	} ++ ++	private String saveRemoteHack(String indrive) { ++		saveRemoteDirectoryCount++; ++//System.out.println("R " + saveRemoteDirectoryCount + " - " + saveRemoteDirectory); ++		if (saveRemoteDirectory != null && saveRemoteDirectoryCount > 1) { ++			saveRemoteDirectory = null; ++		} ++		if (saveRemoteDirectory != null) { ++			if (! saveRemoteDirectory.equals("")) { ++System.out.println("saveRemoteHack setText + refreshRemoteLocation '" + saveRemoteDirectory + "'"); ++				return saveRemoteDirectory; ++			} ++		} ++		return indrive; ++	} ++// end runge/x11vnc ++ ++ + 	/* + 	 * Prints the list of drives on the remote directory and returns a String[].   + 	 * str takes as string like A:fC:lD:lE:lF:lG:cH:c +@@ -143,6 +221,9 @@ + 		int size = str.length(); + 		String driveType = null; + 		String[] drive = new String[str.length() / 3]; ++		int idx = 0, C_drive = -1, O_drive = -1; ++ ++System.out.println("ComboBox: Str   '" + str + "'"); +  + 		// Loop through the string to create a String[] + 		for (int i = 0; i < size; i = i + 3) { +@@ -150,26 +231,68 @@ + 			driveType = str.substring(i + 2, i + 3); + 			if (driveType.compareTo("f") == 0) + 				drive[i / 3] += "\\ Floppy"; +-			if (driveType.compareTo("l") == 0) ++			if (driveType.compareTo("l") == 0) { + 				drive[i / 3] += "\\ Local Disk"; ++				if (drive[i/3].substring(0,1).toUpperCase().equals("C")) { ++					C_drive = idx; ++				} else if (O_drive < 0) { ++					O_drive = idx; ++				} ++			} + 			if (driveType.compareTo("c") == 0) + 				drive[i / 3] += "\\ CD-ROM"; + 			if (driveType.compareTo("n") == 0) + 				drive[i / 3] += "\\ Network"; +  + 			remoteDrivesComboBox.addItem(drive[i / 3]); ++System.out.println("ComboBox: Add " + idx + " '" + drive[i/3] + "'"); ++			idx++; ++		} ++ ++		// runge ++		if (viewer.ftpDropDown != null) { ++			String[] dd = viewer.ftpDropDown.split("\\."); ++			for (int i=0; i < dd.length; i++) { ++				if (!dd[i].equals("")) { ++					String s = dd[i]; ++					if (s.startsWith("TOP_")) { ++						s = s.substring(4); ++						remoteDrivesComboBox.insertItemAt(" [" + s + "]", 0); ++					} else { ++						remoteDrivesComboBox.addItem(" [" + s + "]"); ++					} ++				} ++			} ++		} else { ++			remoteDrivesComboBox.addItem(" [My Documents]"); ++			remoteDrivesComboBox.addItem(" [Desktop]"); ++			remoteDrivesComboBox.addItem(" [Home]"); + 		} ++ + 		//sf@ - Select Drive C:as default if possible + 		boolean bFound = false; +-		for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++) +-		{ +-			if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C")) +-			{ +-				remoteDrivesComboBox.setSelectedIndex(i); ++ ++		if (false) { ++			for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++) { ++				if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C")) { ++					remoteDrivesComboBox.setSelectedIndex(i); ++					bFound = true; ++				} ++			} ++		} else { ++			if (C_drive >= 0) { ++				remoteDrivesComboBox.setSelectedIndex(C_drive); ++				bFound = true; ++System.out.println("ComboBox: C_drive index: " + C_drive); ++			} else if (O_drive >= 0) { ++				remoteDrivesComboBox.setSelectedIndex(O_drive); + 				bFound = true; ++System.out.println("ComboBox: Other_drive index: " + O_drive); + 			} + 		} ++ + 		if (!bFound) remoteDrivesComboBox.setSelectedIndex(0); ++ + 		updateDriveList = false; + 		return drive; + 	} +@@ -185,6 +308,8 @@ + 		stopButton.setVisible(true); + 		stopButton.setEnabled(true); + 		receiveButton.setEnabled(false); ++		viewButton.setEnabled(false);	// runge/x11vnc ++		refreshButton.setEnabled(false); + 		remoteTopButton.setEnabled(false); + 		sendButton.setEnabled(false); + 		remoteFileTable.setEnabled(false); +@@ -207,6 +332,8 @@ + 		stopButton.setVisible(false); + 		stopButton.setEnabled(false); + 		receiveButton.setEnabled(true); ++		viewButton.setEnabled(true);	// runge/x11vnc ++		refreshButton.setEnabled(true); + 		remoteTopButton.setEnabled(true); + 		sendButton.setEnabled(true); + 		remoteFileTable.setEnabled(true); +@@ -221,10 +348,11 @@ + 	/* + 	 * Print Directory prints out all the contents of a directory + 	 */ +-	void printDirectory(ArrayList a) { ++	void printDirectory(ArrayList a, ArrayList b) { +  + 		for (int i = 0; i < a.size(); i++) { + 			remoteList.addElement(a.get(i)); ++			remoteListInfo.addElement(b.get(i)); + 		} + 		remoteFileTable.setListData(remoteList); + 	} +@@ -235,10 +363,12 @@ + 	 * @return void + 	 */ + 	private void initialize() { ++		ignore_events = true; + 		this.setSize(794, 500); + 		this.setContentPane(getJContentPane()); ++		ignore_events = false; + 		updateDriveList = true; +-		} ++	} + 	/** + 	 * This method initializes jContentPane.  This is the main content pane + 	 *  +@@ -253,6 +383,33 @@ + 			jContentPane.add(getRemotePanel(), java.awt.BorderLayout.EAST); + 			jContentPane.add(getLocalPanel(), java.awt.BorderLayout.WEST); + 			jContentPane.add(getButtonPanel(), java.awt.BorderLayout.CENTER); ++ ++			KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); ++			AbstractAction escapeAction = new AbstractAction() { ++				public void actionPerformed(ActionEvent actionEvent) { ++					System.out.println("Escape Pressed"); ++					if (viewer.ftpOnly) { ++						System.out.println("exiting..."); ++						System.exit(0); ++					} else { ++						doClose(); ++					} ++				} ++			}; ++			jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction"); ++			jContentPane.getInputMap().put(stroke, "escapeAction"); ++			jContentPane.getActionMap().put("escapeAction", escapeAction); ++ ++			stroke = KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK); ++			AbstractAction resetAction = new AbstractAction() { ++				public void actionPerformed(ActionEvent actionEvent) { ++					System.out.println("Ctrl-R Pressed"); ++					doReset(); ++				} ++			}; ++			jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "resetAction"); ++			jContentPane.getInputMap().put(stroke, "resetAction"); ++			jContentPane.getActionMap().put("resetAction", resetAction); + 		} + 		return jContentPane; + 	} +@@ -270,6 +427,7 @@ + 			topPanelLocal.add(getLocalMachineLabel(), java.awt.BorderLayout.CENTER); + 			topPanelLocal.add(getLocalTopButton(), java.awt.BorderLayout.EAST); + 			topPanelLocal.setBackground(java.awt.Color.lightGray); ++//System.out.println("getTopPanelLocal"); + 		} + 		return topPanelLocal; + 	} +@@ -288,6 +446,7 @@ + 			topPanelRemote.add(getRemoteMachineLabel(), java.awt.BorderLayout.CENTER); + 			topPanelRemote.add(getRemoteTopButton(), java.awt.BorderLayout.EAST); + 			topPanelRemote.setBackground(java.awt.Color.lightGray); ++//System.out.println("getTopPanelRemote"); + 		} + 		return topPanelRemote; + 	} +@@ -301,6 +460,7 @@ + 		if (topPanelCenter == null) { + 			topPanelCenter = new javax.swing.JPanel(); + 			topPanelCenter.add(getDummyButton(), null); ++//System.out.println("getTopPanelCenter"); + 		} + 		return topPanelCenter; + 	} +@@ -328,6 +488,7 @@ + 			topPanel.add(getRemoteTopButton(), null); + 			topPanel.setBackground(java.awt.Color.lightGray); + 			*/ ++//System.out.println("getTopPanel"); + 		} + 		return topPanel; + 	} +@@ -348,6 +509,7 @@ + 			statusPanel.add(getJProgressBar(), null); + 			statusPanel.add(getConnectionStatus(), null); + 			statusPanel.setBackground(java.awt.Color.lightGray); ++//System.out.println("getStatusPanel"); + 			 + 		} + 		return statusPanel; +@@ -368,6 +530,7 @@ + 			remotePanel.add(getRemoteScrollPane(), null); + 			remotePanel.add(getRemoteStatus(), null); + 			remotePanel.setBackground(java.awt.Color.lightGray); ++//System.out.println("getRemotePanel"); + 		} + 		return remotePanel; + 	} +@@ -390,6 +553,7 @@ + 			localPanel.setComponentOrientation( + 				java.awt.ComponentOrientation.UNKNOWN); + 			localPanel.setName("localPanel"); ++//System.out.println("getLocalPanel"); + 		} + 		return localPanel; + 	} +@@ -405,12 +569,15 @@ + 			buttonPanel = new javax.swing.JPanel(); + 			buttonPanel.setLayout(null); + 			buttonPanel.add(getReceiveButton(), null); ++			buttonPanel.add(getRefreshButton(), null);	// runge/x11vnc ++			buttonPanel.add(getViewButton(), null);	// runge/x11vnc + 			buttonPanel.add(getNewFolderButton(), null); + 			buttonPanel.add(getCloseButton(), null); + 			buttonPanel.add(getDeleteButton(), null); + 			buttonPanel.add(getSendButton(), null); + 			buttonPanel.add(getStopButton(), null); + 			buttonPanel.setBackground(java.awt.Color.lightGray); ++//System.out.println("getButtonPanel"); + 		} + 		return buttonPanel; + 	} +@@ -422,10 +589,11 @@ + 	private javax.swing.JButton getSendButton() { + 		if (sendButton == null) { + 			sendButton = new javax.swing.JButton(); +-			sendButton.setBounds(20, 30, 97, 25); ++			sendButton.setBounds(15, 30, 107, 25);	// runge/x11vnc + 			sendButton.setText("Send >>"); + 			sendButton.setName("sendButton"); + 			sendButton.addActionListener(this); ++//System.out.println("getSendButton"); +  + 		} + 		return sendButton; +@@ -438,7 +606,7 @@ + 	private javax.swing.JButton getReceiveButton() { + 		if (receiveButton == null) { + 			receiveButton = new javax.swing.JButton(); +-			receiveButton.setBounds(20, 60, 97, 25); ++			receiveButton.setBounds(15, 60, 107, 25);	// runge/x11vnc + 			receiveButton.setText("<< Receive"); + 			receiveButton.setName("receiveButton"); + 			receiveButton.addActionListener(this); +@@ -453,7 +621,7 @@ + 	private javax.swing.JButton getDeleteButton() { + 		if (deleteButton == null) { + 			deleteButton = new javax.swing.JButton(); +-			deleteButton.setBounds(20, 110, 97, 25); ++			deleteButton.setBounds(15, 110, 107, 25);	// runge/x11vnc + 			deleteButton.setText("Delete File"); + 			deleteButton.setName("deleteButton"); + 			deleteButton.addActionListener(this); +@@ -468,7 +636,7 @@ + 	private javax.swing.JButton getNewFolderButton() { + 		if (newFolderButton == null) { + 			newFolderButton = new javax.swing.JButton(); +-			newFolderButton.setBounds(20, 140, 97, 25); ++			newFolderButton.setBounds(15, 140, 107, 25);	// runge/x11vnc + 			newFolderButton.setText("New Folder"); + 			newFolderButton.setName("newFolderButton"); + 			newFolderButton.addActionListener(this); +@@ -476,6 +644,39 @@ + 		return newFolderButton; + 	} + 	 ++// begin runge/x11vnc ++	/** ++	 * This method initializes refreshButton ++	 *  ++	 * @return javax.swing.JButton ++	 */ ++	private javax.swing.JButton getRefreshButton() { ++		if (refreshButton == null) { ++			refreshButton = new javax.swing.JButton(); ++			refreshButton.setBounds(15, 170, 107, 25); ++			refreshButton.setText("Refresh"); ++			refreshButton.setName("refreshButton"); ++			refreshButton.addActionListener(this); ++		} ++		return refreshButton; ++	} ++	/** ++	 * This method initializes viewButton ++	 *  ++	 * @return javax.swing.JButton ++	 */ ++	private javax.swing.JButton getViewButton() { ++		if (viewButton == null) { ++			viewButton = new javax.swing.JButton(); ++			viewButton.setBounds(15, 200, 107, 25); ++			viewButton.setText("View File"); ++			viewButton.setName("viewButton"); ++			viewButton.addActionListener(this); ++		} ++		return viewButton; ++	} ++// end   runge/x11vnc ++ + 	/** + 	 * This method initializes stopButton + 	 *  +@@ -486,7 +687,7 @@ + 		if (stopButton == null) + 		{ + 			stopButton = new javax.swing.JButton(); +-			stopButton.setBounds(20, 200, 97, 25); ++			stopButton.setBounds(15, 230, 107, 25);	// runge/x11vnc + 			stopButton.setText("Stop"); + 			stopButton.setName("stopButton"); + 			stopButton.addActionListener(this); +@@ -503,8 +704,12 @@ + 	private javax.swing.JButton getCloseButton() { + 		if (closeButton == null) { + 			closeButton = new javax.swing.JButton(); +-			closeButton.setBounds(20, 325, 97, 25); +-			closeButton.setText("Close"); ++			closeButton.setBounds(15, 325, 107, 25);	// runge/x11vnc ++			if (viewer.ftpOnly) { ++				closeButton.setText("Quit"); ++			} else { ++				closeButton.setText("Close"); ++			} + 			closeButton.setName("closeButton"); + 			closeButton.addActionListener(this); + 		} +@@ -551,6 +756,7 @@ + 			//Select the second entry (e.g. C:\) + 			// localDrivesComboBox.setSelectedIndex(1); + 			localDrivesComboBox.addActionListener(this); ++//System.out.println("getLocalDrivesComboBox"); + 		} + 		updateDriveList = false; + 		return localDrivesComboBox; +@@ -567,6 +773,7 @@ + 			remoteDrivesComboBox.setFont( + 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); + 			remoteDrivesComboBox.addActionListener(this); ++//System.out.println("getRemoteDrivesComboBox"); +  + 		} + 		return remoteDrivesComboBox; +@@ -587,6 +794,7 @@ + 			localMachineLabel.setFont( + 				new java.awt.Font("Dialog", java.awt.Font.BOLD, 11)); + 			localMachineLabel.setEditable(false); ++//System.out.println("getLocalMachineLabel"); + 		} + 		return localMachineLabel; + 	} +@@ -622,6 +830,7 @@ + 			localTopButton.setFont( + 				new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); + 			localTopButton.addActionListener(this); ++//System.out.println("getLocalTopButton"); + 		} + 		return localTopButton; + 	} +@@ -638,6 +847,7 @@ + 			remoteTopButton.setFont( + 				new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); + 			remoteTopButton.addActionListener(this); ++//System.out.println("getRemoteTopButton"); + 		} + 		return remoteTopButton; + 	} +@@ -650,9 +860,24 @@ + 	private javax.swing.JList getLocalFileTable() { + 		if (localFileTable == null) { + 			localList = new Vector(0); ++			localListInfo = new Vector(0); + 			localFileTable = new JList(localList); ++			MouseMotionListener mlisten = new MouseMotionAdapter() { ++				public void mouseMoved(MouseEvent e) { ++					int index = localFileTable.locationToIndex(e.getPoint()); ++					if (index == lastLocalIndex) { ++						return; ++					} else if (index < 0) { ++						return; ++					} ++					lastLocalIndex = index; ++					connectionStatus.setText((String) localListInfo.get(index)); ++				} ++			}; + 			localFileTable.addMouseListener(this); ++			localFileTable.addMouseMotionListener(mlisten); + 			localFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); ++//System.out.println("getLocalFileTable"); + 		} + 		return localFileTable; + 	} +@@ -669,6 +894,7 @@ + 			localScrollPane.setFont( + 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); + 			localScrollPane.setName("localFileList"); ++//System.out.println("getLocalScrollPane"); + 		} + 		return localScrollPane; + 	} +@@ -680,10 +906,25 @@ + 	private javax.swing.JList getRemoteFileTable() { + 		if (remoteFileTable == null) { + 			remoteList = new Vector(0); ++			remoteListInfo = new Vector(0); + 			remoteFileTable = new JList(remoteList); ++			MouseMotionListener mlisten = new MouseMotionAdapter() { ++				public void mouseMoved(MouseEvent e) { ++					int index = remoteFileTable.locationToIndex(e.getPoint()); ++					if (index == lastRemoteIndex) { ++						return; ++					} else if (index < 0) { ++						return; ++					} ++					lastRemoteIndex = index; ++					connectionStatus.setText((String) remoteListInfo.get(index)); ++				} ++			}; + 			remoteFileTable.addMouseListener(this); ++			remoteFileTable.addMouseMotionListener(mlisten); + 			remoteFileTable.setSelectedValue("C:\\", false); + 			remoteFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); ++//System.out.println("getRemoteFileTable"); + 			 + 		} + 		return remoteFileTable; +@@ -698,6 +939,7 @@ + 			remoteScrollPane = new javax.swing.JScrollPane(); + 			remoteScrollPane.setViewportView(getRemoteFileTable()); + 			remoteScrollPane.setPreferredSize(new java.awt.Dimension(325, 418)); ++//System.out.println("getRemoteScrollPane"); + 		} + 		return remoteScrollPane; + 	} +@@ -716,6 +958,7 @@ + 			remoteLocation.setBackground(new Color(255,255,238)); + 			remoteLocation.setFont( + 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); ++//System.out.println("getRemoteLocation"); + 		} + 		return remoteLocation; + 	} +@@ -732,6 +975,7 @@ + 			localLocation.setBackground( new Color(255,255,238)); + 			localLocation.setFont( + 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); ++//System.out.println("getLocalLocation"); + 		} + 		return localLocation; + 	} +@@ -748,6 +992,7 @@ + 			localStatus.setFont( + 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); + 			localStatus.setEditable(false); ++//System.out.println("getLocalStatus"); + 		} + 		return localStatus; + 	} +@@ -764,6 +1009,7 @@ + 			remoteStatus.setFont( + 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); + 			remoteStatus.setEditable(false); ++//System.out.println("getRemoteStatus"); + 		} + 		return remoteStatus; + 	} +@@ -777,9 +1023,10 @@ + 			historyComboBox = new javax.swing.JComboBox(); + 			historyComboBox.setFont( + 				new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); +-			historyComboBox.insertItemAt(new String("Pulldown to view history ..."),0); ++			historyComboBox.insertItemAt(new String("Pulldown to view history;   Press Escape to Close/Quit;   Press Ctrl-R to Reset Panel."),0); + 			historyComboBox.setSelectedIndex(0); + 			historyComboBox.addActionListener(this); ++//System.out.println("getHistoryComboBox"); + 		} + 		return historyComboBox; + 	} +@@ -791,6 +1038,7 @@ + 	private javax.swing.JProgressBar getJProgressBar() { + 		if (jProgressBar == null) { + 			jProgressBar = new javax.swing.JProgressBar(); ++//System.out.println("getJProgressBar"); + 		} + 		return jProgressBar; + 	} +@@ -806,6 +1054,7 @@ + 			connectionStatus.setBackground(java.awt.Color.lightGray); + 			connectionStatus.setFont( + 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); ++//System.out.println("getConnectionStatus"); + 		} + 			connectionStatus.setEditable(false); + 		return connectionStatus; +@@ -815,7 +1064,12 @@ + 	 * Implements Action listener. + 	 */ + 	public void actionPerformed(ActionEvent evt) { +-		System.out.println(evt.getSource()); ++//		System.out.println(evt.getSource()); ++ ++		if (ignore_events) { ++			System.out.println("ignore_events: " + evt.getSource()); ++			return; ++		} +  + 		if (evt.getSource() == closeButton) + 		{ // Close Button +@@ -829,15 +1083,27 @@ + 		{ + 			doReceive(); + 		} ++// begin runge/x11vnc ++		else if (evt.getSource() == viewButton) ++		{ ++			doView(); ++		} ++// end   runge/x11vnc + 		else if (evt.getSource() == localDrivesComboBox) + 		{ + 			changeLocalDrive(); + 		} + 		else if (evt.getSource() == remoteDrivesComboBox) + 		{  ++//System.out.println("remoteDrivesComboBox");	// runge/x11vnc + 			changeRemoteDrive(); +-			remoteList.clear(); +-			remoteFileTable.setListData(remoteList); ++ ++			// are these really needed? changeRemoteDrive() does them at the end. ++			if (false) { ++				remoteList.clear(); ++				remoteListInfo.clear(); ++				remoteFileTable.setListData(remoteList); ++			} + 		} + 		else if (evt.getSource() == localTopButton) + 		{ +@@ -845,12 +1111,17 @@ + 		} + 		else if (evt.getSource() == remoteTopButton) + 		{ ++//System.out.println("remoteTopButton");	// runge/x11vnc + 		  	changeRemoteDrive(); + 		} + 		else if(evt.getSource() == deleteButton) + 		{ + 			doDelete(); + 		} ++		else if(evt.getSource() == refreshButton) ++		{ ++			doRefresh(); ++		} + 		else if(evt.getSource()==newFolderButton) + 		{ + 			doNewFolder(); +@@ -864,7 +1135,7 @@ +  + 	private void doNewFolder() + 	{ +-		String name = JOptionPane.showInputDialog(null,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE); ++		String name = JOptionPane.showInputDialog(jContentPane,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE); + 		if(selectedTable.equals("remote")) + 		{ + 			name = remoteLocation.getText()+name; +@@ -880,34 +1151,106 @@ + 			historyComboBox.setSelectedIndex(0); + 		} + 	} +-	private void doClose() ++	public void doClose() + 	{ ++		if (viewer.ftpOnly) { ++			viewer.disconnect(); ++			return; ++		} + 		try { + 			this.setVisible(false); +-			viewer.rfb.writeFramebufferUpdateRequest( +-									0, +-									0, +-									viewer.rfb.framebufferWidth, +-									viewer.rfb.framebufferHeight, +-									true); ++			viewer.rfb.writeFramebufferUpdateRequest(0, 0, viewer.rfb.framebufferWidth, ++			    viewer.rfb.framebufferHeight, true); ++ ++			if (false) { ++				this.dispose(); ++				jContentPane = null; ++			} + 		} catch (IOException e) { + 			// TODO Auto-generated catch block + 			e.printStackTrace(); + 		} + 	} ++	private void unSwing() { ++		jContentPane = null; ++		topPanel = null; ++		topPanelLocal = null; ++		topPanelRemote = null; ++		topPanelCenter = null; ++		statusPanel = null; ++		remotePanel = null; ++		localPanel = null; ++		buttonPanel = null; ++		sendButton = null; ++		receiveButton = null; ++		deleteButton = null; ++		newFolderButton = null; ++		stopButton = null; ++		closeButton = null; ++		dummyButton = null; ++		localDrivesComboBox = null; ++		remoteDrivesComboBox = null; ++		localMachineLabel = null; ++		remoteMachineLabel = null; ++		localTopButton = null; ++		remoteTopButton = null; ++		localScrollPane = null; ++		localFileTable = null; ++		remoteScrollPane = null; ++		remoteFileTable = null; ++		remoteLocation = null; ++		localLocation = null; ++		localStatus = null; ++		remoteStatus = null; ++		historyComboBox = null; ++		jProgressBar = null; ++		connectionStatus = null; ++		viewButton = null; ++		refreshButton = null; ++	} ++ ++	public void doReset() ++	{ ++		try { ++			this.setVisible(false); ++			this.dispose(); ++			jContentPane = null; ++		        try {Thread.sleep(500);} catch (InterruptedException e) {} ++			viewer.ftp_init(); ++		} catch (Exception e) { ++			// TODO Auto-generated catch block ++			e.printStackTrace(); ++		} ++	} +  ++	public void doOpen() ++	{ ++		try { ++			this.setVisible(true); ++			if (false) { ++				this.initialize(); ++			} ++		} catch (Exception e) { ++			// TODO Auto-generated catch block ++			e.printStackTrace(); ++		} ++	} + 	private void doDelete() + 	{ +-		System.out.println("Delete Button Pressed"); ++//		System.out.println("Delete Button Pressed"); + 		//Call this method to delete a file at server + 		if(selectedTable.equals("remote")) + 		{	 +-			String sFileName = ((String) this.remoteFileTable.getSelectedValue()); ++			Object selected = this.remoteFileTable.getSelectedValue(); ++			if (selected == null) { ++				return; ++			} ++			String sFileName = ((String) selected); + 			 + //			 sf@2004 - Directory can't be deleted + 			if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) + 			{ +-				JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); ++				JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); + 				return; + 			}			 + 			 +@@ -916,7 +1259,7 @@ + 			// sf@2004 - Delete prompt + 			if (remoteList.contains(sFileName)) + 			{ +-				int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); ++				int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); + 				if (r == JOptionPane.NO_OPTION) + 					return; + 			} +@@ -926,18 +1269,22 @@ + 		} + 		else + 		{ +-			String sFileName = ((String) this.localFileTable.getSelectedValue()); ++			Object selected = this.localFileTable.getSelectedValue(); ++			if (selected == null) { ++				return; ++			} ++			String sFileName = ((String) selected); + 			 + //			 sf@2004 - Directory can't be deleted + 			if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) + 			{ +-				JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); ++				JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); + 				return; + 			}			 + 			// sf@2004 - Delete prompt + 			if (localList.contains(sFileName)) + 			{ +-				int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); ++				int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); + 				if (r == JOptionPane.NO_OPTION) + 					return; + 			}			 +@@ -952,21 +1299,25 @@ +  + 	private void doReceive() + 	{ +-		System.out.println("Received Button Pressed"); ++//		System.out.println("Received Button Pressed"); +  +-		String sFileName = ((String) this.remoteFileTable.getSelectedValue()); ++		Object selected = this.remoteFileTable.getSelectedValue(); ++		if (selected == null) { ++			return; ++		} ++		String sFileName = ((String) selected); + 		 + 		// sf@2004 - Directory can't be transfered + 		if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) + 		{ +-			JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); ++			JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); + 			return; + 		} + 		 + 		// sf@2004 - Overwrite prompt + 		if (localList.contains(sFileName)) + 		{ +-			int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); ++			int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); + 			if (r == JOptionPane.NO_OPTION) + 				return; + 		} +@@ -979,23 +1330,101 @@ + 		viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath); + 	} +  ++// begin runge/x11vnc ++	private void doRefresh() ++	{ ++		System.out.println("Refreshing Local and Remote."); ++		refreshLocalLocation(); ++		refreshRemoteLocation(); ++	} ++ ++	private void doView() ++	{ ++//		System.out.println("View Button Pressed"); ++ ++		if (selectedTable == null) { ++			return; ++		} ++		if (selectedTable.equals("remote")) { ++			viewRemote(); ++		} else if (selectedTable.equals("local")) { ++			viewLocal(); ++		} ++	} ++ ++	private File doReceiveTmp() ++	{ ++ ++		if (remoteFileTable == null) { ++			return null; ++		} ++		Object selected = this.remoteFileTable.getSelectedValue(); ++		if (selected == null) { ++			return null; ++		} ++		String sFileName = ((String) selected); ++		 ++		if (sFileName == null) { ++			return null; ++		} ++		 ++		// sf@2004 - Directory can't be transfered ++		if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) ++		{ ++			return null; ++		} ++		 ++		File tmp = null; ++		try { ++			tmp = File.createTempFile("ULTRAFTP", ".txt"); ++		} catch (Exception e) { ++			return null; ++		} ++		 ++		//updateHistory("Downloaded " + localSelection.toString()); ++		String remoteFileName = this.remoteLocation.getText(); ++		remoteFileName+= ((String) this.remoteFileTable.getSelectedValue()).substring(1); ++		System.out.println("remoteFileName: " + remoteFileName); ++if (false) { ++		char[] b = remoteFileName.toCharArray(); ++		for (int n = 0; n < b.length; n++) { ++			System.out.print(Integer.toHexString(b[n]) + " "); ++		} ++		System.out.println(""); ++		for (int n = 0; n < b.length; n++) { ++			System.out.print(b[n]); ++		} ++		System.out.println(""); ++} ++		 ++		String localDestinationPath = tmp.getAbsolutePath(); ++		viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath); ++		System.out.println("ReceiveTmp: " + localDestinationPath); ++		return tmp; ++	} ++// end   runge/x11vnc ++ + 	private void doSend() + 	{ +-		System.out.println("Send Button Pressed"); ++//		System.out.println("Send Button Pressed"); +  +-		String sFileName = ((String) this.localFileTable.getSelectedValue()); ++		Object selected = this.localFileTable.getSelectedValue(); ++		if (selected == null) { ++			return; ++		} ++		String sFileName = ((String) selected); + 		 + 		// sf@2004 - Directory can't be transfered + 		if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) + 		{ +-			JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); ++			JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); + 			return; + 		} + 		 + 		// sf@2004 - Overwrite prompt + 		if (remoteList.contains(sFileName)) + 		{ +-			int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); ++			int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); + 			if (r == JOptionPane.NO_OPTION) + 				return; + 		} +@@ -1013,6 +1442,7 @@ + 	//  + 	private void doStop() + 	{ ++		System.out.println("** Current Transfer Aborted **"); + 		viewer.rfb.fAbort = true; + 	} + 	/** +@@ -1024,6 +1454,14 @@ + 		System.out.println("History: " + message); + 		historyComboBox.insertItemAt(new String(message), 0); + 	} ++ ++	public void receivedRemoteDirectoryName(String str) { ++		if (doingShortcutDir) { ++			if (str.length() > 1) { ++				remoteLocation.setText(str); ++			} ++		} ++	} + 	 + 	/** + 	 * This method updates the file table to the current selection of the remoteComboBox +@@ -1034,11 +1472,44 @@ + 		remoteSelection = null; + 	 + 		if (!updateDriveList) { +-			String drive =	remoteDrivesComboBox.getSelectedItem().toString().substring(0,1)+ ":\\"; +-			viewer.rfb.readServerDirectory(drive); +-			remoteLocation.setText(drive); ++//System.out.println("changeRemoteDrive-A " + drive);	// begin runge/x11vnc ++			Object selected = remoteDrivesComboBox.getSelectedItem(); ++			if (selected != null)  { ++				String instr =	selected.toString(); ++				if (instr != null) { ++System.out.println("changeRemoteDrive: instr='" + instr + "'"); ++					String drive =	instr.substring(0,1)+ ":\\"; ++					if (instr.startsWith(" [")) { ++						int idx = instr.lastIndexOf(']');  ++						if (idx > 2) { ++							drive = instr.substring(2, idx); ++						} else { ++							drive = instr.substring(2); ++						} ++						if (drive.equals("Home")) { ++							drive = ""; ++						} ++						drive += "\\"; ++						doingShortcutDir = true; ++					} else { ++						doingShortcutDir = false; ++						drive = saveRemoteHack(drive); ++					} ++					gotShortcutDir = false; ++					viewer.rfb.readServerDirectory(drive); ++					if (!gotShortcutDir) { ++						remoteLocation.setText(drive); ++					} ++				} else { ++System.out.println("changeRemoteDrive: instr null"); ++				} ++			} else { ++System.out.println("changeRemoteDrive: selection null"); ++			} ++//System.out.println("changeRemoteDrive-B " + drive);	// end runge/x11vnc + 		} + 		remoteList.clear(); ++		remoteListInfo.clear(); + 		remoteFileTable.setListData(remoteList); + 	} + 	/** +@@ -1048,6 +1519,7 @@ + 	private void changeLocalDrive() + 	{ + 		File currentDrive = new File(localDrivesComboBox.getSelectedItem().toString()); ++System.out.println("changeLocalDrive " + currentDrive.toString());	// runge/x11vnc + 		if(currentDrive.canRead()) + 		{ + 			localSelection = null; +@@ -1057,9 +1529,11 @@ + 		else + 		{ + 			localList.clear(); ++			localListInfo.clear(); + 			localStatus.setText("WARNING: Drive " + localDrivesComboBox.getSelectedItem().toString()); + 			connectionStatus.setText(" > WARNING - Local Drive unavailable (possibly restricted access or media not present)"); + 		} ++ + 	} + 	/** + 	 * Determines which FileTable was double-clicked and updates the table +@@ -1098,10 +1572,18 @@ + 		selectedTable = "remote"; + 		localFileTable.setBackground(new Color(238, 238, 238)); + 		remoteFileTable.setBackground(new Color(255, 255, 255)); +-		String name = (remoteFileTable.getSelectedValue().toString()).substring(1); ++		Object selected = remoteFileTable.getSelectedValue(); ++		if (selected == null) { ++			return; ++		} ++		String selstr = selected.toString(); ++		if (selstr == null) { ++			return; ++		} ++		String name = selstr.substring(1); + 		if( !name.substring(0, 2).equals(" ["))	 + 			remoteSelection = remoteLocation.getText() + name.substring(0, name.length()); +-		 ++ + 	} +  + 	/* +@@ -1115,10 +1597,38 @@ + 		localFileTable.setBackground(new Color(255, 255, 255)); + 		File currentSelection = new File(currentLocalDirectory, getTrimmedSelection()); + 		 +-		if(currentSelection.isFile())  ++// begin runge/x11vnc ++		// localSelection = currentSelection.getAbsoluteFile(); ++		if(currentSelection.isFile()) { + 			localSelection = currentSelection.getAbsoluteFile(); ++			localCurrentIsDir = false; ++		} else { ++			localCurrentIsDir = true; ++		} ++// end   runge/x11vnc +  + 	} ++ ++// begin runge/x11vnc ++	private void viewRemote() { ++		File tmp = doReceiveTmp(); ++		if (tmp == null) { ++			return; ++		} ++		TextViewer tv = new TextViewer("Remote: " + remoteSelection, tmp, true); ++	} ++	private void viewLocal() { ++		if (localSelection == null) { ++			return; ++		} ++		if (localCurrentIsDir) { ++			return; ++		} ++		File loc = new File(localSelection.toString()); ++		TextViewer tv = new TextViewer("Local: " + localSelection.toString(), loc, false); ++	} ++// end runge/x11vnc ++ + 	/** + 	 * Updates the Remote File Table based on selection.  Called from mouseClicked handler + 	 */ +@@ -1126,20 +1636,29 @@ + 		String name = null; + 		String action = null; + 		String drive = null; +-		name = (remoteFileTable.getSelectedValue().toString()).substring(1); ++		Object selected = remoteFileTable.getSelectedValue(); ++		if (selected == null) { ++			return; ++		} ++		String sname = selected.toString(); ++		if (sname == null) { ++			return; ++		} ++		name = sname.substring(1); +  + 		if (name.equals("[..]")) + 		{ + 			action = "up"; + 			remoteSelection = null; + 			drive = remoteLocation.getText().substring(0, remoteLocation.getText().length() - 1); +-			// JOptionPane.showMessageDialog(null, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE); ++			// JOptionPane.showMessageDialog(jContentPane, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE); + 			int index = drive.lastIndexOf("\\"); + 			drive = drive.substring(0, index + 1); +  + 			remoteLocation.setText(drive); + 			viewer.rfb.readServerDirectory(drive); + 			remoteList.clear(); ++			remoteListInfo.clear(); + 			remoteFileTable.setListData(remoteList);	 + 		} + 		else if (!name.substring(0, 2).equals(" [") && !name.substring((name.length() - 1), name.length()).equals("]")) +@@ -1149,6 +1668,7 @@ + 			remoteSelection = remoteLocation.getText() + name.substring(0, name.length()); + 			drive = remoteLocation.getText(); + 			// ?? ++			viewRemote();	// runge/x11vnc + 		} + 		else + 		{  +@@ -1159,10 +1679,12 @@ + 			remoteLocation.setText(drive); + 			viewer.rfb.readServerDirectory(drive); + 			remoteList.clear(); ++			remoteListInfo.clear(); + 			remoteFileTable.setListData(remoteList);	 + 		}	 + 		//remoteLocation.setText(drive);	 + 	} ++ + 	/** + 	 * Updates the Local File Table based on selection. Called from MouseClicked handler + 	 */ +@@ -1188,6 +1710,7 @@ + 		else if (currentSelection.isFile()) + 		{ + 			localSelection = currentSelection.getAbsoluteFile(); ++			viewLocal();	// runge/x11vnc + 		} + 		else if (currentSelection.isDirectory()) + 		{ +@@ -1201,13 +1724,22 @@ + 	 *  + 	 */ + 	private String getTrimmedSelection(){ +-		String currentSelection = (localFileTable.getSelectedValue().toString()).substring(1); +-				if(currentSelection.substring(0,1).equals("[") && +-				currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){ +-				return currentSelection.substring(1,currentSelection.length()-1); +-				} else { +-					return currentSelection; +-				} ++		String currentSelection = ""; ++		Object selected = localFileTable.getSelectedValue(); ++		if (selected == null) { ++			return currentSelection; ++		} ++		String selstr = selected.toString(); ++		if (selstr == null) { ++			return currentSelection; ++		} ++		currentSelection = selstr.substring(1); ++		if(currentSelection.substring(0,1).equals("[") && ++			currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){ ++			return currentSelection.substring(1,currentSelection.length()-1); ++		} else { ++			return currentSelection; ++		} + 	} +  + 	/* +@@ -1241,36 +1773,148 @@ + 	 	return null; + 	} + 	 ++	String timeStr(long t) { ++		Date date = new Date(t); ++		return date.toString(); ++	} ++	String dotPast(double f, int n) { ++		String fs = "" + f; ++		int i = fs.lastIndexOf(".") + n; ++		if (i >= 0) { ++			int len = fs.length(); ++			if (i >= len) { ++				i = len-1; ++			} ++			fs = fs.substring(0, i); ++		} ++		return fs; ++	} ++	String sizeStr(int s) { ++		if (s < 0) { ++			return s + "? B"; ++		} else if (s < 1024) { ++			return s + " B"; ++		} else if (s < 1024 * 1024) { ++			double k = s / 1024.0; ++			String ks = dotPast(k, 3); ++			 ++			return s + " (" + ks + " KB)"; ++		} else { ++			double m = s / (1024.0*1024.0); ++			String ms = dotPast(m, 3); ++			return s + " (" + ms + " MB)"; ++		} ++	} ++ ++	int max_char(String text) { ++		int maxc = 0; ++		char chars[] = text.toCharArray(); ++		for (int n = 0; n < chars.length; n++) { ++			if ((int) chars[n] > maxc) { ++				maxc = (int) chars[n]; ++			} ++		} ++		return maxc; ++	} +  + 	/* + 	 * Navigates the local file structure up or down one directory + 	 */ + 	public void changeLocalDirectory(File dir) + 	{ +-			currentLocalDirectory = dir;	// Updates Global ++			dir = saveLocalHack(dir);	// runge/x11vnc ++ ++			if (dir == null) { ++				connectionStatus.setText("Error changing local directory."); ++				historyComboBox.insertItemAt(new String("> Error changing local directory."), 0); ++				historyComboBox.setSelectedIndex(0); ++				return; ++			} ++ + 			File allFiles[] = dir.listFiles();	// Reads files + 			String[] contents = dir.list(); +  ++			if (contents == null || allFiles == null) { ++				connectionStatus.setText("Error changing local directory."); ++				historyComboBox.insertItemAt(new String("> Error changing local directory."), 0); ++				historyComboBox.setSelectedIndex(0); ++				return; ++			} ++ ++			currentLocalDirectory = dir;	// Updates Global ++// begin runge/x11vnc ++System.out.println("changeLocalDirectory: " + dir.toString()); ++			if (contents != null) { ++				java.util.Arrays.sort(contents, String.CASE_INSENSITIVE_ORDER); ++				for (int i = 0; i < contents.length; i++) { ++					allFiles[i] = new File(dir, contents[i]); ++				} ++			} else { ++				return; ++			} ++// end runge/x11vnc ++ + 			localList.clear(); ++			localListInfo.clear(); + 			localList.addElement(" [..]"); ++			localListInfo.addElement(" [..]"); ++ ++			ArrayList DirInfo = new ArrayList(); ++			ArrayList FilInfo = new ArrayList(); ++ ++			Charset charset = Charset.forName("ISO-8859-1"); ++			CharsetDecoder decoder = charset.newDecoder(); ++			CharsetEncoder encoder = charset.newEncoder(); + 			 + 			// Populate the Lists + 			for (int i = 0; i < contents.length; i++) + 			{ +-				if (allFiles[i].isDirectory()) ++				String f1 = contents[i]; ++ ++if (false) { ++	 ++System.out.println("max_char: " + max_char(f1) + " "  + f1); ++				if (max_char(f1) > 255) { ++					try { ++System.out.println("bbuf1"); ++						ByteBuffer bbuf = encoder.encode(CharBuffer.wrap(f1.toCharArray())); ++System.out.println("bbuf2"); ++						CharBuffer cbuf = decoder.decode(bbuf); ++System.out.println("bbuf3"); ++						f1 = cbuf.toString();  ++System.out.println("did bbuf: " + f1); ++					} catch (Exception e) { ++						; ++					} ++				} ++} ++				 ++				String f2 = f1; ++				if (f2.length() < 24) { ++					for (int ik = f2.length(); ik < 24; ik++) { ++						f2 = f2 + " "; ++					} ++				} ++				String s = f2 + "    \tLastmod: " + timeStr(allFiles[i].lastModified()) + "    \t\tSize: " + sizeStr((int) allFiles[i].length());  ++				if (allFiles[i].isDirectory()) { + 					// localList.addElement("[" + contents[i] + "]"); +-					DirsList.add(" [" + contents[i] + "]"); // sf@2004 +-				else +-				{ ++					DirsList.add(" [" + f1 + "]"); // sf@2004 ++					DirInfo.add(s); ++				} else { + 					// localList.addElement(contents[i]); +-					FilesList.add(" " + contents[i]); // sf@2004 ++					FilesList.add(" " + f1); // sf@2004 ++					FilInfo.add(s); + 				} + 			} + 			// sf@2004 +-			for (int i = 0; i < DirsList.size(); i++)  ++			for (int i = 0; i < DirsList.size(); i++) { + 				localList.addElement(DirsList.get(i)); +-			for (int i = 0; i < FilesList.size(); i++)  ++				localListInfo.addElement(DirInfo.get(i)); ++			} ++			for (int i = 0; i < FilesList.size(); i++) { + 				localList.addElement(FilesList.get(i)); ++				localListInfo.addElement(FilInfo.get(i)); ++			} + 			 + 			FilesList.clear(); + 			DirsList.clear(); +@@ -1296,3 +1940,147 @@ + 	} +  + } //  @jve:visual-info  decl-index=0 visual-constraint="10,10" ++ ++// begin runge/x11vnc ++class TextViewer extends JFrame implements ActionListener { ++ ++	JTextArea textArea = new JTextArea(35, 80); ++	File file = null; ++	JButton refreshButton; ++	JButton dismissButton; ++	Timer tim = null; ++	int rcnt = 0; ++	int tms = 250; ++	boolean delete_it = false; ++	TextViewer me; ++ ++	public TextViewer(String s, File f, boolean d) { ++ ++		delete_it = d; ++		file = f; ++		me = this; ++ ++		JScrollPane scrollPane = new JScrollPane(textArea, ++		    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, ++		    JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); ++ ++		textArea.setEditable(false); ++		textArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); ++ ++		KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, InputEvent.SHIFT_MASK); ++		AbstractAction escapeAction = new AbstractAction() { ++			public void actionPerformed(ActionEvent actionEvent) { ++				cleanse(); ++				me.dispose(); ++			} ++		}; ++		textArea.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction"); ++		textArea.getInputMap().put(stroke, "escapeAction"); ++		textArea.getActionMap().put("escapeAction", escapeAction); ++ ++		refreshButton = new JButton(); ++		refreshButton.setText("Reload"); ++		refreshButton.setName("refreshButton"); ++		refreshButton.addActionListener(this); ++ ++		dismissButton = new JButton(); ++		dismissButton.setText("Dismiss"); ++		dismissButton.setName("dismissButton"); ++		dismissButton.addActionListener(this); ++ ++		JPanel buttons = new JPanel(); ++		buttons.setLayout(new BorderLayout()); ++		buttons.add(refreshButton, BorderLayout.WEST); ++		buttons.add(dismissButton, BorderLayout.EAST); ++ ++		JPanel content = new JPanel(); ++		content.setLayout(new BorderLayout()); ++		content.add(scrollPane, BorderLayout.CENTER); ++		content.add(buttons, BorderLayout.SOUTH); ++ ++		ActionListener tsk = new ActionListener() { ++			public void actionPerformed(ActionEvent evt) { ++				// System.out.println("tsk"); ++				refresh(); ++			} ++		}; ++		tim = new Timer(tms, tsk); ++		tim.start(); ++ ++		this.setContentPane(content); ++		this.setTitle("TextViewer - " + s); ++		this.pack(); ++		this.setVisible(true); ++	} ++ ++	private void refresh() { ++ ++		rcnt++; ++		if (rcnt * tms > 3000 && tim != null) { ++			tim.stop(); ++			tim = null; ++		} ++		BufferedReader input = null; ++		StringBuffer contents = new StringBuffer(); ++		try { ++			if (input == null) { ++				input = new BufferedReader(new FileReader(file)); ++			} ++			String line = null; ++			int i = 0; ++			while (( line = input.readLine()) != null) { ++				if (i == 0) { ++					// System.out.println("read"); ++				} ++				i++; ++				contents.append(line); ++				contents.append(System.getProperty("line.separator")); ++			} ++		} catch (Exception e) { ++			; ++		} finally { ++			try { ++				if (input != null) { ++					input.close(); ++					input = null; ++				} ++			} catch (Exception e) { ++				; ++			} ++		} ++ ++		textArea.setText(contents.toString()); ++		textArea.setCaretPosition(0); ++	} ++ ++	public void actionPerformed(ActionEvent evt) { ++ ++		if (evt.getSource() == refreshButton) { ++			refresh(); ++		} ++		if (evt.getSource() == dismissButton) { ++			cleanse(); ++			this.dispose(); ++		} ++	} ++ ++	private void cleanse() { ++		if (delete_it && file != null) { ++			try { ++				file.delete(); ++				file = null; ++			} catch (Exception e) { ++				; ++			} ++		} ++	} ++ ++	protected void finalize() throws Throwable { ++		try { ++			cleanse(); ++		} finally { ++			super.finalize(); ++		} ++	} ++} ++// end runge/x11vnc +diff -Naur JavaViewer.orig/Makefile JavaViewer/Makefile +--- JavaViewer.orig/Makefile	2006-05-29 09:06:32.000000000 -0400 ++++ JavaViewer/Makefile	2010-05-18 20:53:32.000000000 -0400 +@@ -4,6 +4,7 @@ +  + CP = cp + JC = javac ++JC_ARGS = -target 1.4 -source 1.4 + JAR = jar + ARCHIVE = VncViewer.jar + PAGES = index.vnc shared.vnc noshared.vnc hextile.vnc zlib.vnc tight.vnc +@@ -20,7 +21,7 @@ + all: $(CLASSES) $(ARCHIVE) +  + $(CLASSES): $(SOURCES) +-	$(JC) -O $(SOURCES) ++	$(JC) $(JC_ARGS) -O $(SOURCES) +  + $(ARCHIVE): $(CLASSES) + 	$(JAR) cf $(ARCHIVE) $(CLASSES) +diff -Naur JavaViewer.orig/OptionsFrame.java JavaViewer/OptionsFrame.java +--- JavaViewer.orig/OptionsFrame.java	2005-11-21 18:50:16.000000000 -0500 ++++ JavaViewer/OptionsFrame.java	2007-05-13 22:18:30.000000000 -0400 +@@ -144,7 +144,10 @@ +     choices[jpegQualityIndex].select("6"); +     choices[cursorUpdatesIndex].select("Enable"); +     choices[useCopyRectIndex].select("Yes"); +-    choices[eightBitColorsIndex].select("64"); ++// begin runge/x11vnc ++//    choices[eightBitColorsIndex].select("64"); ++    choices[eightBitColorsIndex].select("Full"); ++// end runge/x11vnc +     choices[mouseButtonIndex].select("Normal"); +     choices[viewOnlyIndex].select("No"); +     choices[shareDesktopIndex].select("Yes"); +diff -Naur JavaViewer.orig/RfbProto.java JavaViewer/RfbProto.java +--- JavaViewer.orig/RfbProto.java	2006-05-24 15:14:40.000000000 -0400 ++++ JavaViewer/RfbProto.java	2010-11-30 22:13:58.000000000 -0500 +@@ -31,6 +31,7 @@ + import java.net.Socket; + import java.util.*; + import java.util.zip.*; ++import java.text.DateFormat; +  +  + class RfbProto { +@@ -86,8 +87,11 @@ +  + 	// sf@2004 - FileTransfer part + 	ArrayList remoteDirsList; ++	ArrayList remoteDirsListInfo; + 	ArrayList remoteFilesList; ++	ArrayList remoteFilesListInfo; + 	ArrayList a; ++	ArrayList b; + 	boolean fFTInit = true; // sf@2004 + 	boolean fFTAllowed = true; + 	boolean fAbort = false; +@@ -199,6 +203,10 @@ + 	// playback. + 	int numUpdatesInSession; +  ++// begin runge/x11vnc ++	int readServerDriveListCnt = -1; ++	long readServerDriveListTime = 0; ++// end   runge/x11vnc + 	// + 	// Constructor. Make TCP connection to RFB server. + 	// +@@ -207,7 +215,27 @@ + 		viewer = v; + 		host = h; + 		port = p; +-		sock = new Socket(host, port); ++// begin runge/x11vnc ++//		sock = new Socket(host, port); ++    if (! viewer.disableSSL) { ++       System.out.println("new SSLSocketToMe"); ++       SSLSocketToMe ssl; ++       try { ++               ssl = new SSLSocketToMe(host, port, v); ++       } catch (Exception e) { ++               throw new IOException(e.getMessage()); ++       } ++ ++       try { ++               sock = ssl.connectSock(); ++       } catch (Exception es) { ++               throw new IOException(es.getMessage()); ++       } ++    } else { ++       sock = new Socket(host, port); ++    } ++// end runge/x11vnc ++ + 		is = + 			new DataInputStream( + 				new BufferedInputStream(sock.getInputStream(), 16384)); +@@ -215,9 +243,12 @@ + 		osw = new OutputStreamWriter(sock.getOutputStream()); + 		inDirectory2 = false; + 		a = new ArrayList(); ++		b = new ArrayList(); + 		// sf@2004 + 		remoteDirsList = new ArrayList(); ++		remoteDirsListInfo = new ArrayList(); + 		remoteFilesList = new ArrayList(); ++		remoteFilesListInfo = new ArrayList(); + 	 + 		sendFileSource = ""; + 	} +@@ -420,7 +451,13 @@ + 	// +  + 	int readServerMessageType() throws IOException { +-		int msgType = is.readUnsignedByte(); ++		int msgType; ++		try { ++			msgType = is.readUnsignedByte(); ++		} catch (Exception e) { ++			viewer.disconnect(); ++			return -1; ++		} +  + 		// If the session is being recorded: + 		if (rec != null) { +@@ -600,6 +637,7 @@ + 		contentParamT = is.readUnsignedByte(); + 		contentParamT = contentParamT << 8; + 		contentParam = contentParam | contentParamT; ++//System.out.println("FTM: contentType " + contentType + " contentParam " + contentParam); + 		if (contentType == rfbRDrivesList || contentType == rfbDirPacket) + 		{ + 			readDriveOrDirectory(contentParam); +@@ -610,7 +648,7 @@ + 		} + 		else if (contentType == rfbFilePacket) + 		{ +-				receiveFileChunk(); ++			receiveFileChunk(); + 		} + 		else if (contentType == rfbEndOfFile) + 		{ +@@ -618,6 +656,10 @@ + 		} + 		else if (contentType == rfbAbortFileTransfer) + 		{ ++			System.out.println("rfbAbortFileTransfer: fFileReceptionRunning=" ++			    + fFileReceptionRunning + " fAbort=" ++			    + fAbort + " fFileReceptionError=" ++			    + fFileReceptionError); + 			if (fFileReceptionRunning) + 			{ + 				endOfReceiveFile(false); // Error +@@ -626,6 +668,11 @@ + 			{ + 				// sf@2004 - Todo: Add TestPermission  + 				// System.out.println("File Transfer Aborted!"); ++ ++				// runge: seems like we must at least read the remaining ++				// 8 bytes of the header, right? ++				int size = is.readInt(); ++				int length = is.readInt(); + 			} + 			 + 		} +@@ -645,6 +692,7 @@ + 		{ + 			System.out.println("ContentType: " + contentType); + 		} ++//System.out.println("FTM: done"); + 	} +  + 	//Refactored from readRfbFileTransferMsg() +@@ -662,6 +710,7 @@ +  + 	//Refactored from readRfbFileTransferMsg() + 	public void readDriveOrDirectory(int contentParam) throws IOException { ++//System.out.println("RDOD: " + contentParam + " " + inDirectory2); + 		if (contentParam == rfbADrivesList) + 		{ + 			readFTPMsgDriveList(); +@@ -688,13 +737,21 @@ +  + 	// Internally used. Write an Rfb message to the server + 	void writeRfbFileTransferMsg( +-								int contentType, +-								int contentParam, +-								long size, // 0 : compression not supported - 1 : compression supported +-								long length, +-								String text) throws IOException ++		int contentType, ++		int contentParam, ++		long size, // 0 : compression not supported - 1 : compression supported ++		long length, ++		String text) throws IOException + 	{ + 		byte b[] = new byte[12]; ++		byte byteArray[]; ++ ++		if (viewer.dsmActive) { ++			// need to send the rfbFileTransfer msg type twice for the plugin... ++			byte b2[] = new byte[1]; ++			b2[0] = (byte) rfbFileTransfer; ++			os.write(b2); ++		} +  + 		b[0] = (byte) rfbFileTransfer; + 		b[1] = (byte) contentType; +@@ -702,7 +759,7 @@ +  + 		byte by = 0; + 		long c = 0; +-		length++; ++ + 		c = size & 0xFF000000; + 		by = (byte) (c >>> 24); + 		b[4] = by; +@@ -716,6 +773,32 @@ + 		by = (byte) c; + 		b[7] = by; +  ++		if (text != null) { ++			byte byteArray0[] = text.getBytes(); ++			int maxc = max_char(text); ++			if (maxc > 255) { ++				System.out.println("writeRfbFileTransferMsg: using getBytes(\"UTF-8\")"); ++				byteArray0 = text.getBytes("UTF-8"); ++			} else if (maxc > 127) { ++				System.out.println("writeRfbFileTransferMsg: using getBytes(\"ISO-8859-1\")"); ++				byteArray0 = text.getBytes("ISO-8859-1"); ++			} ++			byteArray = new byte[byteArray0.length + 1]; ++			for (int i = 0; i < byteArray0.length; i++) { ++				byteArray[i] = byteArray0[i]; ++			} ++			byteArray[byteArray.length - 1] = 0; ++System.out.println("writeRfbFileTransferMsg: length: " + length + " -> byteArray.length: " + byteArray.length); ++ ++			// will equal length for ascii, ISO-8859-1, more for UTF-8 ++			length = byteArray.length; ++ ++			//length++;	// used to not include null byte at end. ++		} else { ++			String moo = "moo"; ++			byteArray = moo.getBytes(); ++		} ++ + 		c = length & 0xFF000000; + 		by = (byte) (c >>> 24); + 		b[8] = by; +@@ -729,29 +812,91 @@ + 		by = (byte) c; + 		b[11] = by; + 		os.write(b); ++ ++//System.out.println("size: " + size + " length: " + length + " text: " + text); + 		 +  + 		if (text != null) + 		{ +-			byte byteArray[] = text.getBytes(); +-			byte byteArray2[] = new byte[byteArray.length + 1]; +-			for (int i = 0; i < byteArray.length; i++) { +-				byteArray2[i] = byteArray[i]; ++			os.write(byteArray); ++		} ++	} ++ ++	int max_char(String text) { ++		int maxc = 0; ++		char chars[] = text.toCharArray(); ++		for (int n = 0; n < chars.length; n++) { ++			if ((int) chars[n] > maxc) { ++				maxc = (int) chars[n]; + 			} +-			byteArray2[byteArray2.length - 1] = 0; +-			os.write(byteArray2); + 		} +-		 ++		return maxc; + 	} +  ++	String guess_encoding(char[] chars) { ++		boolean saw_high_char = false; ++ ++		for (int i = 0; i < chars.length; i++) { ++			if (chars[i] == '\0') { ++				break; ++			} ++			if (chars[i] >= 128) { ++				saw_high_char = true; ++				break; ++			} ++		} ++		if (!saw_high_char) { ++			return "ASCII"; ++		} ++		char prev = 1; ++		boolean valid_utf8 = true; ++		int n = 0; ++		for (int i = 0; i < chars.length; i++) { ++			if (chars[i] == '\0') { ++				break; ++			} ++			char c = chars[i]; ++			if (prev < 128 && c >= 128) { ++				if (c >> 5 == 0x6) { ++					n = 1; ++				} else if (c >> 4 == 0xe) { ++					n = 2; ++				} else if (c >> 3 == 0x1e) { ++					n = 3; ++				} else if (c >> 2 == 0x3e) { ++					n = 4; ++				} else { ++					valid_utf8 = false; ++					break; ++				} ++			} else { ++				if (n > 0) { ++					if (c < 128) { ++						valid_utf8 = false; ++						break; ++					} ++					n--; ++				} ++			} ++ ++			prev = c; ++		} ++		if (valid_utf8) { ++			return "UTF-8"; ++		} else { ++			return "ISO-8859-1"; ++		} ++	} ++ ++ + 	//Internally used. Write an rfb message to the server for sending files ONLY  + 	int writeRfbFileTransferMsgForSendFile( +-											int contentType, +-											int contentParam, +-											long size, +-											long length, +-											String source +-											) throws IOException ++		int contentType, ++		int contentParam, ++		long size, ++		long length, ++		String source ++		) throws IOException + 	{ + 		File f = new File(source); + 		fis = new FileInputStream(f); +@@ -768,50 +913,47 @@ + 	 + 		while (bytesRead!=-1) + 		{ +-				counter += bytesRead; +-				myDeflater.setInput(byteBuffer, 0, bytesRead); +-				myDeflater.finish(); +-				compressedSize = myDeflater.deflate(CompressionBuffer); +-				myDeflater.reset(); +-				// If the compressed data is larger than the original one, we're dealing with +-				// already compressed data +-				if (compressedSize > bytesRead) +-					fCompress = false; +-				this.writeRfbFileTransferMsg( +-											contentType, +-											contentParam, +-											(fCompress ? 1 : 0),  +-											(fCompress ? compressedSize-1 : bytesRead-1), +-											null +-											); +-				// Todo: Test write error ! +-				os.write( +-						fCompress ? CompressionBuffer : byteBuffer, +-						0, +-						fCompress ? compressedSize : bytesRead +-						); +-				 +-				// Todo: test read error ! +-				bytesRead = fis.read(byteBuffer); +-				 +-				// viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes"); +-				viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length())); +-				viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%"); +-				 +-				if (fAbort == true) +-				{ +-					fAbort = false; +-					fError = true; +-					break; +-				} +-				try +-				{ +-			        Thread.sleep(5); +-			    } +-				catch(InterruptedException e) +-				{ +-			        System.err.println("Interrupted"); +-			    }				 ++			counter += bytesRead; ++			myDeflater.setInput(byteBuffer, 0, bytesRead); ++			myDeflater.finish(); ++			compressedSize = myDeflater.deflate(CompressionBuffer); ++			myDeflater.reset(); ++			// If the compressed data is larger than the original one, we're dealing with ++			// already compressed data ++			if (compressedSize > bytesRead) ++				fCompress = false; ++			this.writeRfbFileTransferMsg( ++				contentType, ++				contentParam, ++				(fCompress ? 1 : 0),  ++// RUNGE				(fCompress ? compressedSize-1 : bytesRead-1), ++				(fCompress ? compressedSize : bytesRead), ++				null ++			); ++			// Todo: Test write error ! ++			os.write(fCompress ? CompressionBuffer : byteBuffer, 0, fCompress ? compressedSize : bytesRead); ++			 ++			// Todo: test read error ! ++			bytesRead = fis.read(byteBuffer); ++			 ++			// viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes"); ++			viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length())); ++			viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%"); ++			 ++			if (fAbort == true) ++			{ ++				fAbort = false; ++				fError = true; ++				break; ++			} ++			try ++			{ ++		        Thread.sleep(5); ++		    } ++			catch(InterruptedException e) ++			{ ++		        System.err.println("Interrupted"); ++		    }				 + 		} + 		 + 		writeRfbFileTransferMsg(fError ? rfbAbortFileTransfer : rfbEndOfFile, 0, 0, 0, null); +@@ -831,24 +973,30 @@ + 			{ + 				System.out.print((char) is.readUnsignedByte()); + 			} ++			System.out.println(""); ++ ++			if (size == rfbRErrorCmd || size == -1) { ++				viewer.ftp.enableButtons(); ++				viewer.ftp.connectionStatus.setText("Remote file not available for writing."); ++				viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for writing."), 0); ++				viewer.ftp.historyComboBox.setSelectedIndex(0); ++				return; ++			} + 			 +-			int ret = writeRfbFileTransferMsgForSendFile( +-															rfbFilePacket, +-															0, +-															0, +-															0, +-															sendFileSource); ++			int ret = writeRfbFileTransferMsgForSendFile(rfbFilePacket, 0, 0, 0, sendFileSource); + 	 + 			viewer.ftp.refreshRemoteLocation(); + 			if (ret != 1) + 			{ + 				viewer.ftp.connectionStatus.setText(" > Error - File NOT sent"); +-				viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource) + "> was not correctly sent (aborted by user or error)",0); ++				viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource) ++				    + "> was not correctly sent (aborted or error). Data may still be buffered/in transit. Wait for remote listing...",0); + 			} + 			else + 			{ + 				viewer.ftp.connectionStatus.setText(" > File sent"); +-				viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource) + "> was sent to Remote Machine",0); ++				viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource) ++				    + "> was sent to Remote Machine. Note: data may still be buffered/in transit. Wait for remote listing...",0); + 			} + 			viewer.ftp.historyComboBox.setSelectedIndex(0); + 			viewer.ftp.enableButtons(); +@@ -907,7 +1055,7 @@ + 	//Handles acknowledgement that the file has been deleted on the server + 	void deleteRemoteFileFeedback() throws IOException + 	{ +-		is.readInt(); ++		int ret = is.readInt(); + 		int length = is.readInt(); + 		String f = ""; + 		for (int i = 0; i < length; i++) +@@ -916,7 +1064,11 @@ + 		} + 		 + 		viewer.ftp.refreshRemoteLocation();	 +-		viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0); ++		if (ret == -1) { ++			viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Delete File On Remote Machine: "),0); ++		} else { ++			viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0); ++		} + 		viewer.ftp.historyComboBox.setSelectedIndex(0); + 	} +  +@@ -926,12 +1078,7 @@ + 		try + 		{ + 			String temp = text; +-			writeRfbFileTransferMsg( +-									rfbCommand, +-									rfbCFileDelete, +-									0, +-									temp.length(), +-									temp); ++			writeRfbFileTransferMsg(rfbCommand, rfbCFileDelete, 0, temp.length(), temp); + 		} + 		catch (IOException e) + 		{ +@@ -943,7 +1090,7 @@ + 	// Handles acknowledgement that the directory has been created on the server + 	void createRemoteDirectoryFeedback() throws IOException + 	{ +-		is.readInt(); ++		int ret = is.readInt(); + 		int length = is.readInt(); + 		String f=""; + 		for (int i = 0; i < length; i++) +@@ -951,7 +1098,11 @@ + 			f += (char)is.readUnsignedByte(); + 		} + 		viewer.ftp.refreshRemoteLocation();	 +-		viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0); ++		if (ret == -1) { ++			viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Create Directory on Remote Machine."),0); ++		} else { ++			viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0); ++		} + 		viewer.ftp.historyComboBox.setSelectedIndex(0); + 	} +  +@@ -961,12 +1112,7 @@ + 		try + 		{ + 			String temp = text; +-			writeRfbFileTransferMsg( +-				rfbCommand, +-				rfbCDirCreate, +-				0, +-				temp.length(), +-				temp); ++			writeRfbFileTransferMsg(rfbCommand, rfbCDirCreate, 0, temp.length(), temp); + 		} + 		catch (IOException e) + 		{ +@@ -979,15 +1125,13 @@ + 	{ + 		try + 		{ ++//System.out.println("requestRemoteFile text: " + text); ++//System.out.println("requestRemoteFile leng: " + text.length()); + 			String temp = text; + 			receivePath = localPath; + 					 +-			writeRfbFileTransferMsg( +-									rfbFileTransferRequest, +-									0, +-									1, // 0 : compression not supported - 1 : compression supported +-									temp.length(), +-									temp); ++			// 0 : compression not supported - 1 : compression supported ++			writeRfbFileTransferMsg(rfbFileTransferRequest, 0, 1, temp.length(), temp); + 		} + 		catch (IOException e) + 		{ +@@ -1004,6 +1148,9 @@ + 		viewer.ftp.disableButtons(); + 		int size = is.readInt(); + 		int length = is.readInt(); ++ ++//System.out.println("receiveFileHeader size: " + size); ++//System.out.println("receiveFileHeader leng: " + length); + 		 + 		String tempName = ""; + 		for (int i = 0; i < length; i++) +@@ -1011,6 +1158,15 @@ + 			tempName += (char) is.readUnsignedByte(); + 		} +  ++		if (size == rfbRErrorCmd || size == -1) { ++			fFileReceptionRunning = false; ++			viewer.ftp.enableButtons(); ++			viewer.ftp.connectionStatus.setText("Remote file not available for reading."); ++			viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for reading."), 0); ++			viewer.ftp.historyComboBox.setSelectedIndex(0); ++			return; ++		} ++ + 		// sf@2004 - Read the high part of file size (not yet in rfbFileTransferMsg for  + 		// backward compatibility reasons...) + 		int sizeH = is.readInt(); +@@ -1021,7 +1177,16 @@ + 		fileSize=0; + 		fileChunkCounter = 0; + 		String fileName = receivePath; +-		fos = new FileOutputStream(fileName); ++		try { ++			fos = new FileOutputStream(fileName); ++		} catch (Exception e) { ++			fFileReceptionRunning = false; ++			writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null); ++			viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR opening Local File: <" + fileName ),0); ++			viewer.ftp.historyComboBox.setSelectedIndex(0); ++			viewer.ftp.enableButtons(); ++			return; ++		} + 		writeRfbFileTransferMsg(rfbFileHeader, 0, 0, 0, null); + 	} +  +@@ -1085,7 +1250,13 @@ + 			fAbort = false; + 			fFileReceptionError = true; + 			writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null); +-			 ++ ++			//runge for use with x11vnc/libvncserver, no rfbAbortFileTransfer reply sent. ++		        try {Thread.sleep(500);} catch (InterruptedException e) {} ++			viewer.ftp.enableButtons(); ++			viewer.ftp.refreshLocalLocation(); ++			viewer.ftp.connectionStatus.setText(" > Error - File NOT received"); ++			viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + receivePath + "> not correctly received from Remote Machine (aborted by user or error)") ,0); + 		} + 		// sf@2004 - For old FT protocole only + 		/* +@@ -1104,7 +1275,7 @@ + 		int length = is.readInt(); + 		fileSize=0; + 		fos.close(); +-		 ++ + 		viewer.ftp.refreshLocalLocation(); + 		if (fReceptionOk && !fFileReceptionError) + 		{ +@@ -1132,12 +1303,7 @@ + 		try + 		{ + 			String temp = text; +-			writeRfbFileTransferMsg( +-									rfbDirContentRequest, +-									rfbRDirContent, +-									0, +-									temp.length(), +-									temp); ++			writeRfbFileTransferMsg(rfbDirContentRequest, rfbRDirContent, 0, temp.length(), temp); + 		} + 		catch (IOException e) + 		{ +@@ -1197,11 +1363,80 @@ + 					str += temp; + 				} + 			} ++			// runge ++			viewer.ftp.receivedRemoteDirectoryName(str); + 			// viewer.ftp.changeRemoteDirectory(str); + 			 + 		} + 	} +  ++	int zogswap(int n) { ++		long l = n; ++		if (l < 0) { ++			l += 0x100000000L; ++		} ++		l = l & 0xFFFFFFFF; ++		l = (l >> 24) | ((l & 0x00ff0000) >> 8) | ((l & 0x0000ff00) << 8) | (l << 24); ++		return (int) l; ++	} ++ ++	int windozeToUnix(int L, int H) { ++		long L2 = zogswap(L); ++		long H2 = zogswap(H); ++		long unix = (H2 << 32) + L2; ++		unix -= 11644473600L * 10000000L; ++		unix /= 10000000L; ++		//System.out.println("unix time: " + unix + " H2: " + H2 + " L2: " + L2); ++		return (int) unix; ++	} ++	 ++	String timeStr(int t, int h) { ++		if (h == 0) { ++			// x11vnc/libvncserver unix ++			t = zogswap(t); ++		} else { ++			// ultra (except if h==0 by chance) ++			t = windozeToUnix(t, h); ++		} ++		long tl = (long) t; ++		Date date = new Date(tl * 1000); ++		if (true) { ++			return date.toString(); ++		} else { ++			return DateFormat.getDateTimeInstance().format(date); ++		} ++	} ++ ++	String dotPast(double f, int n) { ++		String fs = "" + f; ++		int i = fs.lastIndexOf(".") + n; ++		if (i >= 0) { ++			int len = fs.length(); ++			if (i >= len) { ++				i = len-1; ++			} ++			fs = fs.substring(0, i); ++		} ++		return fs; ++	} ++	String sizeStr(int s) { ++		s = zogswap(s); ++		if (s < 0) { ++			return s + "? B"; ++		} else if (s < 1024) { ++			return s + " B"; ++		} else if (s < 1024 * 1024) { ++			double k = s / 1024.0; ++			String ks = dotPast(k, 3); ++			 ++			return s + " (" + ks + " KB)"; ++		} else { ++			double m = s / (1024.0*1024.0); ++			String ms = dotPast(m, 3); ++			return s + " (" + ms + " MB)"; ++		} ++	} ++ + 	//Internally used to receive directory content from server + 	//Here, the server sends one file/directory with it's attributes + 	void readFTPMsgDirectoryListContent() throws IOException +@@ -1217,17 +1452,32 @@ + 			dwReserved0, + 			dwReserved1; + 		long ftCreationTime, ftLastAccessTime, ftLastWriteTime; ++		int ftCreationTimeL, ftLastAccessTimeL, ftLastWriteTimeL; ++		int ftCreationTimeH, ftLastAccessTimeH, ftLastWriteTimeH; + 		char cFileName, cAlternateFileName; + 		int length = 0; + 		is.readInt(); + 		length = is.readInt(); ++ ++		char[] chars = new char[4*length]; ++		int char_cnt = 0; ++		for (int i = 0; i < chars.length; i++) { ++			chars[i] = '\0'; ++		} ++		 + 		dwFileAttributes = is.readInt(); + 		length -= 4; +-		ftCreationTime = is.readLong(); ++		//ftCreationTime = is.readLong(); ++		ftCreationTimeL = is.readInt(); ++		ftCreationTimeH = is.readInt(); + 		length -= 8; +-		ftLastAccessTime = is.readLong(); ++		//ftLastAccessTime = is.readLong(); ++		ftLastAccessTimeL = is.readInt(); ++		ftLastAccessTimeH = is.readInt(); + 		length -= 8; +-		ftLastWriteTime = is.readLong(); ++		//ftLastWriteTime = is.readLong(); ++		ftLastWriteTimeL = is.readInt(); ++		ftLastWriteTimeH = is.readInt(); + 		length -= 8; + 		nFileSizeHigh = is.readInt(); + 		length -= 4; +@@ -1239,10 +1489,12 @@ + 		length -= 4; + 		cFileName = (char) is.readUnsignedByte(); + 		length--; ++		chars[char_cnt++] = cFileName; + 		while (cFileName != '\0') + 		{ + 			fileName += cFileName; + 			cFileName = (char) is.readUnsignedByte(); ++			chars[char_cnt++] = cFileName; + 			length--; + 		} + 		cAlternateFileName = (char) is.readByte(); +@@ -1253,7 +1505,28 @@ + 			cAlternateFileName = (char) is.readUnsignedByte(); + 			length--; + 		} +-		if (dwFileAttributes == 268435456 ++		String guessed = guess_encoding(chars); ++		if (!guessed.equals("ASCII")) { ++			System.out.println("guess: " + guessed + "\t" + fileName); ++		} ++		if (guessed.equals("UTF-8")) { ++			try { ++				byte[] bytes = new byte[char_cnt-1]; ++				for (int i=0; i < char_cnt-1; i++) { ++					bytes[i] = (byte) chars[i]; ++				} ++				String newstr = new String(bytes, "UTF-8"); ++				fileName = newstr; ++			} catch (Exception e) { ++				System.out.println("failed to convert bytes to UTF-8 based string"); ++			} ++		} ++		for (int i = 0; i < char_cnt; i++) { ++			//System.out.println("char[" + i + "]\t" + (int) chars[i]); ++		} ++		if (fileName.length() <= 0) { ++			; ++		} else if (dwFileAttributes == 268435456 + 			|| dwFileAttributes == 369098752 + 			|| dwFileAttributes == 285212672  + 			|| dwFileAttributes == 271056896 +@@ -1263,11 +1536,74 @@ + 			|| dwFileAttributes == 369623040) + 		{ + 			fileName = " [" + fileName + "]"; +-			remoteDirsList.add(fileName); // sf@2004 +-		} +-		else +-		{ +-			remoteFilesList.add(" " + fileName); // sf@2004 ++// begin runge/x11vnc ++//			remoteDirsList.add(fileName); // sf@2004 ++			int i = -1; ++			String t1 = fileName.toLowerCase(); ++			for (int j = 0; j < remoteDirsList.size(); j++) { ++				String t = (String) remoteDirsList.get(j); ++				String t2 = t.toLowerCase(); ++				if (t1.compareTo(t2) < 0) { ++					i = j; ++					break; ++				} ++			} ++			//String s = "Lastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + "    " + fileName;  ++			String f2 = fileName; ++			if (f2.length() < 24) { ++				for (int ik = f2.length(); ik < 24; ik++) { ++					f2 = f2 + " "; ++				} ++			} ++			String s = f2 + "    \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + "    \t\tSize: " + sizeStr(nFileSizeLow);  ++			//s = fileName + " Lastmod: " + zogswap(ftLastWriteTimeL);  ++			if (i >= 0) { ++				remoteDirsList.add(i, fileName); ++				remoteDirsListInfo.add(i, s); ++			} else { ++				remoteDirsList.add(fileName); ++				remoteDirsListInfo.add(s); ++			} ++// end runge/x11vnc ++		} else { ++// begin runge/x11vnc ++//			remoteFilesList.add(" " + fileName); // sf@2004 ++	 ++			fileName = " " + fileName; ++			int i = -1; ++			String t1 = fileName.toLowerCase(); ++			for (int j = 0; j < remoteFilesList.size(); j++) { ++				String t = (String) remoteFilesList.get(j); ++				String t2 = t.toLowerCase(); ++				if (t1.compareTo(t2) < 0) { ++					i = j; ++					break; ++				} ++			} ++			String f2 = fileName; ++			if (f2.length() < 24) { ++				for (int ik = f2.length(); ik < 24; ik++) { ++					f2 = f2 + " "; ++				} ++			} ++ ++if (false) { ++System.out.println("fileName:         " + f2); ++System.out.println("ftLastWriteTimeL: " + ftLastWriteTimeL); ++System.out.println("ftLastWriteTimeH: " + ftLastWriteTimeH); ++System.out.println("nFileSizeLow:     " + nFileSizeLow); ++} ++ ++			String s = f2 + "    \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + "    \t\tSize: " + sizeStr(nFileSizeLow);  ++			//s = fileName + " Lastmod: " + ftLastWriteTimeL + "/" + zogswap(ftLastWriteTimeL) + "  Size: " + nFileSizeLow + "/" + zogswap(nFileSizeLow);  ++			if (i >= 0) { ++				remoteFilesList.add(i, fileName); ++				remoteFilesListInfo.add(i, s); ++			} else { ++				remoteFilesList.add(fileName); ++				remoteFilesListInfo.add(s); ++			} ++// end runge/x11vnc + 		} + 	 + 		// a.add(fileName); +@@ -1282,14 +1618,32 @@ +  + 		// sf@2004 + 		a.clear(); +-		for (int i = 0; i < remoteDirsList.size(); i++)  ++		b.clear(); ++		for (int i = 0; i < remoteDirsList.size(); i++) { + 			a.add(remoteDirsList.get(i)); +-		for (int i = 0; i < remoteFilesList.size(); i++)  ++			b.add(remoteDirsListInfo.get(i)); ++		} ++		for (int i = 0; i < remoteFilesList.size(); i++) { + 			a.add(remoteFilesList.get(i)); ++ ++			b.add(remoteFilesListInfo.get(i)); ++		} + 		remoteDirsList.clear(); ++		remoteDirsListInfo.clear(); + 		remoteFilesList.clear(); ++		remoteFilesListInfo.clear(); + 		 +-		viewer.ftp.printDirectory(a); ++// begin runge/x11vnc ++		// Hack for double listing at startup... probably libvncserver bug.. ++		readServerDriveListCnt++; ++		if (readServerDriveListCnt == 2) { ++			if (System.currentTimeMillis() - readServerDriveListTime < 2000)  { ++//System.out.println("readServerDriveListCnt skip " + readServerDriveListCnt); ++				return; ++			} ++		} ++// end runge/x11vnc ++		viewer.ftp.printDirectory(a, b); + 	} +  + 	//Internally used to signify the drive requested is not ready +@@ -1299,6 +1653,8 @@ + 		System.out.println("Remote Drive unavailable"); + 		viewer.ftp.connectionStatus.setText(" > WARNING - Remote Drive unavailable (possibly restricted access or media not present)"); + 		viewer.ftp.remoteStatus.setText("WARNING: Remote Drive unavailable"); ++		viewer.ftp.historyComboBox.insertItemAt(new String(" > WARNING: Remote Drive unavailable."), 0); ++		viewer.ftp.historyComboBox.setSelectedIndex(0); + 	} +  + 	//Call this method to request the list of drives on the server. +@@ -1306,12 +1662,11 @@ + 	{ + 		try + 		{ +-			viewer.rfb.writeRfbFileTransferMsg( +-												RfbProto.rfbDirContentRequest, +-												RfbProto.rfbRDrivesList, +-												0, +-												0, +-												null); ++			viewer.rfb.writeRfbFileTransferMsg(RfbProto.rfbDirContentRequest, RfbProto.rfbRDrivesList, 0, 0, null); ++// begin runge/x11vnc ++			readServerDriveListCnt = 0; ++			readServerDriveListTime = System.currentTimeMillis(); ++// end   runge/x11vnc + 		} + 		catch (IOException e) + 		{ +@@ -1355,21 +1710,21 @@ + 		int h, + 		boolean incremental) + 		throws IOException { +-			if (!viewer.ftp.isVisible()) { +-		byte[] b = new byte[10]; ++		if (!viewer.ftp.isVisible()) { ++			byte[] b = new byte[10]; +  +-		b[0] = (byte) FramebufferUpdateRequest; +-		b[1] = (byte) (incremental ? 1 : 0); +-		b[2] = (byte) ((x >> 8) & 0xff); +-		b[3] = (byte) (x & 0xff); +-		b[4] = (byte) ((y >> 8) & 0xff); +-		b[5] = (byte) (y & 0xff); +-		b[6] = (byte) ((w >> 8) & 0xff); +-		b[7] = (byte) (w & 0xff); +-		b[8] = (byte) ((h >> 8) & 0xff); +-		b[9] = (byte) (h & 0xff); ++			b[0] = (byte) FramebufferUpdateRequest; ++			b[1] = (byte) (incremental ? 1 : 0); ++			b[2] = (byte) ((x >> 8) & 0xff); ++			b[3] = (byte) (x & 0xff); ++			b[4] = (byte) ((y >> 8) & 0xff); ++			b[5] = (byte) (y & 0xff); ++			b[6] = (byte) ((w >> 8) & 0xff); ++			b[7] = (byte) (w & 0xff); ++			b[8] = (byte) ((h >> 8) & 0xff); ++			b[9] = (byte) (h & 0xff); +  +-		os.write(b); ++			os.write(b); + 		} + 	} +  +@@ -1482,7 +1837,13 @@ + 		b[6] = (byte) ((text.length() >> 8) & 0xff); + 		b[7] = (byte) (text.length() & 0xff); +  +-		System.arraycopy(text.getBytes(), 0, b, 8, text.length()); ++		if (false && max_char(text) > 255) { ++			System.arraycopy(text.getBytes("UTF-8"), 0, b, 8, text.length()); ++		} else if (max_char(text) > 127) { ++			System.arraycopy(text.getBytes("ISO-8859-1"), 0, b, 8, text.length()); ++		} else { ++			System.arraycopy(text.getBytes(), 0, b, 8, text.length()); ++		} +  + 		os.write(b); + 	//	} +@@ -1506,6 +1867,37 @@ + 	final static int META_MASK = InputEvent.META_MASK; + 	final static int ALT_MASK = InputEvent.ALT_MASK; +  ++	void writeWheelEvent(MouseWheelEvent evt) throws IOException { ++		eventBufLen = 0; ++ ++		int x = evt.getX(); ++		int y = evt.getY(); ++ ++		if (x < 0) x = 0; ++		if (y < 0) y = 0; ++ ++		int ptrmask; ++ ++		int clicks = evt.getWheelRotation(); ++		System.out.println("writeWheelEvent: clicks: " + clicks); ++		if (clicks > 0) { ++			ptrmask = 16; ++		} else if (clicks < 0) { ++			ptrmask = 8; ++		} else { ++			return; ++		} ++ ++		eventBuf[eventBufLen++] = (byte) PointerEvent; ++		eventBuf[eventBufLen++] = (byte) ptrmask; ++		eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); ++		eventBuf[eventBufLen++] = (byte) (x & 0xff); ++		eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); ++		eventBuf[eventBufLen++] = (byte) (y & 0xff); ++ ++		os.write(eventBuf, 0, eventBufLen); ++	} ++ + 	// + 	// Write a pointer event message.  We may need to send modifier key events + 	// around it to set the correct modifier state. +@@ -1610,6 +2002,21 @@ +  + 		boolean down = (evt.getID() == KeyEvent.KEY_PRESSED); +  ++		if (viewer.debugKeyboard) { ++			System.out.println("----------------------------------------"); ++			System.out.println("evt.getKeyChar:      " + evt.getKeyChar()); ++			System.out.println("getKeyText:          " + KeyEvent.getKeyText(evt.getKeyCode())); ++			System.out.println("evt.getKeyCode:      " + evt.getKeyCode()); ++			System.out.println("evt.getID:           " + evt.getID()); ++			System.out.println("evt.getKeyLocation:  " + evt.getKeyLocation()); ++			System.out.println("evt.isActionKey:     " + evt.isActionKey()); ++			System.out.println("evt.isControlDown:   " + evt.isControlDown()); ++			System.out.println("evt.getModifiers:    " + evt.getModifiers()); ++			System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers())); ++			System.out.println("evt.paramString:     " + evt.paramString()); ++		} ++ ++ + 		int key; + 		if (evt.isActionKey()) { +  +@@ -1685,6 +2092,9 @@ + 				default : + 					return; + 			} ++			if (key == 0xffc2 && viewer.mapF5_to_atsign) { ++				key = 0x40; ++			} +  + 		} else { +  +@@ -1794,6 +2204,16 @@ + 	int oldModifiers = 0; +  + 	void writeModifierKeyEvents(int newModifiers) { ++		if(viewer.forbid_Ctrl_Alt) { ++			if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) { ++				int orig = newModifiers; ++				newModifiers &= ~ALT_MASK; ++				newModifiers &= ~CTRL_MASK; ++				if (viewer.debugKeyboard) { ++					System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers); ++				} ++			} ++		} + 		if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) + 			writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); +  +diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java +--- JavaViewer.orig/SSLSocketToMe.java	1969-12-31 19:00:00.000000000 -0500 ++++ JavaViewer/SSLSocketToMe.java	2010-07-10 19:18:06.000000000 -0400 +@@ -0,0 +1,2067 @@ ++/* ++ * SSLSocketToMe.java: add SSL encryption to Java VNC Viewer. ++ * ++ * Copyright (c) 2006 Karl J. Runge <runge@karlrunge.com> ++ * All rights reserved. ++ * ++ *  This is free software; you can redistribute it and/or modify ++ *  it under the terms of the GNU General Public License as published by ++ *  the Free Software Foundation; version 2 of the License, or ++ *  (at your option) any later version. ++ * ++ *  This software is distributed in the hope that it will be useful, ++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of ++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the ++ *  GNU General Public License for more details. ++ * ++ *  You should have received a copy of the GNU General Public License ++ *  along with this software; if not, write to the Free Software ++ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, ++ *  USA. ++ * ++ */ ++ ++import java.net.*; ++import java.io.*; ++import javax.net.ssl.*; ++import java.util.*; ++ ++import java.security.*; ++import java.security.cert.*; ++import java.security.spec.*; ++import java.security.cert.Certificate; ++import java.security.cert.CertificateFactory; ++ ++import java.awt.*; ++import java.awt.event.*; ++ ++public class SSLSocketToMe { ++ ++	/* basic member data: */ ++	String host; ++	int port; ++	VncViewer viewer; ++ ++	boolean debug = true; ++	boolean debug_certs = false; ++ ++	/* sockets */ ++	SSLSocket socket = null; ++	SSLSocketFactory factory; ++ ++	/* fallback for Proxy connection */ ++	boolean proxy_in_use = false; ++	boolean proxy_failure = false; ++	public DataInputStream is = null; ++	public OutputStream os = null; ++ ++	/* strings from user WRT proxy: */ ++	String proxy_auth_string = null; ++	String proxy_dialog_host = null; ++	int proxy_dialog_port = 0; ++ ++	Socket proxySock; ++	DataInputStream proxy_is; ++	OutputStream proxy_os; ++ ++	/* trust contexts */ ++	SSLContext trustloc_ctx; ++	SSLContext trustall_ctx; ++	SSLContext trustsrv_ctx; ++	SSLContext trusturl_ctx; ++	SSLContext trustone_ctx; ++ ++	/* corresponding trust managers */ ++	TrustManager[] trustAllCerts; ++	TrustManager[] trustSrvCert; ++	TrustManager[] trustUrlCert; ++	TrustManager[] trustOneCert; ++ ++	/* client-side SSL auth key (oneTimeKey=...) */ ++	KeyManager[] mykey = null; ++ ++	boolean user_wants_to_see_cert = true; ++	String cert_fail = null; ++ ++	/* cert(s) we retrieve from Web server, VNC server, or serverCert param: */ ++	java.security.cert.Certificate[] trustallCerts = null; ++	java.security.cert.Certificate[] trustsrvCerts = null; ++	java.security.cert.Certificate[] trusturlCerts = null; ++ ++	/* utility to decode hex oneTimeKey=... and serverCert=... */ ++	byte[] hex2bytes(String s) { ++		byte[] bytes = new byte[s.length()/2]; ++		for (int i=0; i<s.length()/2; i++) { ++			int j = 2*i; ++			try { ++				int val = Integer.parseInt(s.substring(j, j+2), 16); ++				if (val > 127) { ++					val -= 256; ++				} ++				Integer I = new Integer(val); ++				bytes[i] = Byte.decode(I.toString()).byteValue(); ++				 ++			} catch (Exception e) { ++				; ++			} ++		} ++		return bytes; ++	} ++ ++	SSLSocketToMe(String h, int p, VncViewer v) throws Exception { ++		host = h; ++		port = p; ++		viewer = v; ++ ++		debug_certs = v.debugCerts; ++ ++		/* we will first try default factory for certification: */ ++ ++		factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); ++ ++		dbg("SSL startup: " + host + " " + port); ++ ++ ++		/* create trust managers to be used if initial handshake fails: */ ++ ++		trustAllCerts = new TrustManager[] { ++		    /* ++		     * this one accepts everything.  Only used if user ++		     * has disabled checking (trustAllVncCerts=yes) ++		     * or when we grab the cert to show it to them in ++		     * a dialog and ask them to manually verify/accept it. ++		     */ ++		    new X509TrustManager() { ++			public java.security.cert.X509Certificate[] ++			    getAcceptedIssuers() { ++				return null; ++			} ++			public void checkClientTrusted( ++			    java.security.cert.X509Certificate[] certs, ++			    String authType) { ++				/* empty */ ++			} ++			public void checkServerTrusted( ++			    java.security.cert.X509Certificate[] certs, ++			    String authType) { ++				/* empty */ ++				dbg("ALL: an untrusted connect to grab cert."); ++			} ++		    } ++		}; ++ ++		trustUrlCert = new TrustManager[] { ++		    /* ++		     * this one accepts only the retrieved server ++		     * cert by SSLSocket by this applet and stored in ++		     * trusturlCerts. ++		     */ ++		    new X509TrustManager() { ++			public java.security.cert.X509Certificate[] ++			    getAcceptedIssuers() { ++				return null; ++			} ++			public void checkClientTrusted( ++			    java.security.cert.X509Certificate[] certs, ++			    String authType) throws CertificateException { ++				throw new CertificateException("No Clients (URL)"); ++			} ++			public void checkServerTrusted( ++			    java.security.cert.X509Certificate[] certs, ++			    String authType) throws CertificateException { ++				/* we want to check 'certs' against 'trusturlCerts' */ ++				if (trusturlCerts == null) { ++					throw new CertificateException( ++					    "No Trust url Certs array."); ++				} ++				if (trusturlCerts.length < 1) { ++					throw new CertificateException( ++					    "No Trust url Certs."); ++				} ++				if (certs == null) { ++					throw new CertificateException( ++					    "No this-certs array."); ++				} ++				if (certs.length < 1) { ++					throw new CertificateException( ++					    "No this-certs Certs."); ++				} ++				if (certs.length != trusturlCerts.length) { ++					throw new CertificateException( ++					    "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length); ++				} ++				boolean ok = true; ++				for (int i = 0; i < certs.length; i++)  { ++					if (! trusturlCerts[i].equals(certs[i])) { ++						ok = false; ++						dbg("URL: cert mismatch at i=" + i); ++						dbg("URL: cert mismatch cert" + certs[i]); ++						dbg("URL: cert mismatch  url" + trusturlCerts[i]); ++						if (cert_fail == null) { ++							cert_fail = "cert-mismatch"; ++						} ++					} ++					if (debug_certs) { ++						dbg("\n***********************************************"); ++						dbg("URL: cert info at i=" + i); ++						dbg("URL: cert info cert" + certs[i]); ++						dbg("==============================================="); ++						dbg("URL: cert info  url" + trusturlCerts[i]); ++						dbg("***********************************************"); ++					} ++				} ++				if (!ok) { ++					throw new CertificateException( ++					    "Server Cert Chain != URL Cert Chain."); ++				} ++				dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++			} ++		    } ++		}; ++ ++		trustSrvCert = new TrustManager[] { ++		    /* ++		     * this one accepts cert given to us in the serverCert ++		     * Applet Parameter we were started with.  It is ++		     * currently a fatal error if the VNC Server's cert ++		     * doesn't match it. ++		     */ ++		    new X509TrustManager() { ++			public java.security.cert.X509Certificate[] ++			    getAcceptedIssuers() { ++				return null; ++			} ++			public void checkClientTrusted( ++			    java.security.cert.X509Certificate[] certs, ++			    String authType) throws CertificateException { ++				throw new CertificateException("No Clients (SRV)"); ++			} ++			public void checkServerTrusted( ++			    java.security.cert.X509Certificate[] certs, ++			    String authType) throws CertificateException { ++				/* we want to check 'certs' against 'trustsrvCerts' */ ++				if (trustsrvCerts == null) { ++					throw new CertificateException( ++					    "No Trust srv Certs array."); ++				} ++				if (trustsrvCerts.length < 1) { ++					throw new CertificateException( ++					    "No Trust srv Certs."); ++				} ++				if (certs == null) { ++					throw new CertificateException( ++					    "No this-certs array."); ++				} ++				if (certs.length < 1) { ++					throw new CertificateException( ++					    "No this-certs Certs."); ++				} ++				if (certs.length != trustsrvCerts.length) { ++					throw new CertificateException( ++					    "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length); ++				} ++				boolean ok = true; ++				for (int i = 0; i < certs.length; i++)  { ++					if (! trustsrvCerts[i].equals(certs[i])) { ++						ok = false; ++						dbg("SRV: cert mismatch at i=" + i); ++						dbg("SRV: cert mismatch cert" + certs[i]); ++						dbg("SRV: cert mismatch  srv" + trustsrvCerts[i]); ++						if (cert_fail == null) { ++							cert_fail = "server-cert-mismatch"; ++						} ++					} ++					if (debug_certs) { ++						dbg("\n***********************************************"); ++						dbg("SRV: cert info at i=" + i); ++						dbg("SRV: cert info cert" + certs[i]); ++						dbg("==============================================="); ++						dbg("SRV: cert info  srv" + trustsrvCerts[i]); ++						dbg("***********************************************"); ++					} ++				} ++				if (!ok) { ++					throw new CertificateException( ++					    "Server Cert Chain != serverCert Applet Parameter Cert Chain."); ++				} ++				dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++			} ++		    } ++		}; ++ ++		trustOneCert = new TrustManager[] { ++		    /* ++		     * this one accepts only the retrieved server ++		     * cert by SSLSocket by this applet we stored in ++		     * trustallCerts that user has accepted or applet ++		     * parameter trustAllVncCerts=yes is set.  This is ++		     * for when we reconnect after the user has manually ++		     * accepted the trustall cert in the dialog (or set ++		     * trustAllVncCerts=yes applet param.) ++		     */ ++		    new X509TrustManager() { ++			public java.security.cert.X509Certificate[] ++			    getAcceptedIssuers() { ++				return null; ++			} ++			public void checkClientTrusted( ++			    java.security.cert.X509Certificate[] certs, ++			    String authType) throws CertificateException { ++				throw new CertificateException("No Clients (ONE)"); ++			} ++			public void checkServerTrusted( ++			    java.security.cert.X509Certificate[] certs, ++			    String authType) throws CertificateException { ++				/* we want to check 'certs' against 'trustallCerts' */ ++				if (trustallCerts == null) { ++					throw new CertificateException( ++					    "No Trust All Server Certs array."); ++				} ++				if (trustallCerts.length < 1) { ++					throw new CertificateException( ++					    "No Trust All Server Certs."); ++				} ++				if (certs == null) { ++					throw new CertificateException( ++					    "No this-certs array."); ++				} ++				if (certs.length < 1) { ++					throw new CertificateException( ++					    "No this-certs Certs."); ++				} ++				if (certs.length != trustallCerts.length) { ++					throw new CertificateException( ++					    "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length); ++				} ++				boolean ok = true; ++				for (int i = 0; i < certs.length; i++)  { ++					if (! trustallCerts[i].equals(certs[i])) { ++						ok = false; ++						dbg("ONE: cert mismatch at i=" + i); ++						dbg("ONE: cert mismatch cert" + certs[i]); ++						dbg("ONE: cert mismatch  all" + trustallCerts[i]); ++					} ++					if (debug_certs) { ++						dbg("\n***********************************************"); ++						dbg("ONE: cert info at i=" + i); ++						dbg("ONE: cert info cert" + certs[i]); ++						dbg("==============================================="); ++						dbg("ONE: cert info  all" + trustallCerts[i]); ++						dbg("***********************************************"); ++					} ++				} ++				if (!ok) { ++					throw new CertificateException( ++					    "Server Cert Chain != TRUSTALL Cert Chain."); ++				} ++				dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++			} ++		    } ++		}; ++ ++		/*  ++		 * The above TrustManagers are used: ++		 * ++		 * 1) to retrieve the server cert in case of failure to ++		 *    display it to the user in a dialog. ++		 * 2) to subsequently connect to the server if user agrees. ++		 */ ++ ++		/* ++		 * build oneTimeKey cert+key if supplied in applet parameter: ++		 */ ++		if (viewer.oneTimeKey != null && viewer.oneTimeKey.equals("PROMPT")) { ++			ClientCertDialog d = new ClientCertDialog(); ++			viewer.oneTimeKey = d.queryUser(); ++		} ++		if (viewer.oneTimeKey != null && viewer.oneTimeKey.indexOf(",") > 0) { ++			int idx = viewer.oneTimeKey.indexOf(","); ++ ++			String onetimekey = viewer.oneTimeKey.substring(0, idx); ++			byte[] key = hex2bytes(onetimekey); ++			String onetimecert = viewer.oneTimeKey.substring(idx+1); ++			byte[] cert = hex2bytes(onetimecert); ++ ++			KeyFactory kf = KeyFactory.getInstance("RSA"); ++			PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key ); ++			PrivateKey ff = kf.generatePrivate (keysp); ++			if (debug_certs) { ++				dbg("one time key " + ff); ++			} ++ ++			CertificateFactory cf = CertificateFactory.getInstance("X.509"); ++			Collection c = cf.generateCertificates(new ByteArrayInputStream(cert)); ++			Certificate[] certs = new Certificate[c.toArray().length]; ++			if (c.size() == 1) { ++				Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert)); ++				if (debug_certs) { ++					dbg("one time cert" + tmpcert); ++				} ++				certs[0] = tmpcert; ++			} else { ++				certs = (Certificate[]) c.toArray(); ++			} ++ ++			KeyStore ks = KeyStore.getInstance("JKS"); ++			ks.load(null, null); ++			ks.setKeyEntry("onetimekey", ff, "".toCharArray(), certs); ++			String da = KeyManagerFactory.getDefaultAlgorithm(); ++			KeyManagerFactory kmf = KeyManagerFactory.getInstance(da); ++			kmf.init(ks, "".toCharArray()); ++ ++			mykey = kmf.getKeyManagers(); ++		} ++ ++		/* ++		 * build serverCert cert if supplied in applet parameter: ++		 */ ++		if (viewer.serverCert != null) { ++			CertificateFactory cf = CertificateFactory.getInstance("X.509"); ++			byte[] cert = hex2bytes(viewer.serverCert); ++			Collection c = cf.generateCertificates(new ByteArrayInputStream(cert)); ++			trustsrvCerts = new Certificate[c.toArray().length]; ++			if (c.size() == 1) { ++				Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert)); ++				trustsrvCerts[0] = tmpcert; ++			} else { ++				trustsrvCerts = (Certificate[]) c.toArray(); ++			} ++		} ++ ++		/* the trust loc certs context: */ ++		try { ++			trustloc_ctx = SSLContext.getInstance("SSL"); ++ ++			/* ++			 * below is a failed attempt to get jvm's default ++			 * trust manager using null (below) makes it so ++			 * for HttpsURLConnection the server cannot be ++			 * verified (no prompting.) ++			 */ ++			if (false) { ++				boolean didit = false; ++				TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());   ++				tmf.init((KeyStore) null); ++				TrustManager [] tml = tmf.getTrustManagers(); ++				for (int i = 0; i < tml.length; i++) { ++					TrustManager tm = tml[i]; ++					if (tm instanceof X509TrustManager) { ++						TrustManager tm1[] = new TrustManager[1]; ++						tm1[0] = tm; ++						trustloc_ctx.init(mykey, tm1, null); ++						didit = true; ++						break; ++					} ++				} ++				if (!didit) { ++					trustloc_ctx.init(mykey, null, null); ++				} ++			} else { ++				/* we have to set trust manager to null */ ++				trustloc_ctx.init(mykey, null, null); ++			} ++ ++		} catch (Exception e) { ++			String msg = "SSL trustloc_ctx FAILED."; ++			dbg(msg); ++			throw new Exception(msg); ++		} ++ ++		/* the trust all certs context: */ ++		try { ++			trustall_ctx = SSLContext.getInstance("SSL"); ++			trustall_ctx.init(mykey, trustAllCerts, new ++			    java.security.SecureRandom()); ++ ++		} catch (Exception e) { ++			String msg = "SSL trustall_ctx FAILED."; ++			dbg(msg); ++			throw new Exception(msg); ++		} ++ ++		/* the trust url certs context: */ ++		try { ++			trusturl_ctx = SSLContext.getInstance("SSL"); ++			trusturl_ctx.init(mykey, trustUrlCert, new ++			    java.security.SecureRandom()); ++ ++		} catch (Exception e) { ++			String msg = "SSL trusturl_ctx FAILED."; ++			dbg(msg); ++			throw new Exception(msg); ++		} ++ ++		/* the trust srv certs context: */ ++		try { ++			trustsrv_ctx = SSLContext.getInstance("SSL"); ++			trustsrv_ctx.init(mykey, trustSrvCert, new ++			    java.security.SecureRandom()); ++ ++		} catch (Exception e) { ++			String msg = "SSL trustsrv_ctx FAILED."; ++			dbg(msg); ++			throw new Exception(msg); ++		} ++ ++		/* the trust the one cert from server context: */ ++		try { ++			trustone_ctx = SSLContext.getInstance("SSL"); ++			trustone_ctx.init(mykey, trustOneCert, new ++			    java.security.SecureRandom()); ++ ++		} catch (Exception e) { ++			String msg = "SSL trustone_ctx FAILED."; ++			dbg(msg); ++			throw new Exception(msg); ++		} ++	} ++ ++	/* ++	 * we call this early on to 1) check for a proxy, 2) grab ++	 * Browser/JVM accepted HTTPS cert. ++	 */ ++	public void check_for_proxy_and_grab_vnc_server_cert() { ++		 ++		trusturlCerts = null; ++		proxy_in_use = false; ++ ++		if (viewer.ignoreProxy) { ++			/* applet param says skip it. */ ++			/* the downside is we do not set trusturlCerts for comparison later... */ ++			/* nor do we autodetect x11vnc for GET=1. */ ++			return; ++		} ++ ++		dbg("------------------------------------------------"); ++		dbg("Into check_for_proxy_and_grab_vnc_server_cert():"); ++ ++		dbg("TRYING HTTPS:"); ++		String ustr = "https://" + host + ":"; ++		if (viewer.httpsPort != null) { ++			ustr += viewer.httpsPort; ++		} else { ++			ustr += port; ++		} ++		ustr += viewer.urlPrefix + "/check.https.proxy.connection"; ++		dbg("ustr is: " + ustr); ++ ++		try { ++			/* prepare for an HTTPS URL connection to host:port */ ++			URL url = new URL(ustr); ++			HttpsURLConnection https = (HttpsURLConnection) url.openConnection(); ++ ++			if (mykey != null) { ++				/* with oneTimeKey (mykey) we can't use the default SSL context */ ++				if (trustsrvCerts != null) { ++					dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert."); ++					https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory());	 ++				} else if (trustloc_ctx != null) { ++					dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert."); ++					https.setSSLSocketFactory(trustloc_ctx.getSocketFactory());	 ++				} ++			} ++ ++			https.setUseCaches(false); ++			https.setRequestMethod("GET"); ++			https.setRequestProperty("Pragma", "No-Cache"); ++			https.setRequestProperty("Proxy-Connection", "Keep-Alive"); ++			https.setDoInput(true); ++ ++			dbg("trying https.connect()"); ++			https.connect(); ++ ++			dbg("trying https.getServerCertificates()"); ++			trusturlCerts = https.getServerCertificates(); ++ ++			if (trusturlCerts == null) { ++				dbg("set trusturlCerts to null!"); ++			} else { ++				dbg("set trusturlCerts to non-null"); ++			} ++ ++			if (https.usingProxy()) { ++				proxy_in_use = true; ++				dbg("An HTTPS proxy is in use. There may be connection problems."); ++			} ++ ++			dbg("trying https.getContent()"); ++			Object output = https.getContent(); ++			dbg("trying https.disconnect()"); ++			https.disconnect(); ++			if (! viewer.GET) { ++				String header = https.getHeaderField("VNC-Server"); ++				if (header != null && header.startsWith("x11vnc")) { ++					dbg("detected x11vnc server (1), setting GET=1"); ++					viewer.GET = true; ++				} ++			} ++ ++		} catch(Exception e) { ++			dbg("HttpsURLConnection: " + e.getMessage()); ++		} ++ ++		if (proxy_in_use) { ++			dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++			dbg("------------------------------------------------"); ++			return; ++		} else if (trusturlCerts != null && !viewer.forceProxy) { ++			/* Allow user to require HTTP check?  use forceProxy for now. */ ++			dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct."); ++			dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++			dbg("------------------------------------------------"); ++			return; ++		} ++ ++		/* ++		 * XXX need to remember scenario where this extra check ++		 * gives useful info.  User's Browser proxy settings? ++		 */ ++		dbg("TRYING HTTP:"); ++		ustr = "http://" + host + ":" + port; ++		ustr += viewer.urlPrefix + "/index.vnc"; ++		dbg("ustr is: " + ustr); ++ ++		try { ++			/* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */ ++			URL url = new URL(ustr); ++			HttpURLConnection http = (HttpURLConnection) ++			    url.openConnection(); ++ ++			http.setUseCaches(false); ++			http.setRequestMethod("GET"); ++			http.setRequestProperty("Pragma", "No-Cache"); ++			http.setRequestProperty("Proxy-Connection", "Keep-Alive"); ++			http.setDoInput(true); ++ ++			dbg("trying http.connect()"); ++			http.connect(); ++ ++			if (http.usingProxy()) { ++				proxy_in_use = true; ++				dbg("An HTTP proxy is in use. There may be connection problems."); ++			} ++			dbg("trying http.getContent()"); ++			Object output = http.getContent(); ++			dbg("trying http.disconnect()"); ++			http.disconnect(); ++			if (! viewer.GET) { ++				String header = http.getHeaderField("VNC-Server"); ++				if (header != null && header.startsWith("x11vnc")) { ++					dbg("detected x11vnc server (2), setting GET=1"); ++					viewer.GET = true; ++				} ++			} ++		} catch(Exception e) { ++			dbg("HttpURLConnection:  " + e.getMessage()); ++		} ++		dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++		dbg("------------------------------------------------"); ++	} ++ ++	public Socket connectSock() throws IOException { ++		/* ++		 * first try a https connection to detect a proxy, and ++		 * grab the VNC server cert at the same time: ++		 */ ++		check_for_proxy_and_grab_vnc_server_cert(); ++ ++		boolean srv_cert = false; ++		 ++		if (trustsrvCerts != null) { ++			/* applet parameter suppled serverCert */ ++			dbg("viewer.trustSrvCert-0 using trustsrv_ctx"); ++			factory = trustsrv_ctx.getSocketFactory(); ++			srv_cert = true; ++		} else if (viewer.trustAllVncCerts) { ++			/* trust all certs (no checking) */ ++			dbg("viewer.trustAllVncCerts-0 using trustall_ctx"); ++			factory = trustall_ctx.getSocketFactory(); ++		} else if (trusturlCerts != null) { ++			/* trust certs the Browser/JVM accepted in check_for_proxy... */ ++			dbg("using trusturl_ctx"); ++			factory = trusturl_ctx.getSocketFactory(); ++		} else { ++			/* trust the local defaults */ ++			dbg("using trustloc_ctx"); ++			factory = trustloc_ctx.getSocketFactory(); ++		} ++ ++		socket = null; ++ ++		try { ++			if (proxy_in_use && viewer.forceProxy) { ++				throw new Exception("forcing proxy (forceProxy)"); ++			} else if (viewer.CONNECT != null) { ++				throw new Exception("forcing CONNECT"); ++			} ++ ++			int timeout = 6; ++			if (timeout > 0) { ++				socket = (SSLSocket) factory.createSocket(); ++				InetSocketAddress inetaddr = new InetSocketAddress(host, port); ++				dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port); ++				socket.connect(inetaddr, timeout * 1000); ++			} else { ++				socket = (SSLSocket) factory.createSocket(host, port); ++			} ++ ++		} catch (Exception esock) { ++			dbg("socket error: " + esock.getMessage()); ++			if (proxy_in_use || viewer.CONNECT != null) { ++				proxy_failure = true; ++				if (proxy_in_use) { ++					dbg("HTTPS proxy in use. Trying to go with it."); ++				} else { ++					dbg("viewer.CONNECT reverse proxy in use. Trying to go with it."); ++				} ++				try { ++					socket = proxy_socket(factory); ++				} catch (Exception e) { ++					dbg("proxy_socket error: " + e.getMessage()); ++				} ++			} else { ++				/* n.b. socket is left in error state to cause ex. below. */ ++			} ++		} ++ ++		try { ++			socket.startHandshake(); ++ ++			dbg("The Server Connection Verified OK on 1st try."); ++ ++			java.security.cert.Certificate[] currentTrustedCerts; ++			BrowserCertsDialog bcd; ++ ++			SSLSession sess = socket.getSession(); ++			currentTrustedCerts = sess.getPeerCertificates(); ++ ++			if (viewer.trustAllVncCerts) { ++				dbg("viewer.trustAllVncCerts-1  keeping socket."); ++			} else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) { ++				try { ++					socket.close(); ++				} catch (Exception e) { ++					dbg("socket is grumpy."); ++				} ++				socket = null; ++				throw new SSLHandshakeException("no current certs"); ++			} ++ ++			String serv = ""; ++			try { ++				CertInfo ci = new CertInfo(currentTrustedCerts[0]); ++				serv = ci.get_certinfo("CN"); ++			} catch (Exception e) { ++				; ++			} ++ ++			if (viewer.trustAllVncCerts) { ++				dbg("viewer.trustAllVncCerts-2  skipping browser certs dialog"); ++				user_wants_to_see_cert = false; ++			} else if (viewer.serverCert != null && trustsrvCerts != null) { ++				dbg("viewer.serverCert-1  skipping browser certs dialog"); ++				user_wants_to_see_cert = false; ++			} else if (viewer.trustUrlVncCert) { ++				dbg("viewer.trustUrlVncCert-1  skipping browser certs dialog"); ++				user_wants_to_see_cert = false; ++			} else { ++				/* have a dialog with the user: */ ++				bcd = new BrowserCertsDialog(serv, host + ":" + port); ++				dbg("browser certs dialog begin."); ++				bcd.queryUser(); ++				dbg("browser certs dialog finished."); ++ ++				if (bcd.showCertDialog) { ++					String msg = "user wants to see cert"; ++					dbg(msg); ++					user_wants_to_see_cert = true; ++					if (cert_fail == null) { ++						cert_fail = "user-view"; ++					} ++					throw new SSLHandshakeException(msg); ++				} else { ++					user_wants_to_see_cert = false; ++					dbg("browser certs dialog: user said yes, accept it"); ++				} ++			} ++ ++		} catch (SSLHandshakeException eh)  { ++			dbg("SSLHandshakeException: could not automatically verify Server."); ++			dbg("msg: " + eh.getMessage()); ++ ++ ++			/* send a cleanup string just in case: */ ++			String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n"; ++ ++			try { ++				OutputStream os = socket.getOutputStream(); ++				os.write(getoutstr.getBytes()); ++				socket.close(); ++			} catch (Exception e) { ++				dbg("socket is grumpy!"); ++			} ++ ++			/* reload */ ++ ++			socket = null; ++ ++			String reason = null; ++ ++			if (srv_cert) { ++				/* for serverCert usage we make this a fatal error. */ ++				throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'"); ++				/* see below in TrustDialog were we describe this case to user anyway */ ++			} ++ ++			/* ++			 * Reconnect, trusting any cert, so we can grab ++			 * the cert to show it to the user in a dialog ++			 * for him to manually accept.  This connection ++			 * is not used for anything else. ++			 */ ++			factory = trustall_ctx.getSocketFactory(); ++			if (proxy_failure) { ++				socket = proxy_socket(factory); ++			} else { ++				socket = (SSLSocket) factory.createSocket(host, port); ++			} ++ ++			if (debug_certs) { ++				dbg("trusturlCerts: " + trusturlCerts); ++				dbg("trustsrvCerts: " + trustsrvCerts); ++			} ++			if (trusturlCerts == null && cert_fail == null) { ++				cert_fail = "missing-certs"; ++			} ++ ++			try { ++				socket.startHandshake(); ++ ++				dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK."); ++ ++				/* grab the cert: */ ++				try { ++					SSLSession sess = socket.getSession(); ++					trustallCerts = sess.getPeerCertificates(); ++				} catch (Exception e) { ++					throw new Exception("Could not get " +  ++					    "Peer Certificate");	 ++				} ++				if (debug_certs) { ++					dbg("trustallCerts: " + trustallCerts); ++				} ++ ++				if (viewer.trustAllVncCerts) { ++					dbg("viewer.trustAllVncCerts-3.  skipping dialog, trusting everything."); ++				} else if (! browser_cert_match()) { ++					/* ++					 * close socket now, we will reopen after ++					 * dialog if user agrees to use the cert. ++					 */ ++					try { ++						OutputStream os = socket.getOutputStream(); ++						os.write(getoutstr.getBytes()); ++						socket.close(); ++					} catch (Exception e) { ++						dbg("socket is grumpy!!"); ++					} ++					socket = null; ++ ++					/* dialog with user to accept cert or not: */ ++ ++					TrustDialog td= new TrustDialog(host, port, ++					    trustallCerts); ++ ++					if (cert_fail == null) { ++						; ++					} else if (cert_fail.equals("user-view")) { ++						reason = "Reason for this Dialog:\n\n" ++						       + "        You Asked to View the Certificate."; ++					} else if (cert_fail.equals("server-cert-mismatch")) { ++						/* this is now fatal error, see above. */ ++						reason = "Reason for this Dialog:\n\n" ++						       + "        The VNC Server's Certificate does not match the Certificate\n" ++						       + "        specified in the supplied 'serverCert' Applet Parameter."; ++					} else if (cert_fail.equals("cert-mismatch")) { ++						reason = "Reason for this Dialog:\n\n" ++						       + "        The VNC Server's Certificate does not match the Website's\n" ++						       + "        HTTPS Certificate (that you previously accepted; either\n" ++						       + "        manually or automatically via Certificate Authority.)"; ++					} else if (cert_fail.equals("missing-certs")) { ++						reason = "Reason for this Dialog:\n\n" ++						       + "        Not all Certificates could be obtained to check."; ++					} ++ ++					if (! td.queryUser(reason)) { ++						String msg = "User decided against it."; ++						dbg(msg); ++						throw new IOException(msg); ++					} ++				} ++ ++			} catch (Exception ehand2)  { ++				dbg("** Could not TrustAll Verify Server!"); ++ ++				throw new IOException(ehand2.getMessage()); ++			} ++ ++			/* reload again: */ ++ ++			if (socket != null) { ++				try { ++					socket.close(); ++				} catch (Exception e) { ++					dbg("socket is grumpy!!!"); ++				} ++				socket = null; ++			} ++ ++			/* ++			 * Now connect a 3rd time, using the cert ++			 * retrieved during connection 2 (sadly, that ++			 * the user likely blindly agreed to...) ++			 */ ++ ++			factory = trustone_ctx.getSocketFactory(); ++			if (proxy_failure) { ++				socket = proxy_socket(factory); ++			} else { ++				socket = (SSLSocket) factory.createSocket(host, port); ++			} ++ ++			try { ++				socket.startHandshake(); ++				dbg("TrustAll/TrustOne Server Connection Verified #3."); ++ ++			} catch (Exception ehand3)  { ++				dbg("** Could not TrustAll/TrustOne Verify Server #3."); ++ ++				throw new IOException(ehand3.getMessage()); ++			} ++		} ++ ++		/* we have socket (possibly null) at this point, so proceed: */ ++ ++		/* handle x11vnc GET=1, if applicable: */ ++		if (socket != null && viewer.GET) { ++			String str = "GET "; ++			str += viewer.urlPrefix; ++			str += "/request.https.vnc.connection"; ++			str += " HTTP/1.0\r\n"; ++			str += "Pragma: No-Cache\r\n"; ++			str += "\r\n"; ++ ++			System.out.println("sending: " + str); ++    			OutputStream os = socket.getOutputStream(); ++			String type = "os"; ++ ++			if (type == "os") { ++				os.write(str.getBytes()); ++				os.flush(); ++				System.out.println("used OutputStream"); ++			} else if (type == "bs") { ++				BufferedOutputStream bs = new BufferedOutputStream(os); ++				bs.write(str.getBytes()); ++				bs.flush(); ++				System.out.println("used BufferedOutputStream"); ++			} else if (type == "ds") { ++				DataOutputStream ds = new DataOutputStream(os); ++				ds.write(str.getBytes()); ++				ds.flush(); ++				System.out.println("used DataOutputStream"); ++			} ++			if (false) { ++				String rep = ""; ++				DataInputStream is = new DataInputStream( ++				    new BufferedInputStream(socket.getInputStream(), 16384)); ++				while (true) { ++					rep += readline(is); ++					if (rep.indexOf("\r\n\r\n") >= 0) { ++						break; ++					} ++				} ++				System.out.println("rep: " + rep); ++			} ++		} ++ ++		dbg("SSL returning socket to caller."); ++		dbg(""); ++ ++		/* could be null, let caller handle that. */ ++		return (Socket) socket; ++	} ++ ++	boolean browser_cert_match() { ++		String msg = "Browser URL accept previously accepted cert"; ++ ++		if (user_wants_to_see_cert) { ++			return false; ++		} ++ ++		if (viewer.serverCert != null || trustsrvCerts != null) { ++			if (cert_fail == null) { ++				cert_fail = "server-cert-mismatch"; ++			} ++		} ++		if (trustallCerts != null && trusturlCerts != null) { ++		    if (trustallCerts.length == trusturlCerts.length) { ++			boolean ok = true; ++			/* check toath trustallCerts (socket) equals trusturlCerts (browser) */ ++			for (int i = 0; i < trusturlCerts.length; i++)  { ++				if (! trustallCerts[i].equals(trusturlCerts[i])) { ++					dbg("BCM: cert mismatch at i=" + i); ++					dbg("BCM: cert mismatch  url" + trusturlCerts[i]); ++					dbg("BCM: cert mismatch  all" + trustallCerts[i]); ++					ok = false; ++				} ++			} ++			if (ok) { ++				System.out.println(msg); ++				if (cert_fail == null) { ++					cert_fail = "did-not-fail"; ++				} ++				return true; ++			} else { ++				if (cert_fail == null) { ++					cert_fail = "cert-mismatch"; ++				} ++				return false; ++			} ++		    } ++		} ++		if (cert_fail == null) { ++			cert_fail = "missing-certs"; ++		} ++		return false; ++	} ++ ++	private void dbg(String s) { ++		if (debug) { ++			System.out.println(s); ++		} ++	} ++ ++	private int gint(String s) { ++		int n = -1; ++		try { ++			Integer I = new Integer(s); ++			n = I.intValue(); ++		} catch (Exception ex) { ++			return -1; ++		} ++		return n; ++	} ++ ++	/* this will do the proxy CONNECT negotiation and hook us up.  */ ++ ++	private void proxy_helper(String proxyHost, int proxyPort) { ++ ++		boolean proxy_auth = false; ++		String proxy_auth_basic_realm = ""; ++		String hp = host + ":" + port; ++		dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp); ++ ++		/* we loop here a few times trying for the password case */ ++		for (int k=0; k < 2; k++) { ++			dbg("proxy_in_use psocket: " + k); ++ ++			if (proxySock != null) { ++				try { ++					proxySock.close(); ++				} catch (Exception e) { ++					dbg("proxy socket is grumpy."); ++				} ++			} ++ ++			proxySock = psocket(proxyHost, proxyPort); ++			if (proxySock == null) { ++				dbg("1-a sadly, returning a null socket"); ++				return; ++			} ++ ++			String req1 = "CONNECT " + hp + " HTTP/1.1\r\n" ++			    + "Host: " + hp + "\r\n"; ++ ++			dbg("requesting via proxy: " + req1); ++ ++			if (proxy_auth) { ++				if (proxy_auth_string == null) { ++					ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm); ++					pp.queryUser(); ++					proxy_auth_string = pp.getAuth(); ++				} ++				//dbg("auth1: " + proxy_auth_string); ++ ++				String auth2 = Base64Coder.encodeString(proxy_auth_string); ++				//dbg("auth2: " + auth2); ++ ++				req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n"; ++				//dbg("req1: " + req1); ++ ++				dbg("added Proxy-Authorization: Basic ... to request"); ++			} ++			req1 += "\r\n"; ++ ++			try { ++				proxy_os.write(req1.getBytes()); ++				String reply = readline(proxy_is); ++ ++				dbg("proxy replied: " + reply.trim()); ++ ++				if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) { ++					proxy_auth = true; ++					proxySock.close(); ++				} else if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) { ++					proxySock.close(); ++					proxySock = psocket(proxyHost, proxyPort); ++					if (proxySock == null) { ++						dbg("2-a sadly, returning a null socket"); ++						return; ++					} ++				} ++			} catch(Exception e) { ++				dbg("some proxy socket problem: " + e.getMessage()); ++			} ++ ++			/* read the rest of the HTTP headers */ ++			while (true) { ++				String line = readline(proxy_is); ++				dbg("proxy line: " + line.trim()); ++				if (proxy_auth) { ++					String uc = line.toLowerCase(); ++					if (uc.indexOf("proxy-authenticate:") == 0) { ++						if (uc.indexOf(" basic ") >= 0) { ++							int idx = uc.indexOf(" realm"); ++							if (idx >= 0) { ++								proxy_auth_basic_realm = uc.substring(idx+1); ++							} ++						} ++					} ++				} ++				if (line.equals("\r\n") || line.equals("\n")) { ++					break; ++				} ++			} ++			if (!proxy_auth || proxy_auth_basic_realm.equals("")) { ++				/* we only try once for the non-password case: */ ++				break; ++			} ++		} ++	} ++ ++	public SSLSocket proxy_socket(SSLSocketFactory factory) { ++		Properties props = null; ++		String proxyHost = null; ++		int proxyPort = 0; ++		String proxyHost_nossl = null; ++		int proxyPort_nossl = 0; ++		String str; ++ ++		/* see if we can guess the proxy info from Properties: */ ++		try { ++			props = System.getProperties(); ++		} catch (Exception e) { ++			/* sandboxed applet might not be able to read it. */ ++			dbg("props failed: " + e.getMessage()); ++		} ++		if (viewer.proxyHost != null) { ++			dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters."); ++			proxyHost = viewer.proxyHost; ++			if (viewer.proxyPort != null) { ++				proxyPort = gint(viewer.proxyPort); ++			} else { ++				proxyPort = 8080; ++			} ++			 ++		} else if (props != null) { ++			dbg("\n---------------\nAll props:"); ++			props.list(System.out); ++			dbg("\n---------------\n\n"); ++ ++			/* scrape throught properties looking for proxy info: */ ++ ++			for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) { ++				String s = (String) e.nextElement(); ++				String v = System.getProperty(s); ++				String s2 = s.toLowerCase(); ++				String v2 = v.toLowerCase(); ++ ++				if (s2.indexOf("proxy.https.host") >= 0) { ++					proxyHost = v2; ++					continue; ++				} ++				if (s2.indexOf("proxy.https.port") >= 0) { ++					proxyPort = gint(v2); ++					continue; ++				} ++				if (s2.indexOf("proxy.http.host") >= 0) { ++					proxyHost_nossl = v2; ++					continue; ++				} ++				if (s2.indexOf("proxy.http.port") >= 0) { ++					proxyPort_nossl = gint(v2); ++					continue; ++				} ++			} ++ ++			for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) { ++				String s = (String) e.nextElement(); ++				String v = System.getProperty(s); ++				String s2 = s.toLowerCase(); ++				String v2 = v.toLowerCase(); ++ ++				if (proxyHost != null && proxyPort > 0) { ++					break; ++				} ++ ++				// look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082 ++				if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) { ++					continue; ++				} ++				if (v2.indexOf("http") < 0) { ++					continue; ++				} ++ ++				String[] pieces = v.split("[,;]"); ++				for (int i = 0; i < pieces.length; i++) { ++					String p = pieces[i]; ++					int j = p.indexOf("https"); ++					if (j < 0) { ++						j = p.indexOf("http"); ++						if (j < 0) { ++							continue; ++						} ++					} ++					j = p.indexOf("=", j); ++					if (j < 0) { ++						continue; ++					} ++					p = p.substring(j+1); ++					String [] hp = p.split(":"); ++					if (hp.length != 2) { ++						continue; ++					} ++					if (hp[0].length() > 1 && hp[1].length() > 1) { ++ ++						proxyPort = gint(hp[1]); ++						if (proxyPort < 0) { ++							continue; ++						} ++						proxyHost = new String(hp[0]); ++						break; ++					} ++				} ++			} ++		} ++		if (proxyHost != null) { ++			if (proxyHost_nossl != null && proxyPort_nossl > 0) { ++				dbg("Using http proxy info instead of https."); ++				proxyHost = proxyHost_nossl; ++				proxyPort = proxyPort_nossl; ++			} ++		} ++ ++		if (proxy_in_use) { ++			if (proxy_dialog_host != null && proxy_dialog_port > 0) { ++				proxyHost = proxy_dialog_host; ++				proxyPort = proxy_dialog_port; ++			} ++			if (proxyHost != null) { ++				dbg("Lucky us! we figured out the Proxy parameters: " + proxyHost + " " + proxyPort); ++			} else { ++				/* ask user to help us: */ ++				ProxyDialog pd = new ProxyDialog(proxyHost, proxyPort); ++				pd.queryUser(); ++				proxyHost = pd.getHost();  ++				proxyPort = pd.getPort(); ++				proxy_dialog_host = new String(proxyHost); ++				proxy_dialog_port = proxyPort; ++				dbg("User said host: " + pd.getHost() + " port: " + pd.getPort()); ++			} ++ ++			proxy_helper(proxyHost, proxyPort); ++			if (proxySock == null) { ++				return null; ++			} ++		} else if (viewer.CONNECT != null) { ++			dbg("viewer.CONNECT psocket:"); ++			proxySock = psocket(host, port); ++			if (proxySock == null) { ++				dbg("1-b sadly, returning a null socket"); ++				return null; ++			} ++		} ++		 ++		if (viewer.CONNECT != null) { ++			String hp = viewer.CONNECT; ++			String req2 = "CONNECT " + hp + " HTTP/1.1\r\n" ++			    + "Host: " + hp + "\r\n\r\n"; ++ ++			dbg("requesting2: " + req2); ++ ++			try { ++				proxy_os.write(req2.getBytes()); ++				String reply = readline(proxy_is); ++ ++				dbg("proxy replied2: " + reply.trim()); ++ ++				if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) { ++					proxySock.close(); ++					proxySock = psocket(proxyHost, proxyPort); ++					if (proxySock == null) { ++						dbg("2-b sadly, returning a null socket"); ++						return null; ++					} ++				} ++			} catch(Exception e) { ++				dbg("proxy socket problem-2: " + e.getMessage()); ++			} ++ ++			while (true) { ++				String line = readline(proxy_is); ++				dbg("proxy line2: " + line.trim()); ++				if (line.equals("\r\n") || line.equals("\n")) { ++					break; ++				} ++			} ++		} ++ ++		Socket sslsock = null; ++		try { ++			sslsock = factory.createSocket(proxySock, host, port, true); ++		} catch(Exception e) { ++			dbg("sslsock prob: " + e.getMessage()); ++			dbg("3 sadly, returning a null socket"); ++		} ++ ++		return (SSLSocket) sslsock; ++	} ++ ++	Socket psocket(String h, int p) { ++		Socket psock = null; ++		try { ++			psock = new Socket(h, p); ++			proxy_is = new DataInputStream(new BufferedInputStream( ++			    psock.getInputStream(), 16384)); ++			proxy_os = psock.getOutputStream(); ++		} catch(Exception e) { ++			dbg("psocket prob: " + e.getMessage()); ++			return null; ++		} ++ ++		return psock; ++	} ++ ++	String readline(DataInputStream i) { ++		byte[] ba = new byte[1]; ++		String s = new String(""); ++		ba[0] = 0; ++		try { ++			while (ba[0] != 0xa) { ++				ba[0] = (byte) i.readUnsignedByte(); ++				s += new String(ba); ++			} ++		} catch (Exception e) { ++			; ++		} ++		return s; ++	} ++} ++ ++class TrustDialog implements ActionListener { ++	String msg, host, text; ++	int port; ++	java.security.cert.Certificate[] trustallCerts = null; ++	boolean viewing_cert = false; ++	boolean trust_this_session = false; ++ ++	/* ++	 * this is the gui to show the user the cert and info and ask ++	 * them if they want to continue using this cert. ++	 */ ++ ++	Button ok, cancel, viewcert; ++	TextArea textarea; ++	Checkbox accept, deny; ++	Dialog dialog; ++ ++	String s1 = "Accept this certificate temporarily for this session"; ++	String s2 = "Do not accept this certificate and do not connect to" ++	    + " this VNC server"; ++	String ln = "\n---------------------------------------------------\n\n"; ++		 ++	TrustDialog (String h, int p, java.security.cert.Certificate[] s) { ++		host = h; ++		port = p; ++		trustallCerts = s; ++ ++		msg = "VNC Server " + host + ":" + port + " Not Verified"; ++	} ++ ++	public boolean queryUser(String reason) { ++ ++		/* create and display the dialog for unverified cert. */ ++ ++		Frame frame = new Frame(msg); ++ ++		dialog = new Dialog(frame, true); ++ ++		String infostr = ""; ++		if (trustallCerts.length == 1) { ++			CertInfo ci = new CertInfo(trustallCerts[0]); ++			infostr = ci.get_certinfo("all"); ++		} ++		if (reason != null) { ++			reason += "\n\n"; ++		} ++ ++		text = "\n"  +++ "Unable to verify the identity of\n" +++ "\n" +++ "        " + host + ":" + port + "\n"  +++ "\n" +++ infostr +++ "\n" +++ "as a trusted VNC server.\n" +++ "\n" +++ reason +++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n" +++ "is due to one of the following:\n" +++ "\n" +++ " - Your requesting to View the Certificate before accepting.\n" +++ "\n" +++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n" +++ "   Authority not recognized by your Web Browser or Java Plugin runtime.\n" +++ "\n" +++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n" +++ "   the Apache Web server has a certificate *different* from the VNC server's.\n" +++ "\n" +++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n" +++ "   obtained by this applet to compare the VNC Server Certificate against.\n" +++ "\n" +++ " - The VNC Server's Certificate does not match the one specified in the\n" +++ "   supplied 'serverCert' Java Applet Parameter.\n" +++ "\n" +++ " - A Man-In-The-Middle attack impersonating as the VNC server that you wish\n" +++ "   to connect to.  (Wouldn't that be exciting!!)\n" +++ "\n" +++ "By safely copying the VNC server's Certificate (or using a common Certificate\n" +++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n" +++ "automatically authenticate this VNC Server.\n" +++ "\n" +++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n" +++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n" +++ "certificate (except for the Apache portal case above where they don't match.)\n" +++ "\n" +++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n" +++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n" +++ "and thereby see no dialog from this VNC Viewer applet.\n" ++; ++ ++		/* the accept / do-not-accept radio buttons: */ ++		CheckboxGroup checkbox = new CheckboxGroup(); ++		accept = new Checkbox(s1, true, checkbox); ++		deny   = new Checkbox(s2, false, checkbox); ++ ++		/* put the checkboxes in a panel: */ ++		Panel check = new Panel(); ++		check.setLayout(new GridLayout(2, 1)); ++ ++		check.add(accept); ++		check.add(deny); ++ ++		/* make the 3 buttons: */ ++		ok = new Button("OK"); ++		cancel = new Button("Cancel"); ++		viewcert = new Button("View Certificate"); ++ ++		ok.addActionListener(this); ++		cancel.addActionListener(this); ++		viewcert.addActionListener(this); ++ ++		/* put the buttons in their own panel: */ ++		Panel buttonrow = new Panel(); ++		buttonrow.setLayout(new FlowLayout(FlowLayout.LEFT)); ++		buttonrow.add(viewcert); ++		buttonrow.add(ok); ++		buttonrow.add(cancel); ++ ++		/* label at the top: */ ++		Label label = new Label(msg, Label.CENTER); ++		label.setFont(new Font("Helvetica", Font.BOLD, 16)); ++ ++		/* textarea in the middle */ ++		textarea = new TextArea(text, 38, 64, ++		    TextArea.SCROLLBARS_VERTICAL_ONLY); ++		textarea.setEditable(false); ++ ++		/* put the two panels in their own panel at bottom: */ ++		Panel bot = new Panel(); ++		bot.setLayout(new GridLayout(2, 1)); ++		bot.add(check); ++		bot.add(buttonrow); ++ ++		/* now arrange things inside the dialog: */ ++		dialog.setLayout(new BorderLayout()); ++ ++		dialog.add("North", label); ++		dialog.add("South", bot); ++		dialog.add("Center", textarea); ++ ++		dialog.pack(); ++		dialog.resize(dialog.preferredSize()); ++ ++		dialog.show();	/* block here til OK or Cancel pressed. */ ++ ++		return trust_this_session; ++	} ++ ++	public synchronized void actionPerformed(ActionEvent evt) { ++ ++		if (evt.getSource() == viewcert) { ++			/* View Certificate button clicked */ ++			if (viewing_cert) { ++				/* show the original info text: */ ++				textarea.setText(text); ++				viewcert.setLabel("View Certificate"); ++				viewing_cert = false; ++			} else { ++				int i; ++				/* show all (likely just one) certs: */ ++				textarea.setText(""); ++				for (i=0; i < trustallCerts.length; i++) { ++					int j = i + 1; ++					textarea.append("Certificate[" + ++					    j + "]\n\n"); ++					textarea.append( ++					    trustallCerts[i].toString()); ++					textarea.append(ln); ++				} ++				viewcert.setLabel("View Info"); ++				viewing_cert = true; ++ ++				textarea.setCaretPosition(0); ++			} ++ ++		} else if (evt.getSource() == ok) { ++			/* OK button clicked */ ++			if (accept.getState()) { ++				trust_this_session = true; ++			} else { ++				trust_this_session = false; ++			} ++			//dialog.dispose(); ++			dialog.hide(); ++ ++		} else if (evt.getSource() == cancel) { ++			/* Cancel button clicked */ ++			trust_this_session = false; ++ ++			//dialog.dispose(); ++			dialog.hide(); ++		} ++	} ++ ++	String get_certinfo() { ++		String all = ""; ++		String fields[] = {"CN", "OU", "O", "L", "C"}; ++		int i; ++		if (trustallCerts.length < 1) { ++			all = ""; ++			return all; ++		} ++		String cert = trustallCerts[0].toString(); ++ ++		/* ++		 * For now we simply scrape the cert string, there must ++		 * be an API for this... perhaps optionValue? ++		 */ ++ ++		for (i=0; i < fields.length; i++) { ++			int f, t, t1, t2; ++			String sub, mat = fields[i] + "="; ++			 ++			f = cert.indexOf(mat, 0); ++			if (f > 0) { ++				t1 = cert.indexOf(", ", f); ++				t2 = cert.indexOf("\n", f); ++				if (t1 < 0 && t2 < 0) { ++					continue; ++				} else if (t1 < 0) { ++					t = t2; ++				} else if (t2 < 0) { ++					t = t1; ++				} else if (t1 < t2) { ++					t = t1; ++				} else { ++					t = t2; ++				} ++				if (t > f) { ++					sub = cert.substring(f, t); ++					all = all + "        " + sub + "\n"; ++				} ++			} ++		} ++		return all; ++	} ++} ++ ++class ProxyDialog implements ActionListener { ++	String guessedHost = null; ++	String guessedPort = null; ++	/* ++	 * this is the gui to show the user the cert and info and ask ++	 * them if they want to continue using this cert. ++	 */ ++ ++	Button ok; ++	Dialog dialog; ++	TextField entry; ++	String reply = ""; ++ ++	ProxyDialog (String h, int p) { ++		guessedHost = h; ++		try { ++			guessedPort = Integer.toString(p); ++		} catch (Exception e) { ++			guessedPort = "8080"; ++		} ++	} ++ ++	public void queryUser() { ++ ++		/* create and display the dialog for unverified cert. */ ++ ++		Frame frame = new Frame("Need Proxy host:port"); ++ ++		dialog = new Dialog(frame, true); ++ ++ ++		Label label = new Label("Please Enter your https Proxy info as host:port", Label.CENTER); ++		//label.setFont(new Font("Helvetica", Font.BOLD, 16)); ++		entry = new TextField(30); ++		ok = new Button("OK"); ++		ok.addActionListener(this); ++ ++		String guess = ""; ++		if (guessedHost != null) { ++			guess = guessedHost + ":" + guessedPort; ++		} ++		entry.setText(guess); ++ ++		dialog.setLayout(new BorderLayout()); ++		dialog.add("North", label); ++		dialog.add("Center", entry); ++		dialog.add("South", ok); ++		dialog.pack(); ++		dialog.resize(dialog.preferredSize()); ++ ++		dialog.show();	/* block here til OK or Cancel pressed. */ ++		return; ++	} ++ ++	public String getHost() { ++		int i = reply.indexOf(":"); ++		if (i < 0) { ++			return "unknown"; ++		} ++		String h = reply.substring(0, i); ++		return h; ++	} ++ ++	public int getPort() { ++		int i = reply.indexOf(":"); ++		int p = 8080; ++		if (i < 0) { ++			return p; ++		} ++		i++; ++		String ps = reply.substring(i); ++		try { ++			Integer I = new Integer(ps); ++			p = I.intValue(); ++		} catch (Exception e) { ++			; ++		} ++		return p; ++	} ++ ++	public synchronized void actionPerformed(ActionEvent evt) { ++		System.out.println(evt.getActionCommand()); ++		if (evt.getSource() == ok) { ++			reply = entry.getText(); ++			//dialog.dispose(); ++			dialog.hide(); ++		} ++	} ++} ++ ++class ProxyPasswdDialog implements ActionListener { ++	String guessedHost = null; ++	String guessedPort = null; ++	String guessedUser = null; ++	String guessedPasswd = null; ++	String realm = null; ++	/* ++	 * this is the gui to show the user the cert and info and ask ++	 * them if they want to continue using this cert. ++	 */ ++ ++	Button ok; ++	Dialog dialog; ++	TextField entry1; ++	TextField entry2; ++	String reply1 = ""; ++	String reply2 = ""; ++ ++	ProxyPasswdDialog (String h, int p, String realm) { ++		guessedHost = h; ++		try { ++			guessedPort = Integer.toString(p); ++		} catch (Exception e) { ++			guessedPort = "8080"; ++		} ++		this.realm = realm; ++	} ++ ++	public void queryUser() { ++ ++		/* create and display the dialog for unverified cert. */ ++ ++		Frame frame = new Frame("Proxy Requires Username and Password"); ++ ++		dialog = new Dialog(frame, true); ++ ++		//Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER); ++		TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE); ++		entry1 = new TextField(30); ++		entry2 = new TextField(30); ++		entry2.setEchoChar('*'); ++		ok = new Button("OK"); ++		ok.addActionListener(this); ++ ++		dialog.setLayout(new BorderLayout()); ++		dialog.add("North", label); ++		dialog.add("Center", entry1); ++		dialog.add("South",  entry2); ++		dialog.add("East", ok); ++		dialog.pack(); ++		dialog.resize(dialog.preferredSize()); ++ ++		dialog.show();	/* block here til OK or Cancel pressed. */ ++		return; ++	} ++ ++	public String getAuth() { ++		return reply1 + ":" + reply2; ++	} ++ ++	public synchronized void actionPerformed(ActionEvent evt) { ++		System.out.println(evt.getActionCommand()); ++		if (evt.getSource() == ok) { ++			reply1 = entry1.getText(); ++			reply2 = entry2.getText(); ++			//dialog.dispose(); ++			dialog.hide(); ++		} ++	} ++} ++ ++class ClientCertDialog implements ActionListener { ++ ++	Button ok; ++	Dialog dialog; ++	TextField entry; ++	String reply = ""; ++ ++	ClientCertDialog() { ++		; ++	} ++ ++	public String queryUser() { ++ ++		/* create and display the dialog for unverified cert. */ ++ ++		Frame frame = new Frame("Enter SSL Client Cert+Key String"); ++ ++		dialog = new Dialog(frame, true); ++ ++ ++		Label label = new Label("Please Enter the SSL Client Cert+Key String 308204c0...,...522d2d0a", Label.CENTER); ++		entry = new TextField(30); ++		ok = new Button("OK"); ++		ok.addActionListener(this); ++ ++		dialog.setLayout(new BorderLayout()); ++		dialog.add("North", label); ++		dialog.add("Center", entry); ++		dialog.add("South", ok); ++		dialog.pack(); ++		dialog.resize(dialog.preferredSize()); ++ ++		dialog.show();	/* block here til OK or Cancel pressed. */ ++		return reply; ++	} ++ ++	public synchronized void actionPerformed(ActionEvent evt) { ++		System.out.println(evt.getActionCommand()); ++		if (evt.getSource() == ok) { ++			reply = entry.getText(); ++			//dialog.dispose(); ++			dialog.hide(); ++		} ++	} ++} ++ ++class BrowserCertsDialog implements ActionListener { ++	Button yes, no; ++	Dialog dialog; ++	String vncServer; ++	String hostport; ++	public boolean showCertDialog = true; ++ ++	BrowserCertsDialog(String serv, String hp) { ++		vncServer = serv; ++		hostport = hp; ++	} ++ ++	public void queryUser() { ++ ++		/* create and display the dialog for unverified cert. */ ++ ++		Frame frame = new Frame("Use Browser/JVM Certs?"); ++ ++		dialog = new Dialog(frame, true); ++ ++		String m = ""; ++m += "\n"; ++m += "This VNC Viewer applet does not have its own keystore to track\n"; ++m += "SSL certificates, and so cannot authenticate the certificate\n"; ++m += "of the VNC Server:\n"; ++m += "\n"; ++m += "        " + hostport + "\n\n        " + vncServer + "\n"; ++m += "\n"; ++m += "on its own.\n"; ++m += "\n"; ++m += "However, it has noticed that your Web Browser and/or Java VM Plugin\n"; ++m += "has previously accepted the same certificate.  You may have set\n"; ++m += "this up permanently or just for this session, or the server\n"; ++m += "certificate was signed by a CA cert that your Web Browser or\n"; ++m += "Java VM Plugin has.\n"; ++m += "\n"; ++m += "If the VNC Server connection times out while you are reading this\n"; ++m += "dialog, then restart the connection and try again.\n"; ++m += "\n"; ++m += "Should this VNC Viewer applet now connect to the above VNC server?\n"; ++m += "\n"; ++ ++		TextArea textarea = new TextArea(m, 22, 64, ++		    TextArea.SCROLLBARS_VERTICAL_ONLY); ++		textarea.setEditable(false); ++		yes = new Button("Yes"); ++		yes.addActionListener(this); ++		no = new Button("No, Let Me See the Certificate."); ++		no.addActionListener(this); ++ ++		dialog.setLayout(new BorderLayout()); ++		dialog.add("North", textarea); ++		dialog.add("Center", yes); ++		dialog.add("South", no); ++		dialog.pack(); ++		dialog.resize(dialog.preferredSize()); ++ ++		dialog.show();	/* block here til Yes or No pressed. */ ++		System.out.println("done show()"); ++		return; ++	} ++ ++	public synchronized void actionPerformed(ActionEvent evt) { ++		System.out.println(evt.getActionCommand()); ++		if (evt.getSource() == yes) { ++			showCertDialog = false; ++			//dialog.dispose(); ++			dialog.hide(); ++		} else if (evt.getSource() == no) { ++			showCertDialog = true; ++			//dialog.dispose(); ++			dialog.hide(); ++		} ++		System.out.println("done actionPerformed()"); ++	} ++} ++ ++class CertInfo { ++	String fields[] = {"CN", "OU", "O", "L", "C"}; ++	java.security.cert.Certificate cert; ++	String certString = ""; ++ ++	CertInfo(java.security.cert.Certificate c) { ++		cert = c; ++		certString = cert.toString(); ++	} ++	 ++	String get_certinfo(String which) { ++		int i; ++		String cs = new String(certString); ++		String all = ""; ++ ++		/* ++		 * For now we simply scrape the cert string, there must ++		 * be an API for this... perhaps optionValue? ++		 */ ++		for (i=0; i < fields.length; i++) { ++			int f, t, t1, t2; ++			String sub, mat = fields[i] + "="; ++			 ++			f = cs.indexOf(mat, 0); ++			if (f > 0) { ++				t1 = cs.indexOf(", ", f); ++				t2 = cs.indexOf("\n", f); ++				if (t1 < 0 && t2 < 0) { ++					continue; ++				} else if (t1 < 0) { ++					t = t2; ++				} else if (t2 < 0) { ++					t = t1; ++				} else if (t1 < t2) { ++					t = t1; ++				} else { ++					t = t2; ++				} ++				if (t > f) { ++					sub = cs.substring(f, t); ++					all = all + "        " + sub + "\n"; ++					if (which.equals(fields[i])) { ++						return sub; ++					} ++				} ++			} ++		} ++		if (which.equals("all")) { ++			return all; ++		} else { ++			return ""; ++		} ++	} ++} ++ ++class Base64Coder { ++ ++	// Mapping table from 6-bit nibbles to Base64 characters. ++	private static char[]    map1 = new char[64]; ++	   static { ++	      int i=0; ++	      for (char c='A'; c<='Z'; c++) map1[i++] = c; ++	      for (char c='a'; c<='z'; c++) map1[i++] = c; ++	      for (char c='0'; c<='9'; c++) map1[i++] = c; ++	      map1[i++] = '+'; map1[i++] = '/'; } ++ ++	// Mapping table from Base64 characters to 6-bit nibbles. ++	private static byte[]    map2 = new byte[128]; ++	   static { ++	      for (int i=0; i<map2.length; i++) map2[i] = -1; ++	      for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; } ++ ++	/** ++	* Encodes a string into Base64 format. ++	* No blanks or line breaks are inserted. ++	* @param s  a String to be encoded. ++	* @return   A String with the Base64 encoded data. ++	*/ ++	public static String encodeString (String s) { ++	   return new String(encode(s.getBytes())); } ++ ++	/** ++	* Encodes a byte array into Base64 format. ++	* No blanks or line breaks are inserted. ++	* @param in  an array containing the data bytes to be encoded. ++	* @return    A character array with the Base64 encoded data. ++	*/ ++	public static char[] encode (byte[] in) { ++	   return encode(in,in.length); } ++ ++	/** ++	* Encodes a byte array into Base64 format. ++	* No blanks or line breaks are inserted. ++	* @param in   an array containing the data bytes to be encoded. ++	* @param iLen number of bytes to process in <code>in</code>. ++	* @return     A character array with the Base64 encoded data. ++	*/ ++	public static char[] encode (byte[] in, int iLen) { ++	   int oDataLen = (iLen*4+2)/3;       // output length without padding ++	   int oLen = ((iLen+2)/3)*4;         // output length including padding ++	   char[] out = new char[oLen]; ++	   int ip = 0; ++	   int op = 0; ++	   while (ip < iLen) { ++	      int i0 = in[ip++] & 0xff; ++	      int i1 = ip < iLen ? in[ip++] & 0xff : 0; ++	      int i2 = ip < iLen ? in[ip++] & 0xff : 0; ++	      int o0 = i0 >>> 2; ++	      int o1 = ((i0 &   3) << 4) | (i1 >>> 4); ++	      int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); ++	      int o3 = i2 & 0x3F; ++	      out[op++] = map1[o0]; ++	      out[op++] = map1[o1]; ++	      out[op] = op < oDataLen ? map1[o2] : '='; op++; ++	      out[op] = op < oDataLen ? map1[o3] : '='; op++; } ++	   return out; } ++ ++	/** ++	* Decodes a string from Base64 format. ++	* @param s  a Base64 String to be decoded. ++	* @return   A String containing the decoded data. ++	* @throws   IllegalArgumentException if the input is not valid Base64 encoded data. ++	*/ ++	public static String decodeString (String s) { ++	   return new String(decode(s)); } ++ ++	/** ++	* Decodes a byte array from Base64 format. ++	* @param s  a Base64 String to be decoded. ++	* @return   An array containing the decoded data bytes. ++	* @throws   IllegalArgumentException if the input is not valid Base64 encoded data. ++	*/ ++	public static byte[] decode (String s) { ++	   return decode(s.toCharArray()); } ++ ++	/** ++	* Decodes a byte array from Base64 format. ++	* No blanks or line breaks are allowed within the Base64 encoded data. ++	* @param in  a character array containing the Base64 encoded data. ++	* @return    An array containing the decoded data bytes. ++	* @throws    IllegalArgumentException if the input is not valid Base64 encoded data. ++	*/ ++	public static byte[] decode (char[] in) { ++	   int iLen = in.length; ++	   if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); ++	   while (iLen > 0 && in[iLen-1] == '=') iLen--; ++	   int oLen = (iLen*3) / 4; ++	   byte[] out = new byte[oLen]; ++	   int ip = 0; ++	   int op = 0; ++	   while (ip < iLen) { ++	      int i0 = in[ip++]; ++	      int i1 = in[ip++]; ++	      int i2 = ip < iLen ? in[ip++] : 'A'; ++	      int i3 = ip < iLen ? in[ip++] : 'A'; ++	      if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) ++		 throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); ++	      int b0 = map2[i0]; ++	      int b1 = map2[i1]; ++	      int b2 = map2[i2]; ++	      int b3 = map2[i3]; ++	      if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) ++		 throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); ++	      int o0 = ( b0       <<2) | (b1>>>4); ++	      int o1 = ((b1 & 0xf)<<4) | (b2>>>2); ++	      int o2 = ((b2 &   3)<<6) |  b3; ++	      out[op++] = (byte)o0; ++	      if (op<oLen) out[op++] = (byte)o1; ++	      if (op<oLen) out[op++] = (byte)o2; } ++	   return out; } ++ ++	// Dummy constructor. ++	private Base64Coder() {} ++ ++} +diff -Naur JavaViewer.orig/VncCanvas.java JavaViewer/VncCanvas.java +--- JavaViewer.orig/VncCanvas.java	2005-11-21 18:50:18.000000000 -0500 ++++ JavaViewer/VncCanvas.java	2010-11-30 22:57:50.000000000 -0500 +@@ -27,6 +27,13 @@ + import java.lang.*; + import java.util.zip.*; +  ++// begin runge/x11vnc ++import java.util.Collections; ++// end runge/x11vnc ++ ++// begin runge/x11vnc ++// all the MouseWheel stuff below. ++// end runge/x11vnc +  + // + // VncCanvas is a subclass of Canvas which draws a VNC desktop on it. +@@ -34,7 +41,7 @@ +  + class VncCanvas + 	extends Canvas +-	implements KeyListener, MouseListener, MouseMotionListener { ++	implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener { +  + 	VncViewer viewer; + 	RfbProto rfb; +@@ -85,6 +92,22 @@ + 		 + 		cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); +  ++// begin runge/x11vnc ++// kludge to not show any Java cursor in the canvas since we are ++// showing the soft cursor (should be a user setting...) ++Cursor dot = Toolkit.getDefaultToolkit().createCustomCursor( ++    Toolkit.getDefaultToolkit().createImage(new byte[4]), new Point(0,0), ++    "dot"); ++this.setCursor(dot); ++ ++// while we are at it... get rid of the keyboard traversals that ++// make it so we can't type a Tab character: ++this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, ++    Collections.EMPTY_SET); ++this.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, ++    Collections.EMPTY_SET); ++// end runge/x11vnc ++ + 		colors = new Color[256]; + 		// sf@2005 - Now Default + 		for (int i = 0; i < 256; i++) +@@ -186,6 +209,7 @@ + 			inputEnabled = true; + 			addMouseListener(this); + 			addMouseMotionListener(this); ++			addMouseWheelListener(this); + 			if (viewer.showControls) { + 				viewer.buttonPanel.enableRemoteAccessControls(true); + 			} +@@ -193,6 +217,7 @@ + 			inputEnabled = false; + 			removeMouseListener(this); + 			removeMouseMotionListener(this); ++			removeMouseWheelListener(this); + 			if (viewer.showControls) { + 				viewer.buttonPanel.enableRemoteAccessControls(false); + 			} +@@ -202,6 +227,9 @@ + 	 + 	public void setPixelFormat() throws IOException { + 		// sf@2005 - Adding more color modes ++		if (viewer.graftFtp) { ++			return; ++		} + 		if (viewer.options.eightBitColors > 0) + 		{ + 			viewer.options.oldEightBitColors = viewer.options.eightBitColors; +@@ -237,6 +265,9 @@ + 		} + 		else  + 		{ ++// begin runge/x11vnc ++			viewer.options.oldEightBitColors = viewer.options.eightBitColors; ++// end runge/x11vnc + 			rfb.writeSetPixelFormat( + 				32, + 				24, +@@ -376,12 +407,14 @@ + 		// Start/stop session recording if necessary. + 		viewer.checkRecordingStatus(); +  +-		rfb.writeFramebufferUpdateRequest( +-			0, +-			0, +-			rfb.framebufferWidth, +-			rfb.framebufferHeight, +-			false); ++		if (!viewer.graftFtp) { ++			rfb.writeFramebufferUpdateRequest( ++				0, ++				0, ++				rfb.framebufferWidth, ++				rfb.framebufferHeight, ++				false); ++		} +  + 		// + 		// main dispatch loop +@@ -390,6 +423,9 @@ + 		while (true) { + 			// Read message type from the server. + 			int msgType = rfb.readServerMessageType(); ++			if (viewer.ftpOnly && msgType != RfbProto.rfbFileTransfer) { ++				System.out.println("msgType:" + msgType); ++			} +  + 			// Process the message depending on its type. + 			switch (msgType) { +@@ -1332,6 +1368,9 @@ + 	public void mouseDragged(MouseEvent evt) { + 		processLocalMouseEvent(evt, true); + 	} ++	public void mouseWheelMoved(MouseWheelEvent evt) { ++		processLocalMouseWheelEvent(evt); ++	} +  + 	public void processLocalKeyEvent(KeyEvent evt) { + 		if (viewer.rfb != null && rfb.inNormalProtocol) { +@@ -1367,6 +1406,19 @@ + 		evt.consume(); + 	} +  ++	public void processLocalMouseWheelEvent(MouseWheelEvent evt) { ++		if (viewer.rfb != null && rfb.inNormalProtocol) { ++			synchronized(rfb) { ++			try { ++				rfb.writeWheelEvent(evt); ++			} catch (Exception e) { ++				e.printStackTrace(); ++			} ++				rfb.notify(); ++			} ++		} ++	} ++ + 	public void processLocalMouseEvent(MouseEvent evt, boolean moved) { + 		if (viewer.rfb != null && rfb.inNormalProtocol) { + 			if (moved) { +@@ -1532,9 +1584,14 @@ + 							else + 							{ + 								result = +-									0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF) +-										<< 16 | (pixBuf[i * 4 + 2] & 0xFF) +-										<< 8 | (pixBuf[i * 4 + 3] & 0xFF); ++// begin runge/x11vnc ++//									0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF) ++//										<< 16 | (pixBuf[i * 4 + 2] & 0xFF) ++//										<< 8 | (pixBuf[i * 4 + 3] & 0xFF); ++									0xFF000000 | (pixBuf[i * 4 + 2] & 0xFF) ++										<< 16 | (pixBuf[i * 4 + 1] & 0xFF) ++										<< 8 | (pixBuf[i * 4 + 0] & 0xFF); ++// end runge/x11vnc + 							} + 						} else { + 							result = 0; // Transparent pixel +@@ -1565,9 +1622,14 @@ + 						else + 						{ + 							result = +-								0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF) +-									<< 16 | (pixBuf[i * 4 + 2] & 0xFF) +-									<< 8 | (pixBuf[i * 4 + 3] & 0xFF); ++// begin runge/x11vnc ++//								0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF) ++//									<< 16 | (pixBuf[i * 4 + 2] & 0xFF) ++//									<< 8 | (pixBuf[i * 4 + 3] & 0xFF); ++								0xFF000000 | (pixBuf[i * 4 + 2] & 0xFF) ++									<< 16 | (pixBuf[i * 4 + 1] & 0xFF) ++									<< 8 | (pixBuf[i * 4 + 0] & 0xFF); ++// end runge/x11vnc + 						} + 					} else { + 						result = 0; // Transparent pixel +diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java +--- JavaViewer.orig/VncViewer.java	2006-05-24 15:14:40.000000000 -0400 ++++ JavaViewer/VncViewer.java	2010-03-27 18:00:28.000000000 -0400 +@@ -41,6 +41,7 @@ + import java.io.*; + import java.net.*; + import javax.swing.*; ++import java.util.Date; +  + public class VncViewer extends java.applet.Applet +   implements java.lang.Runnable, WindowListener { +@@ -80,11 +81,11 @@ +   GridBagLayout gridbag; +   ButtonPanel buttonPanel; +   AuthPanel authenticator; +-  VncCanvas vc; ++  VncCanvas vc = null; +   OptionsFrame options; +   ClipboardFrame clipboard; +   RecordingFrame rec; +-  FTPFrame ftp; // KMC: FTP Frame declaration ++  FTPFrame ftp = null; // KMC: FTP Frame declaration +  +   // Control session recording. +   Object recordingSync; +@@ -96,7 +97,7 @@ +  +   // Variables read from parameter values. +   String host; +-  int port; ++  int port, vncserverport; +   String passwordParam; +   String encPasswordParam; +   boolean showControls; +@@ -115,28 +116,75 @@ +   int i; +   // mslogon support 2 end +  ++// begin runge/x11vnc ++boolean disableSSL; ++boolean GET; ++String CONNECT; ++String urlPrefix; ++String httpsPort; ++String oneTimeKey; ++String serverCert; ++String ftpDropDown; ++String proxyHost; ++String proxyPort; ++boolean forceProxy; ++boolean ignoreProxy; ++boolean trustAllVncCerts; ++boolean trustUrlVncCert; ++boolean debugCerts; ++boolean debugKeyboard; ++boolean mapF5_to_atsign; ++boolean forbid_Ctrl_Alt; ++ ++boolean ignoreMSLogonCheck; ++boolean delayAuthPanel; ++boolean ftpOnly; ++boolean graftFtp; ++boolean dsmActive; ++ ++boolean gotAuth; ++int authGot; ++// end runge/x11vnc ++ ++ +   // +   // init() +   // +  ++public void ftp_init() { ++	boolean show = false; ++	if (ftp != null) { ++		show = true; ++	} ++	ftp = null; ++ ++	ftp = new FTPFrame(this); // KMC: FTPFrame creation ++ ++	if (show) { ++		ftp.doOpen(); ++		rfb.readServerDriveList(); ++	} ++} ++ +   public void init() { +  +     readParameters(); +  +     if (inSeparateFrame) { +-      vncFrame = new Frame("Ultr@VNC"); +-      if (!inAnApplet) { +-	vncFrame.add("Center", this); +-      } +-      vncContainer = vncFrame; ++	vncFrame = new Frame("Ultr@VNC"); ++	if (!inAnApplet) { ++		vncFrame.add("Center", this); ++	} ++	vncContainer = vncFrame; +     } else { +-      vncContainer = this; ++	vncContainer = this; +     } +  +     recordingSync = new Object(); +  +     options = new OptionsFrame(this); +     clipboard = new ClipboardFrame(this); ++ +     // authenticator = new AuthPanel(false);   // mslogon support : go to connectAndAuthenticate() +     if (RecordingFrame.checkSecurity()) +       rec = new RecordingFrame(this); +@@ -147,10 +195,11 @@ +     cursorUpdatesDef = null; +     eightBitColorsDef = null; +  +-    if (inSeparateFrame) ++    if (inSeparateFrame && vncFrame != null) +       vncFrame.addWindowListener(this); +  +-    ftp = new FTPFrame(this); // KMC: FTPFrame creation ++    ftp_init(); ++ +     rfbThread = new Thread(this); +     rfbThread.start(); +   } +@@ -186,6 +235,30 @@ +       gbc.weightx = 1.0; +       gbc.weighty = 1.0; +  ++	if (ftpOnly) { ++		if (showControls) { ++			buttonPanel.enableButtons(); ++		} ++		ActionListener taskPerformer = new ActionListener() { ++			public void actionPerformed(ActionEvent evt) { ++				vncFrame.setVisible(false); ++				ftp.setSavedLocations(); ++				if (ftp.isVisible()) { ++					ftp.doClose(); ++				} else { ++					ftp.doOpen(); ++				} ++				rfb.readServerDriveList(); ++			} ++		}; ++		Timer t = new Timer(300, taskPerformer); ++		t.setRepeats(false); ++		t.start(); ++		 ++		vc.processNormalProtocol(); ++		return; ++	} ++ + 	// Add ScrollPanel to applet mode +  + 	// Create a panel which itself is resizeable and can hold +@@ -286,6 +359,24 @@ +  +   void connectAndAuthenticate() throws Exception { +  ++	if (graftFtp) { ++		rfb = new RfbProto(host, port, this); ++		rfb.desktopName = "ftponly"; ++		rfb.framebufferWidth  = 12; ++		rfb.framebufferHeight = 12; ++		rfb.bitsPerPixel = 32; ++		rfb.depth = 24; ++		rfb.trueColour = true; ++		rfb.redMax   = 255; ++		rfb.greenMax = 255; ++		rfb.blueMax  = 255; ++		rfb.redShift   = 16; ++		rfb.greenShift = 8; ++		rfb.blueShift  = 0; ++		rfb.inNormalProtocol = true; ++		return; ++	} ++ +     // If "ENCPASSWORD" parameter is set, decrypt the password into +     // the passwordParam string. +  +@@ -336,7 +427,22 @@ +     // +  +      +-    prologueDetectAuthProtocol() ; ++// begin runge/x11vnc ++	gotAuth = false; ++	if (delayAuthPanel) { ++      		if (tryAuthenticate(null, null)) { ++			if (inSeparateFrame) { ++				vncFrame.pack(); ++				vncFrame.show(); ++			} ++      			return; ++      		} ++	} ++//  prologueDetectAuthProtocol() ; ++    if (ignoreMSLogonCheck == false) { ++        prologueDetectAuthProtocol() ; ++    } ++// end runge/x11vnc +  +     authenticator = new AuthPanel(mslogon); +      +@@ -371,6 +477,7 @@ +      //mslogon support end +     } +  ++    int tries = 0; +     while (true) { +       // Wait for user entering a password, or a username and a password +       synchronized(authenticator) { +@@ -390,6 +497,13 @@ + 	break; +       //mslogon support end +  ++// begin runge/x11vnc ++      gotAuth = false; ++      if (++tries > 2) { ++         throw new Exception("Incorrect password entered " + tries + " times."); ++      } ++// end runge/x11vnc ++ +       // Retry on authentication failure. +       authenticator.retry(); +     } +@@ -405,9 +519,11 @@ +  +   void prologueDetectAuthProtocol() throws Exception { +  +-    rfb = new RfbProto(host, port, this); ++	if (!gotAuth) { ++		rfb = new RfbProto(host, port, this); +  +-    rfb.readVersionMsg(); ++		rfb.readVersionMsg(); ++	} +  +     System.out.println("RFB server supports protocol version " + + 		       rfb.serverMajor + "." + rfb.serverMinor); +@@ -431,16 +547,36 @@ +  +   boolean tryAuthenticate(String us, String pw) throws Exception { +      +-    rfb = new RfbProto(host, port, this); ++	int authScheme; +  +-    rfb.readVersionMsg(); ++	if (!gotAuth) { ++		rfb = new RfbProto(host, port, this); +  +-    System.out.println("RFB server supports protocol version " + +-		       rfb.serverMajor + "." + rfb.serverMinor); ++		rfb.readVersionMsg(); +  +-    rfb.writeVersionMsg(); ++		System.out.println("RFB server supports protocol version: " + ++			       rfb.serverMajor + "." + rfb.serverMinor); +  +-    int authScheme = rfb.readAuthScheme(); ++		rfb.writeVersionMsg(); ++ ++		authScheme = rfb.readAuthScheme(); ++ ++		gotAuth = true; ++		authGot = authScheme; ++	} else { ++		authScheme = authGot; ++	} ++// begin runge/x11vnc ++	if (delayAuthPanel && pw == null) { ++		if (authScheme == RfbProto.NoAuth) { ++			System.out.println("No authentication needed"); ++			return true; ++		} else { ++			return false; ++		} ++	} ++System.out.println("as: " + authScheme); ++// end runge/x11vnc +  +     switch (authScheme) { +  +@@ -629,6 +765,10 @@ +  +   void doProtocolInitialisation() throws IOException { +  ++	if (graftFtp) { ++		return; ++	} ++ +     rfb.writeClientInit(); +  +     rfb.readServerInit(); +@@ -774,9 +914,28 @@ + 	fatalError("HOST parameter not specified"); +       } +     } ++    Date d = new Date(); ++    System.out.println("-\nSSL VNC Java Applet starting.  " + d); +  +-    String str = readParameter("PORT", true); +-    port = Integer.parseInt(str); ++    port = 0; ++    String str = readParameter("PORT", false); ++    if (str != null) { ++	port = Integer.parseInt(str); ++    } ++    // When there is a proxy VNCSERVERPORT may be inaccessible (inside firewall). ++    vncserverport = 0; ++    str = readParameter("VNCSERVERPORT", false); ++    if (str != null) { ++	vncserverport = Integer.parseInt(str); ++    } ++    if (port == 0 && vncserverport == 0) { ++	fatalError("Neither PORT nor VNCSERVERPORT parameters specified"); ++    } ++    if (port == 0) { ++	// Nevertheless, fall back to vncserverport if we have to. ++	System.out.println("using vncserverport: '" + vncserverport + "' for PORT."); ++	port = vncserverport; ++    } +  +     if (inAnApplet) { +       str = readParameter("Open New Window", false); +@@ -804,6 +963,158 @@ +     deferScreenUpdates = readIntParameter("Defer screen updates", 20); +     deferCursorUpdates = readIntParameter("Defer cursor updates", 10); +     deferUpdateRequests = readIntParameter("Defer update requests", 50); ++ ++// begin runge/x11vnc ++    // SSL ++    disableSSL = false; ++    str = readParameter("DisableSSL", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) ++      disableSSL = true; ++ ++    httpsPort = readParameter("httpsPort", false); ++ ++    // Extra GET, CONNECT string: ++    CONNECT = readParameter("CONNECT", false); ++    if (CONNECT != null) { ++	CONNECT = CONNECT.replaceAll(" ", ":"); ++    } ++ ++    GET = false; ++    str = readParameter("GET", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) { ++      GET = true; ++    } ++    if (str != null && str.equalsIgnoreCase("1")) { ++      GET = true; ++    } ++ ++    urlPrefix = readParameter("urlPrefix", false); ++    if (urlPrefix != null) { ++	urlPrefix = urlPrefix.replaceAll("%2F", "/"); ++	urlPrefix = urlPrefix.replaceAll("%2f", "/"); ++	urlPrefix = urlPrefix.replaceAll("_2F_", "/"); ++	if (urlPrefix.indexOf("/") != 0) { ++		urlPrefix = "/" + urlPrefix; ++	} ++    } else { ++    	urlPrefix = ""; ++    } ++    System.out.println("urlPrefix: '" + urlPrefix + "'"); ++ ++    ftpDropDown = readParameter("ftpDropDown", false); ++    if (ftpDropDown != null) { ++	ftpDropDown = ftpDropDown.replaceAll("%2F", "/"); ++	ftpDropDown = ftpDropDown.replaceAll("%2f", "/"); ++	ftpDropDown = ftpDropDown.replaceAll("_2F_", "/"); ++	ftpDropDown = ftpDropDown.replaceAll("%20", " "); ++	System.out.println("ftpDropDown: '" + ftpDropDown + "'"); ++    } ++ ++ ++    oneTimeKey = readParameter("oneTimeKey", false); ++    if (oneTimeKey != null) { ++    	System.out.println("oneTimeKey is set."); ++    } ++ ++    serverCert = readParameter("serverCert", false); ++    if (serverCert != null) { ++    	System.out.println("serverCert is set."); ++    } ++ ++    forceProxy = false; ++    proxyHost = null; ++    proxyPort = null; ++    str = readParameter("forceProxy", false); ++    if (str != null) { ++	    if (str.equalsIgnoreCase("Yes")) { ++		forceProxy = true; ++	    } else if (str.equalsIgnoreCase("No")) { ++		forceProxy = false; ++	    } else { ++		forceProxy = true; ++		String[] pieces = str.split(" "); ++		proxyHost = new String(pieces[0]); ++		if (pieces.length >= 2) { ++			proxyPort = new String(pieces[1]); ++		} else { ++			proxyPort = new String("8080"); ++		} ++	    } ++    } ++    str = readParameter("proxyHost", false); ++    if (str != null) { ++	proxyHost = new String(str); ++    } ++    str = readParameter("proxyPort", false); ++    if (str != null) { ++	proxyPort = new String(str); ++    } ++    if (proxyHost != null && proxyPort == null) { ++    	proxyPort = new String("8080"); ++    } ++ ++    ignoreProxy = false; ++    str = readParameter("ignoreProxy", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) { ++	ignoreProxy = true; ++    } ++ ++    trustAllVncCerts = false; ++    str = readParameter("trustAllVncCerts", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) { ++	trustAllVncCerts = true; ++    } ++    trustUrlVncCert = false; ++    str = readParameter("trustUrlVncCert", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) { ++	trustUrlVncCert = true; ++    } ++    debugCerts = false; ++    str = readParameter("debugCerts", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) { ++	debugCerts = true; ++    } ++    debugKeyboard = false; ++    str = readParameter("debugKeyboard", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) { ++	debugKeyboard = true; ++    } ++    mapF5_to_atsign = false; ++    str = readParameter("mapF5_to_atsign", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) { ++	mapF5_to_atsign = true; ++    } ++    forbid_Ctrl_Alt = false; ++    str = readParameter("forbid_Ctrl_Alt", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) { ++	forbid_Ctrl_Alt = true; ++    } ++    ignoreMSLogonCheck = false; ++    str = readParameter("ignoreMSLogonCheck", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) { ++	ignoreMSLogonCheck = true; ++    } ++    ftpOnly = false; ++    str = readParameter("ftpOnly", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) { ++	ftpOnly = true; ++    } ++    graftFtp = false; ++    str = readParameter("graftFtp", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) { ++	graftFtp = true; ++    } ++    dsmActive = false; ++    str = readParameter("dsmActive", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) { ++	dsmActive = true; ++    } ++    delayAuthPanel = false; ++    str = readParameter("delayAuthPanel", false); ++    if (str != null && str.equalsIgnoreCase("Yes")) { ++	delayAuthPanel = true; ++    } ++// end runge/x11vnc +   } +  +   public String readParameter(String name, boolean required) { | 
