1diff -Naur JavaViewer.orig/ButtonPanel.java JavaViewer/ButtonPanel.java
2--- JavaViewer.orig/ButtonPanel.java	2004-12-12 20:51:02.000000000 -0500
3+++ JavaViewer/ButtonPanel.java	2007-05-31 15:40:45.000000000 -0400
4@@ -43,30 +43,36 @@
5     viewer = v;
6
7     setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
8-    disconnectButton = new Button("Disconnect");
9+	if (v.ftpOnly) {
10+		disconnectButton = new Button("Quit");
11+	} else {
12+		disconnectButton = new Button("Close");
13+	}
14     disconnectButton.setEnabled(false);
15     add(disconnectButton);
16     disconnectButton.addActionListener(this);
17-    optionsButton = new Button("Options");
18-    add(optionsButton);
19-    optionsButton.addActionListener(this);
20-    clipboardButton = new Button("Clipboard");
21-    clipboardButton.setEnabled(false);
22-    add(clipboardButton);
23-    clipboardButton.addActionListener(this);
24-    if (viewer.rec != null) {
25-      recordButton = new Button("Record");
26-      add(recordButton);
27-      recordButton.addActionListener(this);
28-    }
29-    ctrlAltDelButton = new Button("Send Ctrl-Alt-Del");
30-    ctrlAltDelButton.setEnabled(false);
31-    add(ctrlAltDelButton);
32-    ctrlAltDelButton.addActionListener(this);
33-    refreshButton = new Button("Refresh");
34-    refreshButton.setEnabled(false);
35-    add(refreshButton);
36-    refreshButton.addActionListener(this);
37+	if (!v.ftpOnly) {
38+		optionsButton = new Button("Options");
39+		add(optionsButton);
40+		optionsButton.addActionListener(this);
41+		clipboardButton = new Button("Clipboard");
42+		clipboardButton.setEnabled(false);
43+		add(clipboardButton);
44+		clipboardButton.addActionListener(this);
45+		if (viewer.rec != null) {
46+			recordButton = new Button("Record");
47+			add(recordButton);
48+			recordButton.addActionListener(this);
49+		}
50+		ctrlAltDelButton = new Button("Send Ctrl-Alt-Del");
51+		ctrlAltDelButton.setEnabled(false);
52+		add(ctrlAltDelButton);
53+		ctrlAltDelButton.addActionListener(this);
54+		refreshButton = new Button("Refresh");
55+		refreshButton.setEnabled(false);
56+		add(refreshButton);
57+		refreshButton.addActionListener(this);
58+	}
59     ftpButton = new Button("File Transfer");
60     ftpButton.setEnabled(false);
61     add(ftpButton);
62@@ -79,9 +85,10 @@
63
64   public void enableButtons() {
65     disconnectButton.setEnabled(true);
66+    ftpButton.setEnabled(true);
67+    if (viewer.ftpOnly) {return;}
68     clipboardButton.setEnabled(true);
69     refreshButton.setEnabled(true);
70-    ftpButton.setEnabled(true);
71   }
72
73   //
74@@ -89,6 +96,9 @@
75   //
76
77   public void disableButtonsOnDisconnect() {
78+    ftpButton.setEnabled(false);
79+    if (viewer.ftpOnly) {return;}
80+
81     remove(disconnectButton);
82     disconnectButton = new Button("Hide desktop");
83     disconnectButton.setEnabled(true);
84@@ -99,7 +109,6 @@
85     clipboardButton.setEnabled(false);
86     ctrlAltDelButton.setEnabled(false);
87     refreshButton.setEnabled(false);
88-    ftpButton.setEnabled(false);
89
90     validate();
91   }
92@@ -110,6 +119,7 @@
93   //
94
95   public void enableRemoteAccessControls(boolean enable) {
96+    if (viewer.ftpOnly) {return;}
97     ctrlAltDelButton.setEnabled(enable);
98   }
99
100@@ -163,9 +173,19 @@
101     }
102     else if (evt.getSource() == ftpButton)
103     {
104-		viewer.ftp.setVisible(!viewer.ftp.isVisible());
105+// begin runge/x11vnc
106+		if (viewer.ftpOnly) {
107+			viewer.vncFrame.setVisible(false);
108+		}
109+		viewer.ftp.setSavedLocations();
110+		if (viewer.ftp.isVisible()) {
111+			viewer.ftp.doClose();
112+		} else {
113+			viewer.ftp.doOpen();
114+		}
115+// end runge/x11vnc
116 		viewer.rfb.readServerDriveList();
117-
118+
119     }
120   }
121 }
122diff -Naur JavaViewer.orig/FTPFrame.java JavaViewer/FTPFrame.java
123--- JavaViewer.orig/FTPFrame.java	2005-03-15 23:53:14.000000000 -0500
124+++ JavaViewer/FTPFrame.java	2009-01-13 09:48:30.000000000 -0500
125@@ -24,8 +24,17 @@
126 import java.io.*;
127 import java.util.ArrayList;
128 import java.util.Vector;
129+import java.util.Date;
130 import javax.swing.*;
131
132+import java.nio.ByteBuffer;
133+import java.nio.CharBuffer;
134+import java.nio.charset.*;
135+
136+// begin runge/x11vnc
137+import java.util.Arrays;
138+// end runge/x11vnc
139+
140
141 /*
142  * Created on Feb 25, 2004
143@@ -74,12 +83,31 @@
144 	public javax.swing.JTextField connectionStatus = null;
145 	public boolean updateDriveList;
146 	private Vector remoteList = null;
147+	private Vector remoteListInfo = null;
148 	private Vector localList = null;
149+	private Vector localListInfo = null;
150 	private File currentLocalDirectory = null;	// Holds the current local Directory
151 	private File currentRemoteDirectory = null;	// Holds the current remote Directory
152 	private File localSelection = null;		// Holds the currently selected local file
153 	private String remoteSelection = null;	// Holds the currently selected remote file
154 	public String selectedTable = null;
155+
156+// begin runge/x11vnc
157+	private javax.swing.JButton viewButton = null;
158+	private javax.swing.JButton refreshButton = null;
159+	public File saveLocalDirectory = null;
160+	public long saveLocalDirectoryTime = 0;
161+	public int saveLocalDirectoryCount = 0;
162+	public String saveRemoteDirectory = null;
163+	public long saveRemoteDirectoryTime = 0;
164+	public int saveRemoteDirectoryCount = 0;
165+	private boolean localCurrentIsDir = true;
166+	private int lastRemoteIndex = -1;
167+	private int lastLocalIndex = -1;
168+	private boolean doingShortcutDir = false;
169+	private boolean gotShortcutDir = false;
170+	private boolean ignore_events = false;
171+// end   runge/x11vnc
172
173 //	 sf@2004 - Separate directories and files for better lisibility
174 	private ArrayList DirsList;
175@@ -125,11 +153,61 @@
176
177 	 void refreshRemoteLocation()
178 	 {
179+
180+//System.out.println("refreshRemoteLocation1");
181 		remoteList.clear();
182+		remoteListInfo.clear();
183 		remoteFileTable.setListData(remoteList);
184+System.out.println("refreshRemoteLocation '" + remoteLocation.getText() + "'");	// runge/x11vnc
185 		viewer.rfb.readServerDirectory(remoteLocation.getText());
186 	 }
187
188+// begin runge/x11vnc
189+	 public void setSavedLocations() {
190+		saveLocalDirectory = currentLocalDirectory;
191+		saveLocalDirectoryTime = System.currentTimeMillis();
192+		saveLocalDirectoryCount = 0;
193+
194+		if (remoteLocation != null) {
195+			saveRemoteDirectory = remoteLocation.getText();
196+System.out.println("RemoteSave '" + saveRemoteDirectory + "'");
197+		}
198+		saveRemoteDirectoryTime = System.currentTimeMillis();
199+		saveRemoteDirectoryCount = 0;
200+	 }
201+
202+	private File saveLocalHack(File dir) {
203+		saveLocalDirectoryCount++;
204+//System.out.println("L " + saveLocalDirectoryCount + " dt: " + (System.currentTimeMillis() - saveLocalDirectoryTime) + " - " + saveLocalDirectory);
205+		if (System.currentTimeMillis() > saveLocalDirectoryTime + 2000 || saveLocalDirectoryCount > 2) {
206+			saveLocalDirectory = null;
207+		}
208+		if (saveLocalDirectory != null) {
209+			currentLocalDirectory = saveLocalDirectory;
210+			localLocation.setText(saveLocalDirectory.toString());
211+			return saveLocalDirectory;
212+		} else {
213+			return dir;
214+		}
215+	}
216+
217+	private String saveRemoteHack(String indrive) {
218+		saveRemoteDirectoryCount++;
219+//System.out.println("R " + saveRemoteDirectoryCount + " - " + saveRemoteDirectory);
220+		if (saveRemoteDirectory != null && saveRemoteDirectoryCount > 1) {
221+			saveRemoteDirectory = null;
222+		}
223+		if (saveRemoteDirectory != null) {
224+			if (! saveRemoteDirectory.equals("")) {
225+System.out.println("saveRemoteHack setText + refreshRemoteLocation '" + saveRemoteDirectory + "'");
226+				return saveRemoteDirectory;
227+			}
228+		}
229+		return indrive;
230+	}
231+// end runge/x11vnc
232+
233+
234 	/*
235 	 * Prints the list of drives on the remote directory and returns a String[].
236 	 * str takes as string like A:fC:lD:lE:lF:lG:cH:c
237@@ -143,6 +221,9 @@
238 		int size = str.length();
239 		String driveType = null;
240 		String[] drive = new String[str.length() / 3];
241+		int idx = 0, C_drive = -1, O_drive = -1;
242+
243+System.out.println("ComboBox: Str   '" + str + "'");
244
245 		// Loop through the string to create a String[]
246 		for (int i = 0; i < size; i = i + 3) {
247@@ -150,26 +231,68 @@
248 			driveType = str.substring(i + 2, i + 3);
249 			if (driveType.compareTo("f") == 0)
250 				drive[i / 3] += "\\ Floppy";
251-			if (driveType.compareTo("l") == 0)
252+			if (driveType.compareTo("l") == 0) {
253 				drive[i / 3] += "\\ Local Disk";
254+				if (drive[i/3].substring(0,1).toUpperCase().equals("C")) {
255+					C_drive = idx;
256+				} else if (O_drive < 0) {
257+					O_drive = idx;
258+				}
259+			}
260 			if (driveType.compareTo("c") == 0)
261 				drive[i / 3] += "\\ CD-ROM";
262 			if (driveType.compareTo("n") == 0)
263 				drive[i / 3] += "\\ Network";
264
265 			remoteDrivesComboBox.addItem(drive[i / 3]);
266+System.out.println("ComboBox: Add " + idx + " '" + drive[i/3] + "'");
267+			idx++;
268+		}
269+
270+		// runge
271+		if (viewer.ftpDropDown != null) {
272+			String[] dd = viewer.ftpDropDown.split("\\.");
273+			for (int i=0; i < dd.length; i++) {
274+				if (!dd[i].equals("")) {
275+					String s = dd[i];
276+					if (s.startsWith("TOP_")) {
277+						s = s.substring(4);
278+						remoteDrivesComboBox.insertItemAt(" [" + s + "]", 0);
279+					} else {
280+						remoteDrivesComboBox.addItem(" [" + s + "]");
281+					}
282+				}
283+			}
284+		} else {
285+			remoteDrivesComboBox.addItem(" [My Documents]");
286+			remoteDrivesComboBox.addItem(" [Desktop]");
287+			remoteDrivesComboBox.addItem(" [Home]");
288 		}
289+
290 		//sf@ - Select Drive C:as default if possible
291 		boolean bFound = false;
292-		for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++)
293-		{
294-			if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C"))
295-			{
296-				remoteDrivesComboBox.setSelectedIndex(i);
297+
298+		if (false) {
299+			for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++) {
300+				if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C")) {
301+					remoteDrivesComboBox.setSelectedIndex(i);
302+					bFound = true;
303+				}
304+			}
305+		} else {
306+			if (C_drive >= 0) {
307+				remoteDrivesComboBox.setSelectedIndex(C_drive);
308+				bFound = true;
309+System.out.println("ComboBox: C_drive index: " + C_drive);
310+			} else if (O_drive >= 0) {
311+				remoteDrivesComboBox.setSelectedIndex(O_drive);
312 				bFound = true;
313+System.out.println("ComboBox: Other_drive index: " + O_drive);
314 			}
315 		}
316+
317 		if (!bFound) remoteDrivesComboBox.setSelectedIndex(0);
318+
319 		updateDriveList = false;
320 		return drive;
321 	}
322@@ -185,6 +308,8 @@
323 		stopButton.setVisible(true);
324 		stopButton.setEnabled(true);
325 		receiveButton.setEnabled(false);
326+		viewButton.setEnabled(false);	// runge/x11vnc
327+		refreshButton.setEnabled(false);
328 		remoteTopButton.setEnabled(false);
329 		sendButton.setEnabled(false);
330 		remoteFileTable.setEnabled(false);
331@@ -207,6 +332,8 @@
332 		stopButton.setVisible(false);
333 		stopButton.setEnabled(false);
334 		receiveButton.setEnabled(true);
335+		viewButton.setEnabled(true);	// runge/x11vnc
336+		refreshButton.setEnabled(true);
337 		remoteTopButton.setEnabled(true);
338 		sendButton.setEnabled(true);
339 		remoteFileTable.setEnabled(true);
340@@ -221,10 +348,11 @@
341 	/*
342 	 * Print Directory prints out all the contents of a directory
343 	 */
344-	void printDirectory(ArrayList a) {
345+	void printDirectory(ArrayList a, ArrayList b) {
346
347 		for (int i = 0; i < a.size(); i++) {
348 			remoteList.addElement(a.get(i));
349+			remoteListInfo.addElement(b.get(i));
350 		}
351 		remoteFileTable.setListData(remoteList);
352 	}
353@@ -235,10 +363,12 @@
354 	 * @return void
355 	 */
356 	private void initialize() {
357+		ignore_events = true;
358 		this.setSize(794, 500);
359 		this.setContentPane(getJContentPane());
360+		ignore_events = false;
361 		updateDriveList = true;
362-		}
363+	}
364 	/**
365 	 * This method initializes jContentPane.  This is the main content pane
366 	 *
367@@ -253,6 +383,33 @@
368 			jContentPane.add(getRemotePanel(), java.awt.BorderLayout.EAST);
369 			jContentPane.add(getLocalPanel(), java.awt.BorderLayout.WEST);
370 			jContentPane.add(getButtonPanel(), java.awt.BorderLayout.CENTER);
371+
372+			KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
373+			AbstractAction escapeAction = new AbstractAction() {
374+				public void actionPerformed(ActionEvent actionEvent) {
375+					System.out.println("Escape Pressed");
376+					if (viewer.ftpOnly) {
377+						System.out.println("exiting...");
378+						System.exit(0);
379+					} else {
380+						doClose();
381+					}
382+				}
383+			};
384+			jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction");
385+			jContentPane.getInputMap().put(stroke, "escapeAction");
386+			jContentPane.getActionMap().put("escapeAction", escapeAction);
387+
388+			stroke = KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK);
389+			AbstractAction resetAction = new AbstractAction() {
390+				public void actionPerformed(ActionEvent actionEvent) {
391+					System.out.println("Ctrl-R Pressed");
392+					doReset();
393+				}
394+			};
395+			jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "resetAction");
396+			jContentPane.getInputMap().put(stroke, "resetAction");
397+			jContentPane.getActionMap().put("resetAction", resetAction);
398 		}
399 		return jContentPane;
400 	}
401@@ -270,6 +427,7 @@
402 			topPanelLocal.add(getLocalMachineLabel(), java.awt.BorderLayout.CENTER);
403 			topPanelLocal.add(getLocalTopButton(), java.awt.BorderLayout.EAST);
404 			topPanelLocal.setBackground(java.awt.Color.lightGray);
405+//System.out.println("getTopPanelLocal");
406 		}
407 		return topPanelLocal;
408 	}
409@@ -288,6 +446,7 @@
410 			topPanelRemote.add(getRemoteMachineLabel(), java.awt.BorderLayout.CENTER);
411 			topPanelRemote.add(getRemoteTopButton(), java.awt.BorderLayout.EAST);
412 			topPanelRemote.setBackground(java.awt.Color.lightGray);
413+//System.out.println("getTopPanelRemote");
414 		}
415 		return topPanelRemote;
416 	}
417@@ -301,6 +460,7 @@
418 		if (topPanelCenter == null) {
419 			topPanelCenter = new javax.swing.JPanel();
420 			topPanelCenter.add(getDummyButton(), null);
421+//System.out.println("getTopPanelCenter");
422 		}
423 		return topPanelCenter;
424 	}
425@@ -328,6 +488,7 @@
426 			topPanel.add(getRemoteTopButton(), null);
427 			topPanel.setBackground(java.awt.Color.lightGray);
428 			*/
429+//System.out.println("getTopPanel");
430 		}
431 		return topPanel;
432 	}
433@@ -348,6 +509,7 @@
434 			statusPanel.add(getJProgressBar(), null);
435 			statusPanel.add(getConnectionStatus(), null);
436 			statusPanel.setBackground(java.awt.Color.lightGray);
437+//System.out.println("getStatusPanel");
438
439 		}
440 		return statusPanel;
441@@ -368,6 +530,7 @@
442 			remotePanel.add(getRemoteScrollPane(), null);
443 			remotePanel.add(getRemoteStatus(), null);
444 			remotePanel.setBackground(java.awt.Color.lightGray);
445+//System.out.println("getRemotePanel");
446 		}
447 		return remotePanel;
448 	}
449@@ -390,6 +553,7 @@
450 			localPanel.setComponentOrientation(
451 				java.awt.ComponentOrientation.UNKNOWN);
452 			localPanel.setName("localPanel");
453+//System.out.println("getLocalPanel");
454 		}
455 		return localPanel;
456 	}
457@@ -405,12 +569,15 @@
458 			buttonPanel = new javax.swing.JPanel();
459 			buttonPanel.setLayout(null);
460 			buttonPanel.add(getReceiveButton(), null);
461+			buttonPanel.add(getRefreshButton(), null);	// runge/x11vnc
462+			buttonPanel.add(getViewButton(), null);	// runge/x11vnc
463 			buttonPanel.add(getNewFolderButton(), null);
464 			buttonPanel.add(getCloseButton(), null);
465 			buttonPanel.add(getDeleteButton(), null);
466 			buttonPanel.add(getSendButton(), null);
467 			buttonPanel.add(getStopButton(), null);
468 			buttonPanel.setBackground(java.awt.Color.lightGray);
469+//System.out.println("getButtonPanel");
470 		}
471 		return buttonPanel;
472 	}
473@@ -422,10 +589,11 @@
474 	private javax.swing.JButton getSendButton() {
475 		if (sendButton == null) {
476 			sendButton = new javax.swing.JButton();
477-			sendButton.setBounds(20, 30, 97, 25);
478+			sendButton.setBounds(15, 30, 107, 25);	// runge/x11vnc
479 			sendButton.setText("Send >>");
480 			sendButton.setName("sendButton");
481 			sendButton.addActionListener(this);
482+//System.out.println("getSendButton");
483
484 		}
485 		return sendButton;
486@@ -438,7 +606,7 @@
487 	private javax.swing.JButton getReceiveButton() {
488 		if (receiveButton == null) {
489 			receiveButton = new javax.swing.JButton();
490-			receiveButton.setBounds(20, 60, 97, 25);
491+			receiveButton.setBounds(15, 60, 107, 25);	// runge/x11vnc
492 			receiveButton.setText("<< Receive");
493 			receiveButton.setName("receiveButton");
494 			receiveButton.addActionListener(this);
495@@ -453,7 +621,7 @@
496 	private javax.swing.JButton getDeleteButton() {
497 		if (deleteButton == null) {
498 			deleteButton = new javax.swing.JButton();
499-			deleteButton.setBounds(20, 110, 97, 25);
500+			deleteButton.setBounds(15, 110, 107, 25);	// runge/x11vnc
501 			deleteButton.setText("Delete File");
502 			deleteButton.setName("deleteButton");
503 			deleteButton.addActionListener(this);
504@@ -468,7 +636,7 @@
505 	private javax.swing.JButton getNewFolderButton() {
506 		if (newFolderButton == null) {
507 			newFolderButton = new javax.swing.JButton();
508-			newFolderButton.setBounds(20, 140, 97, 25);
509+			newFolderButton.setBounds(15, 140, 107, 25);	// runge/x11vnc
510 			newFolderButton.setText("New Folder");
511 			newFolderButton.setName("newFolderButton");
512 			newFolderButton.addActionListener(this);
513@@ -476,6 +644,39 @@
514 		return newFolderButton;
515 	}
516
517+// begin runge/x11vnc
518+	/**
519+	 * This method initializes refreshButton
520+	 *
521+	 * @return javax.swing.JButton
522+	 */
523+	private javax.swing.JButton getRefreshButton() {
524+		if (refreshButton == null) {
525+			refreshButton = new javax.swing.JButton();
526+			refreshButton.setBounds(15, 170, 107, 25);
527+			refreshButton.setText("Refresh");
528+			refreshButton.setName("refreshButton");
529+			refreshButton.addActionListener(this);
530+		}
531+		return refreshButton;
532+	}
533+	/**
534+	 * This method initializes viewButton
535+	 *
536+	 * @return javax.swing.JButton
537+	 */
538+	private javax.swing.JButton getViewButton() {
539+		if (viewButton == null) {
540+			viewButton = new javax.swing.JButton();
541+			viewButton.setBounds(15, 200, 107, 25);
542+			viewButton.setText("View File");
543+			viewButton.setName("viewButton");
544+			viewButton.addActionListener(this);
545+		}
546+		return viewButton;
547+	}
548+// end   runge/x11vnc
549+
550 	/**
551 	 * This method initializes stopButton
552 	 *
553@@ -486,7 +687,7 @@
554 		if (stopButton == null)
555 		{
556 			stopButton = new javax.swing.JButton();
557-			stopButton.setBounds(20, 200, 97, 25);
558+			stopButton.setBounds(15, 230, 107, 25);	// runge/x11vnc
559 			stopButton.setText("Stop");
560 			stopButton.setName("stopButton");
561 			stopButton.addActionListener(this);
562@@ -503,8 +704,12 @@
563 	private javax.swing.JButton getCloseButton() {
564 		if (closeButton == null) {
565 			closeButton = new javax.swing.JButton();
566-			closeButton.setBounds(20, 325, 97, 25);
567-			closeButton.setText("Close");
568+			closeButton.setBounds(15, 325, 107, 25);	// runge/x11vnc
569+			if (viewer.ftpOnly) {
570+				closeButton.setText("Quit");
571+			} else {
572+				closeButton.setText("Close");
573+			}
574 			closeButton.setName("closeButton");
575 			closeButton.addActionListener(this);
576 		}
577@@ -551,6 +756,7 @@
578 			//Select the second entry (e.g. C:\)
579 			// localDrivesComboBox.setSelectedIndex(1);
580 			localDrivesComboBox.addActionListener(this);
581+//System.out.println("getLocalDrivesComboBox");
582 		}
583 		updateDriveList = false;
584 		return localDrivesComboBox;
585@@ -567,6 +773,7 @@
586 			remoteDrivesComboBox.setFont(
587 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
588 			remoteDrivesComboBox.addActionListener(this);
589+//System.out.println("getRemoteDrivesComboBox");
590
591 		}
592 		return remoteDrivesComboBox;
593@@ -587,6 +794,7 @@
594 			localMachineLabel.setFont(
595 				new java.awt.Font("Dialog", java.awt.Font.BOLD, 11));
596 			localMachineLabel.setEditable(false);
597+//System.out.println("getLocalMachineLabel");
598 		}
599 		return localMachineLabel;
600 	}
601@@ -622,6 +830,7 @@
602 			localTopButton.setFont(
603 				new java.awt.Font("Dialog", java.awt.Font.BOLD, 10));
604 			localTopButton.addActionListener(this);
605+//System.out.println("getLocalTopButton");
606 		}
607 		return localTopButton;
608 	}
609@@ -638,6 +847,7 @@
610 			remoteTopButton.setFont(
611 				new java.awt.Font("Dialog", java.awt.Font.BOLD, 10));
612 			remoteTopButton.addActionListener(this);
613+//System.out.println("getRemoteTopButton");
614 		}
615 		return remoteTopButton;
616 	}
617@@ -650,9 +860,24 @@
618 	private javax.swing.JList getLocalFileTable() {
619 		if (localFileTable == null) {
620 			localList = new Vector(0);
621+			localListInfo = new Vector(0);
622 			localFileTable = new JList(localList);
623+			MouseMotionListener mlisten = new MouseMotionAdapter() {
624+				public void mouseMoved(MouseEvent e) {
625+					int index = localFileTable.locationToIndex(e.getPoint());
626+					if (index == lastLocalIndex) {
627+						return;
628+					} else if (index < 0) {
629+						return;
630+					}
631+					lastLocalIndex = index;
632+					connectionStatus.setText((String) localListInfo.get(index));
633+				}
634+			};
635 			localFileTable.addMouseListener(this);
636+			localFileTable.addMouseMotionListener(mlisten);
637 			localFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
638+//System.out.println("getLocalFileTable");
639 		}
640 		return localFileTable;
641 	}
642@@ -669,6 +894,7 @@
643 			localScrollPane.setFont(
644 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
645 			localScrollPane.setName("localFileList");
646+//System.out.println("getLocalScrollPane");
647 		}
648 		return localScrollPane;
649 	}
650@@ -680,10 +906,25 @@
651 	private javax.swing.JList getRemoteFileTable() {
652 		if (remoteFileTable == null) {
653 			remoteList = new Vector(0);
654+			remoteListInfo = new Vector(0);
655 			remoteFileTable = new JList(remoteList);
656+			MouseMotionListener mlisten = new MouseMotionAdapter() {
657+				public void mouseMoved(MouseEvent e) {
658+					int index = remoteFileTable.locationToIndex(e.getPoint());
659+					if (index == lastRemoteIndex) {
660+						return;
661+					} else if (index < 0) {
662+						return;
663+					}
664+					lastRemoteIndex = index;
665+					connectionStatus.setText((String) remoteListInfo.get(index));
666+				}
667+			};
668 			remoteFileTable.addMouseListener(this);
669+			remoteFileTable.addMouseMotionListener(mlisten);
670 			remoteFileTable.setSelectedValue("C:\\", false);
671 			remoteFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
672+//System.out.println("getRemoteFileTable");
673
674 		}
675 		return remoteFileTable;
676@@ -698,6 +939,7 @@
677 			remoteScrollPane = new javax.swing.JScrollPane();
678 			remoteScrollPane.setViewportView(getRemoteFileTable());
679 			remoteScrollPane.setPreferredSize(new java.awt.Dimension(325, 418));
680+//System.out.println("getRemoteScrollPane");
681 		}
682 		return remoteScrollPane;
683 	}
684@@ -716,6 +958,7 @@
685 			remoteLocation.setBackground(new Color(255,255,238));
686 			remoteLocation.setFont(
687 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
688+//System.out.println("getRemoteLocation");
689 		}
690 		return remoteLocation;
691 	}
692@@ -732,6 +975,7 @@
693 			localLocation.setBackground( new Color(255,255,238));
694 			localLocation.setFont(
695 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
696+//System.out.println("getLocalLocation");
697 		}
698 		return localLocation;
699 	}
700@@ -748,6 +992,7 @@
701 			localStatus.setFont(
702 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
703 			localStatus.setEditable(false);
704+//System.out.println("getLocalStatus");
705 		}
706 		return localStatus;
707 	}
708@@ -764,6 +1009,7 @@
709 			remoteStatus.setFont(
710 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
711 			remoteStatus.setEditable(false);
712+//System.out.println("getRemoteStatus");
713 		}
714 		return remoteStatus;
715 	}
716@@ -777,9 +1023,10 @@
717 			historyComboBox = new javax.swing.JComboBox();
718 			historyComboBox.setFont(
719 				new java.awt.Font("Dialog", java.awt.Font.BOLD, 10));
720-			historyComboBox.insertItemAt(new String("Pulldown to view history ..."),0);
721+			historyComboBox.insertItemAt(new String("Pulldown to view history;   Press Escape to Close/Quit;   Press Ctrl-R to Reset Panel."),0);
722 			historyComboBox.setSelectedIndex(0);
723 			historyComboBox.addActionListener(this);
724+//System.out.println("getHistoryComboBox");
725 		}
726 		return historyComboBox;
727 	}
728@@ -791,6 +1038,7 @@
729 	private javax.swing.JProgressBar getJProgressBar() {
730 		if (jProgressBar == null) {
731 			jProgressBar = new javax.swing.JProgressBar();
732+//System.out.println("getJProgressBar");
733 		}
734 		return jProgressBar;
735 	}
736@@ -806,6 +1054,7 @@
737 			connectionStatus.setBackground(java.awt.Color.lightGray);
738 			connectionStatus.setFont(
739 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
740+//System.out.println("getConnectionStatus");
741 		}
742 			connectionStatus.setEditable(false);
743 		return connectionStatus;
744@@ -815,7 +1064,12 @@
745 	 * Implements Action listener.
746 	 */
747 	public void actionPerformed(ActionEvent evt) {
748-		System.out.println(evt.getSource());
749+//		System.out.println(evt.getSource());
750+
751+		if (ignore_events) {
752+			System.out.println("ignore_events: " + evt.getSource());
753+			return;
754+		}
755
756 		if (evt.getSource() == closeButton)
757 		{ // Close Button
758@@ -829,15 +1083,27 @@
759 		{
760 			doReceive();
761 		}
762+// begin runge/x11vnc
763+		else if (evt.getSource() == viewButton)
764+		{
765+			doView();
766+		}
767+// end   runge/x11vnc
768 		else if (evt.getSource() == localDrivesComboBox)
769 		{
770 			changeLocalDrive();
771 		}
772 		else if (evt.getSource() == remoteDrivesComboBox)
773 		{
774+//System.out.println("remoteDrivesComboBox");	// runge/x11vnc
775 			changeRemoteDrive();
776-			remoteList.clear();
777-			remoteFileTable.setListData(remoteList);
778+
779+			// are these really needed? changeRemoteDrive() does them at the end.
780+			if (false) {
781+				remoteList.clear();
782+				remoteListInfo.clear();
783+				remoteFileTable.setListData(remoteList);
784+			}
785 		}
786 		else if (evt.getSource() == localTopButton)
787 		{
788@@ -845,12 +1111,17 @@
789 		}
790 		else if (evt.getSource() == remoteTopButton)
791 		{
792+//System.out.println("remoteTopButton");	// runge/x11vnc
793 		  	changeRemoteDrive();
794 		}
795 		else if(evt.getSource() == deleteButton)
796 		{
797 			doDelete();
798 		}
799+		else if(evt.getSource() == refreshButton)
800+		{
801+			doRefresh();
802+		}
803 		else if(evt.getSource()==newFolderButton)
804 		{
805 			doNewFolder();
806@@ -864,7 +1135,7 @@
807
808 	private void doNewFolder()
809 	{
810-		String name = JOptionPane.showInputDialog(null,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE);
811+		String name = JOptionPane.showInputDialog(jContentPane,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE);
812 		if(selectedTable.equals("remote"))
813 		{
814 			name = remoteLocation.getText()+name;
815@@ -880,34 +1151,106 @@
816 			historyComboBox.setSelectedIndex(0);
817 		}
818 	}
819-	private void doClose()
820+	public void doClose()
821 	{
822+		if (viewer.ftpOnly) {
823+			viewer.disconnect();
824+			return;
825+		}
826 		try {
827 			this.setVisible(false);
828-			viewer.rfb.writeFramebufferUpdateRequest(
829-									0,
830-									0,
831-									viewer.rfb.framebufferWidth,
832-									viewer.rfb.framebufferHeight,
833-									true);
834+			viewer.rfb.writeFramebufferUpdateRequest(0, 0, viewer.rfb.framebufferWidth,
835+			    viewer.rfb.framebufferHeight, true);
836+
837+			if (false) {
838+				this.dispose();
839+				jContentPane = null;
840+			}
841 		} catch (IOException e) {
842 			// TODO Auto-generated catch block
843 			e.printStackTrace();
844 		}
845 	}
846+	private void unSwing() {
847+		jContentPane = null;
848+		topPanel = null;
849+		topPanelLocal = null;
850+		topPanelRemote = null;
851+		topPanelCenter = null;
852+		statusPanel = null;
853+		remotePanel = null;
854+		localPanel = null;
855+		buttonPanel = null;
856+		sendButton = null;
857+		receiveButton = null;
858+		deleteButton = null;
859+		newFolderButton = null;
860+		stopButton = null;
861+		closeButton = null;
862+		dummyButton = null;
863+		localDrivesComboBox = null;
864+		remoteDrivesComboBox = null;
865+		localMachineLabel = null;
866+		remoteMachineLabel = null;
867+		localTopButton = null;
868+		remoteTopButton = null;
869+		localScrollPane = null;
870+		localFileTable = null;
871+		remoteScrollPane = null;
872+		remoteFileTable = null;
873+		remoteLocation = null;
874+		localLocation = null;
875+		localStatus = null;
876+		remoteStatus = null;
877+		historyComboBox = null;
878+		jProgressBar = null;
879+		connectionStatus = null;
880+		viewButton = null;
881+		refreshButton = null;
882+	}
883+
884+	public void doReset()
885+	{
886+		try {
887+			this.setVisible(false);
888+			this.dispose();
889+			jContentPane = null;
890+		        try {Thread.sleep(500);} catch (InterruptedException e) {}
891+			viewer.ftp_init();
892+		} catch (Exception e) {
893+			// TODO Auto-generated catch block
894+			e.printStackTrace();
895+		}
896+	}
897
898+	public void doOpen()
899+	{
900+		try {
901+			this.setVisible(true);
902+			if (false) {
903+				this.initialize();
904+			}
905+		} catch (Exception e) {
906+			// TODO Auto-generated catch block
907+			e.printStackTrace();
908+		}
909+	}
910 	private void doDelete()
911 	{
912-		System.out.println("Delete Button Pressed");
913+//		System.out.println("Delete Button Pressed");
914 		//Call this method to delete a file at server
915 		if(selectedTable.equals("remote"))
916 		{
917-			String sFileName = ((String) this.remoteFileTable.getSelectedValue());
918+			Object selected = this.remoteFileTable.getSelectedValue();
919+			if (selected == null) {
920+				return;
921+			}
922+			String sFileName = ((String) selected);
923
924 //			 sf@2004 - Directory can't be deleted
925 			if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
926 			{
927-				JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
928+				JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
929 				return;
930 			}
931
932@@ -916,7 +1259,7 @@
933 			// sf@2004 - Delete prompt
934 			if (remoteList.contains(sFileName))
935 			{
936-				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);
937+				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);
938 				if (r == JOptionPane.NO_OPTION)
939 					return;
940 			}
941@@ -926,18 +1269,22 @@
942 		}
943 		else
944 		{
945-			String sFileName = ((String) this.localFileTable.getSelectedValue());
946+			Object selected = this.localFileTable.getSelectedValue();
947+			if (selected == null) {
948+				return;
949+			}
950+			String sFileName = ((String) selected);
951
952 //			 sf@2004 - Directory can't be deleted
953 			if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
954 			{
955-				JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
956+				JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
957 				return;
958 			}
959 			// sf@2004 - Delete prompt
960 			if (localList.contains(sFileName))
961 			{
962-				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);
963+				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);
964 				if (r == JOptionPane.NO_OPTION)
965 					return;
966 			}
967@@ -952,21 +1299,25 @@
968
969 	private void doReceive()
970 	{
971-		System.out.println("Received Button Pressed");
972+//		System.out.println("Received Button Pressed");
973
974-		String sFileName = ((String) this.remoteFileTable.getSelectedValue());
975+		Object selected = this.remoteFileTable.getSelectedValue();
976+		if (selected == null) {
977+			return;
978+		}
979+		String sFileName = ((String) selected);
980
981 		// sf@2004 - Directory can't be transfered
982 		if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
983 		{
984-			JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
985+			JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
986 			return;
987 		}
988
989 		// sf@2004 - Overwrite prompt
990 		if (localList.contains(sFileName))
991 		{
992-			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);
993+			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);
994 			if (r == JOptionPane.NO_OPTION)
995 				return;
996 		}
997@@ -979,23 +1330,101 @@
998 		viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath);
999 	}
1000
1001+// begin runge/x11vnc
1002+	private void doRefresh()
1003+	{
1004+		System.out.println("Refreshing Local and Remote.");
1005+		refreshLocalLocation();
1006+		refreshRemoteLocation();
1007+	}
1008+
1009+	private void doView()
1010+	{
1011+//		System.out.println("View Button Pressed");
1012+
1013+		if (selectedTable == null) {
1014+			return;
1015+		}
1016+		if (selectedTable.equals("remote")) {
1017+			viewRemote();
1018+		} else if (selectedTable.equals("local")) {
1019+			viewLocal();
1020+		}
1021+	}
1022+
1023+	private File doReceiveTmp()
1024+	{
1025+
1026+		if (remoteFileTable == null) {
1027+			return null;
1028+		}
1029+		Object selected = this.remoteFileTable.getSelectedValue();
1030+		if (selected == null) {
1031+			return null;
1032+		}
1033+		String sFileName = ((String) selected);
1034+
1035+		if (sFileName == null) {
1036+			return null;
1037+		}
1038+
1039+		// sf@2004 - Directory can't be transfered
1040+		if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
1041+		{
1042+			return null;
1043+		}
1044+
1045+		File tmp = null;
1046+		try {
1047+			tmp = File.createTempFile("ULTRAFTP", ".txt");
1048+		} catch (Exception e) {
1049+			return null;
1050+		}
1051+
1052+		//updateHistory("Downloaded " + localSelection.toString());
1053+		String remoteFileName = this.remoteLocation.getText();
1054+		remoteFileName+= ((String) this.remoteFileTable.getSelectedValue()).substring(1);
1055+		System.out.println("remoteFileName: " + remoteFileName);
1056+if (false) {
1057+		char[] b = remoteFileName.toCharArray();
1058+		for (int n = 0; n < b.length; n++) {
1059+			System.out.print(Integer.toHexString(b[n]) + " ");
1060+		}
1061+		System.out.println("");
1062+		for (int n = 0; n < b.length; n++) {
1063+			System.out.print(b[n]);
1064+		}
1065+		System.out.println("");
1066+}
1067+
1068+		String localDestinationPath = tmp.getAbsolutePath();
1069+		viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath);
1070+		System.out.println("ReceiveTmp: " + localDestinationPath);
1071+		return tmp;
1072+	}
1073+// end   runge/x11vnc
1074+
1075 	private void doSend()
1076 	{
1077-		System.out.println("Send Button Pressed");
1078+//		System.out.println("Send Button Pressed");
1079
1080-		String sFileName = ((String) this.localFileTable.getSelectedValue());
1081+		Object selected = this.localFileTable.getSelectedValue();
1082+		if (selected == null) {
1083+			return;
1084+		}
1085+		String sFileName = ((String) selected);
1086
1087 		// sf@2004 - Directory can't be transfered
1088 		if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
1089 		{
1090-			JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
1091+			JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
1092 			return;
1093 		}
1094
1095 		// sf@2004 - Overwrite prompt
1096 		if (remoteList.contains(sFileName))
1097 		{
1098-			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);
1099+			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);
1100 			if (r == JOptionPane.NO_OPTION)
1101 				return;
1102 		}
1103@@ -1013,6 +1442,7 @@
1104 	//
1105 	private void doStop()
1106 	{
1107+		System.out.println("** Current Transfer Aborted **");
1108 		viewer.rfb.fAbort = true;
1109 	}
1110 	/**
1111@@ -1024,6 +1454,14 @@
1112 		System.out.println("History: " + message);
1113 		historyComboBox.insertItemAt(new String(message), 0);
1114 	}
1115+
1116+	public void receivedRemoteDirectoryName(String str) {
1117+		if (doingShortcutDir) {
1118+			if (str.length() > 1) {
1119+				remoteLocation.setText(str);
1120+			}
1121+		}
1122+	}
1123
1124 	/**
1125 	 * This method updates the file table to the current selection of the remoteComboBox
1126@@ -1034,11 +1472,44 @@
1127 		remoteSelection = null;
1128
1129 		if (!updateDriveList) {
1130-			String drive =	remoteDrivesComboBox.getSelectedItem().toString().substring(0,1)+ ":\\";
1131-			viewer.rfb.readServerDirectory(drive);
1132-			remoteLocation.setText(drive);
1133+//System.out.println("changeRemoteDrive-A " + drive);	// begin runge/x11vnc
1134+			Object selected = remoteDrivesComboBox.getSelectedItem();
1135+			if (selected != null)  {
1136+				String instr =	selected.toString();
1137+				if (instr != null) {
1138+System.out.println("changeRemoteDrive: instr='" + instr + "'");
1139+					String drive =	instr.substring(0,1)+ ":\\";
1140+					if (instr.startsWith(" [")) {
1141+						int idx = instr.lastIndexOf(']');
1142+						if (idx > 2) {
1143+							drive = instr.substring(2, idx);
1144+						} else {
1145+							drive = instr.substring(2);
1146+						}
1147+						if (drive.equals("Home")) {
1148+							drive = "";
1149+						}
1150+						drive += "\\";
1151+						doingShortcutDir = true;
1152+					} else {
1153+						doingShortcutDir = false;
1154+						drive = saveRemoteHack(drive);
1155+					}
1156+					gotShortcutDir = false;
1157+					viewer.rfb.readServerDirectory(drive);
1158+					if (!gotShortcutDir) {
1159+						remoteLocation.setText(drive);
1160+					}
1161+				} else {
1162+System.out.println("changeRemoteDrive: instr null");
1163+				}
1164+			} else {
1165+System.out.println("changeRemoteDrive: selection null");
1166+			}
1167+//System.out.println("changeRemoteDrive-B " + drive);	// end runge/x11vnc
1168 		}
1169 		remoteList.clear();
1170+		remoteListInfo.clear();
1171 		remoteFileTable.setListData(remoteList);
1172 	}
1173 	/**
1174@@ -1048,6 +1519,7 @@
1175 	private void changeLocalDrive()
1176 	{
1177 		File currentDrive = new File(localDrivesComboBox.getSelectedItem().toString());
1178+System.out.println("changeLocalDrive " + currentDrive.toString());	// runge/x11vnc
1179 		if(currentDrive.canRead())
1180 		{
1181 			localSelection = null;
1182@@ -1057,9 +1529,11 @@
1183 		else
1184 		{
1185 			localList.clear();
1186+			localListInfo.clear();
1187 			localStatus.setText("WARNING: Drive " + localDrivesComboBox.getSelectedItem().toString());
1188 			connectionStatus.setText(" > WARNING - Local Drive unavailable (possibly restricted access or media not present)");
1189 		}
1190+
1191 	}
1192 	/**
1193 	 * Determines which FileTable was double-clicked and updates the table
1194@@ -1098,10 +1572,18 @@
1195 		selectedTable = "remote";
1196 		localFileTable.setBackground(new Color(238, 238, 238));
1197 		remoteFileTable.setBackground(new Color(255, 255, 255));
1198-		String name = (remoteFileTable.getSelectedValue().toString()).substring(1);
1199+		Object selected = remoteFileTable.getSelectedValue();
1200+		if (selected == null) {
1201+			return;
1202+		}
1203+		String selstr = selected.toString();
1204+		if (selstr == null) {
1205+			return;
1206+		}
1207+		String name = selstr.substring(1);
1208 		if( !name.substring(0, 2).equals(" ["))
1209 			remoteSelection = remoteLocation.getText() + name.substring(0, name.length());
1210-
1211+
1212 	}
1213
1214 	/*
1215@@ -1115,10 +1597,38 @@
1216 		localFileTable.setBackground(new Color(255, 255, 255));
1217 		File currentSelection = new File(currentLocalDirectory, getTrimmedSelection());
1218
1219-		if(currentSelection.isFile())
1220+// begin runge/x11vnc
1221+		// localSelection = currentSelection.getAbsoluteFile();
1222+		if(currentSelection.isFile()) {
1223 			localSelection = currentSelection.getAbsoluteFile();
1224+			localCurrentIsDir = false;
1225+		} else {
1226+			localCurrentIsDir = true;
1227+		}
1228+// end   runge/x11vnc
1229
1230 	}
1231+
1232+// begin runge/x11vnc
1233+	private void viewRemote() {
1234+		File tmp = doReceiveTmp();
1235+		if (tmp == null) {
1236+			return;
1237+		}
1238+		TextViewer tv = new TextViewer("Remote: " + remoteSelection, tmp, true);
1239+	}
1240+	private void viewLocal() {
1241+		if (localSelection == null) {
1242+			return;
1243+		}
1244+		if (localCurrentIsDir) {
1245+			return;
1246+		}
1247+		File loc = new File(localSelection.toString());
1248+		TextViewer tv = new TextViewer("Local: " + localSelection.toString(), loc, false);
1249+	}
1250+// end runge/x11vnc
1251+
1252 	/**
1253 	 * Updates the Remote File Table based on selection.  Called from mouseClicked handler
1254 	 */
1255@@ -1126,20 +1636,29 @@
1256 		String name = null;
1257 		String action = null;
1258 		String drive = null;
1259-		name = (remoteFileTable.getSelectedValue().toString()).substring(1);
1260+		Object selected = remoteFileTable.getSelectedValue();
1261+		if (selected == null) {
1262+			return;
1263+		}
1264+		String sname = selected.toString();
1265+		if (sname == null) {
1266+			return;
1267+		}
1268+		name = sname.substring(1);
1269
1270 		if (name.equals("[..]"))
1271 		{
1272 			action = "up";
1273 			remoteSelection = null;
1274 			drive = remoteLocation.getText().substring(0, remoteLocation.getText().length() - 1);
1275-			// JOptionPane.showMessageDialog(null, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE);
1276+			// JOptionPane.showMessageDialog(jContentPane, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE);
1277 			int index = drive.lastIndexOf("\\");
1278 			drive = drive.substring(0, index + 1);
1279
1280 			remoteLocation.setText(drive);
1281 			viewer.rfb.readServerDirectory(drive);
1282 			remoteList.clear();
1283+			remoteListInfo.clear();
1284 			remoteFileTable.setListData(remoteList);
1285 		}
1286 		else if (!name.substring(0, 2).equals(" [") && !name.substring((name.length() - 1), name.length()).equals("]"))
1287@@ -1149,6 +1668,7 @@
1288 			remoteSelection = remoteLocation.getText() + name.substring(0, name.length());
1289 			drive = remoteLocation.getText();
1290 			// ??
1291+			viewRemote();	// runge/x11vnc
1292 		}
1293 		else
1294 		{
1295@@ -1159,10 +1679,12 @@
1296 			remoteLocation.setText(drive);
1297 			viewer.rfb.readServerDirectory(drive);
1298 			remoteList.clear();
1299+			remoteListInfo.clear();
1300 			remoteFileTable.setListData(remoteList);
1301 		}
1302 		//remoteLocation.setText(drive);
1303 	}
1304+
1305 	/**
1306 	 * Updates the Local File Table based on selection. Called from MouseClicked handler
1307 	 */
1308@@ -1188,6 +1710,7 @@
1309 		else if (currentSelection.isFile())
1310 		{
1311 			localSelection = currentSelection.getAbsoluteFile();
1312+			viewLocal();	// runge/x11vnc
1313 		}
1314 		else if (currentSelection.isDirectory())
1315 		{
1316@@ -1201,13 +1724,22 @@
1317 	 *
1318 	 */
1319 	private String getTrimmedSelection(){
1320-		String currentSelection = (localFileTable.getSelectedValue().toString()).substring(1);
1321-				if(currentSelection.substring(0,1).equals("[") &&
1322-				currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){
1323-				return currentSelection.substring(1,currentSelection.length()-1);
1324-				} else {
1325-					return currentSelection;
1326-				}
1327+		String currentSelection = "";
1328+		Object selected = localFileTable.getSelectedValue();
1329+		if (selected == null) {
1330+			return currentSelection;
1331+		}
1332+		String selstr = selected.toString();
1333+		if (selstr == null) {
1334+			return currentSelection;
1335+		}
1336+		currentSelection = selstr.substring(1);
1337+		if(currentSelection.substring(0,1).equals("[") &&
1338+			currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){
1339+			return currentSelection.substring(1,currentSelection.length()-1);
1340+		} else {
1341+			return currentSelection;
1342+		}
1343 	}
1344
1345 	/*
1346@@ -1241,36 +1773,148 @@
1347 	 	return null;
1348 	}
1349
1350+	String timeStr(long t) {
1351+		Date date = new Date(t);
1352+		return date.toString();
1353+	}
1354+	String dotPast(double f, int n) {
1355+		String fs = "" + f;
1356+		int i = fs.lastIndexOf(".") + n;
1357+		if (i >= 0) {
1358+			int len = fs.length();
1359+			if (i >= len) {
1360+				i = len-1;
1361+			}
1362+			fs = fs.substring(0, i);
1363+		}
1364+		return fs;
1365+	}
1366+	String sizeStr(int s) {
1367+		if (s < 0) {
1368+			return s + "? B";
1369+		} else if (s < 1024) {
1370+			return s + " B";
1371+		} else if (s < 1024 * 1024) {
1372+			double k = s / 1024.0;
1373+			String ks = dotPast(k, 3);
1374+
1375+			return s + " (" + ks + " KB)";
1376+		} else {
1377+			double m = s / (1024.0*1024.0);
1378+			String ms = dotPast(m, 3);
1379+			return s + " (" + ms + " MB)";
1380+		}
1381+	}
1382+
1383+	int max_char(String text) {
1384+		int maxc = 0;
1385+		char chars[] = text.toCharArray();
1386+		for (int n = 0; n < chars.length; n++) {
1387+			if ((int) chars[n] > maxc) {
1388+				maxc = (int) chars[n];
1389+			}
1390+		}
1391+		return maxc;
1392+	}
1393
1394 	/*
1395 	 * Navigates the local file structure up or down one directory
1396 	 */
1397 	public void changeLocalDirectory(File dir)
1398 	{
1399-			currentLocalDirectory = dir;	// Updates Global
1400+			dir = saveLocalHack(dir);	// runge/x11vnc
1401+
1402+			if (dir == null) {
1403+				connectionStatus.setText("Error changing local directory.");
1404+				historyComboBox.insertItemAt(new String("> Error changing local directory."), 0);
1405+				historyComboBox.setSelectedIndex(0);
1406+				return;
1407+			}
1408+
1409 			File allFiles[] = dir.listFiles();	// Reads files
1410 			String[] contents = dir.list();
1411
1412+			if (contents == null || allFiles == null) {
1413+				connectionStatus.setText("Error changing local directory.");
1414+				historyComboBox.insertItemAt(new String("> Error changing local directory."), 0);
1415+				historyComboBox.setSelectedIndex(0);
1416+				return;
1417+			}
1418+
1419+			currentLocalDirectory = dir;	// Updates Global
1420+// begin runge/x11vnc
1421+System.out.println("changeLocalDirectory: " + dir.toString());
1422+			if (contents != null) {
1423+				java.util.Arrays.sort(contents, String.CASE_INSENSITIVE_ORDER);
1424+				for (int i = 0; i < contents.length; i++) {
1425+					allFiles[i] = new File(dir, contents[i]);
1426+				}
1427+			} else {
1428+				return;
1429+			}
1430+// end runge/x11vnc
1431+
1432 			localList.clear();
1433+			localListInfo.clear();
1434 			localList.addElement(" [..]");
1435+			localListInfo.addElement(" [..]");
1436+
1437+			ArrayList DirInfo = new ArrayList();
1438+			ArrayList FilInfo = new ArrayList();
1439+
1440+			Charset charset = Charset.forName("ISO-8859-1");
1441+			CharsetDecoder decoder = charset.newDecoder();
1442+			CharsetEncoder encoder = charset.newEncoder();
1443
1444 			// Populate the Lists
1445 			for (int i = 0; i < contents.length; i++)
1446 			{
1447-				if (allFiles[i].isDirectory())
1448+				String f1 = contents[i];
1449+
1450+if (false) {
1451+
1452+System.out.println("max_char: " + max_char(f1) + " "  + f1);
1453+				if (max_char(f1) > 255) {
1454+					try {
1455+System.out.println("bbuf1");
1456+						ByteBuffer bbuf = encoder.encode(CharBuffer.wrap(f1.toCharArray()));
1457+System.out.println("bbuf2");
1458+						CharBuffer cbuf = decoder.decode(bbuf);
1459+System.out.println("bbuf3");
1460+						f1 = cbuf.toString();
1461+System.out.println("did bbuf: " + f1);
1462+					} catch (Exception e) {
1463+						;
1464+					}
1465+				}
1466+}
1467+
1468+				String f2 = f1;
1469+				if (f2.length() < 24) {
1470+					for (int ik = f2.length(); ik < 24; ik++) {
1471+						f2 = f2 + " ";
1472+					}
1473+				}
1474+				String s = f2 + "    \tLastmod: " + timeStr(allFiles[i].lastModified()) + "    \t\tSize: " + sizeStr((int) allFiles[i].length());
1475+				if (allFiles[i].isDirectory()) {
1476 					// localList.addElement("[" + contents[i] + "]");
1477-					DirsList.add(" [" + contents[i] + "]"); // sf@2004
1478-				else
1479-				{
1480+					DirsList.add(" [" + f1 + "]"); // sf@2004
1481+					DirInfo.add(s);
1482+				} else {
1483 					// localList.addElement(contents[i]);
1484-					FilesList.add(" " + contents[i]); // sf@2004
1485+					FilesList.add(" " + f1); // sf@2004
1486+					FilInfo.add(s);
1487 				}
1488 			}
1489 			// sf@2004
1490-			for (int i = 0; i < DirsList.size(); i++)
1491+			for (int i = 0; i < DirsList.size(); i++) {
1492 				localList.addElement(DirsList.get(i));
1493-			for (int i = 0; i < FilesList.size(); i++)
1494+				localListInfo.addElement(DirInfo.get(i));
1495+			}
1496+			for (int i = 0; i < FilesList.size(); i++) {
1497 				localList.addElement(FilesList.get(i));
1498+				localListInfo.addElement(FilInfo.get(i));
1499+			}
1500
1501 			FilesList.clear();
1502 			DirsList.clear();
1503@@ -1296,3 +1940,147 @@
1504 	}
1505
1506 } //  @jve:visual-info  decl-index=0 visual-constraint="10,10"
1507+
1508+// begin runge/x11vnc
1509+class TextViewer extends JFrame implements ActionListener {
1510+
1511+	JTextArea textArea = new JTextArea(35, 80);
1512+	File file = null;
1513+	JButton refreshButton;
1514+	JButton dismissButton;
1515+	Timer tim = null;
1516+	int rcnt = 0;
1517+	int tms = 250;
1518+	boolean delete_it = false;
1519+	TextViewer me;
1520+
1521+	public TextViewer(String s, File f, boolean d) {
1522+
1523+		delete_it = d;
1524+		file = f;
1525+		me = this;
1526+
1527+		JScrollPane scrollPane = new JScrollPane(textArea,
1528+		    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
1529+		    JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
1530+
1531+		textArea.setEditable(false);
1532+		textArea.setFont(new Font("Monospaced", Font.PLAIN, 12));
1533+
1534+		KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, InputEvent.SHIFT_MASK);
1535+		AbstractAction escapeAction = new AbstractAction() {
1536+			public void actionPerformed(ActionEvent actionEvent) {
1537+				cleanse();
1538+				me.dispose();
1539+			}
1540+		};
1541+		textArea.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction");
1542+		textArea.getInputMap().put(stroke, "escapeAction");
1543+		textArea.getActionMap().put("escapeAction", escapeAction);
1544+
1545+		refreshButton = new JButton();
1546+		refreshButton.setText("Reload");
1547+		refreshButton.setName("refreshButton");
1548+		refreshButton.addActionListener(this);
1549+
1550+		dismissButton = new JButton();
1551+		dismissButton.setText("Dismiss");
1552+		dismissButton.setName("dismissButton");
1553+		dismissButton.addActionListener(this);
1554+
1555+		JPanel buttons = new JPanel();
1556+		buttons.setLayout(new BorderLayout());
1557+		buttons.add(refreshButton, BorderLayout.WEST);
1558+		buttons.add(dismissButton, BorderLayout.EAST);
1559+
1560+		JPanel content = new JPanel();
1561+		content.setLayout(new BorderLayout());
1562+		content.add(scrollPane, BorderLayout.CENTER);
1563+		content.add(buttons, BorderLayout.SOUTH);
1564+
1565+		ActionListener tsk = new ActionListener() {
1566+			public void actionPerformed(ActionEvent evt) {
1567+				// System.out.println("tsk");
1568+				refresh();
1569+			}
1570+		};
1571+		tim = new Timer(tms, tsk);
1572+		tim.start();
1573+
1574+		this.setContentPane(content);
1575+		this.setTitle("TextViewer - " + s);
1576+		this.pack();
1577+		this.setVisible(true);
1578+	}
1579+
1580+	private void refresh() {
1581+
1582+		rcnt++;
1583+		if (rcnt * tms > 3000 && tim != null) {
1584+			tim.stop();
1585+			tim = null;
1586+		}
1587+		BufferedReader input = null;
1588+		StringBuffer contents = new StringBuffer();
1589+		try {
1590+			if (input == null) {
1591+				input = new BufferedReader(new FileReader(file));
1592+			}
1593+			String line = null;
1594+			int i = 0;
1595+			while (( line = input.readLine()) != null) {
1596+				if (i == 0) {
1597+					// System.out.println("read");
1598+				}
1599+				i++;
1600+				contents.append(line);
1601+				contents.append(System.getProperty("line.separator"));
1602+			}
1603+		} catch (Exception e) {
1604+			;
1605+		} finally {
1606+			try {
1607+				if (input != null) {
1608+					input.close();
1609+					input = null;
1610+				}
1611+			} catch (Exception e) {
1612+				;
1613+			}
1614+		}
1615+
1616+		textArea.setText(contents.toString());
1617+		textArea.setCaretPosition(0);
1618+	}
1619+
1620+	public void actionPerformed(ActionEvent evt) {
1621+
1622+		if (evt.getSource() == refreshButton) {
1623+			refresh();
1624+		}
1625+		if (evt.getSource() == dismissButton) {
1626+			cleanse();
1627+			this.dispose();
1628+		}
1629+	}
1630+
1631+	private void cleanse() {
1632+		if (delete_it && file != null) {
1633+			try {
1634+				file.delete();
1635+				file = null;
1636+			} catch (Exception e) {
1637+				;
1638+			}
1639+		}
1640+	}
1641+
1642+	protected void finalize() throws Throwable {
1643+		try {
1644+			cleanse();
1645+		} finally {
1646+			super.finalize();
1647+		}
1648+	}
1649+}
1650+// end runge/x11vnc
1651diff -Naur JavaViewer.orig/Makefile JavaViewer/Makefile
1652--- JavaViewer.orig/Makefile	2006-05-29 09:06:32.000000000 -0400
1653+++ JavaViewer/Makefile	2010-05-18 20:53:32.000000000 -0400
1654@@ -4,6 +4,7 @@
1655
1656 CP = cp
1657 JC = javac
1658+JC_ARGS = -target 1.4 -source 1.4
1659 JAR = jar
1660 ARCHIVE = VncViewer.jar
1661 PAGES = index.vnc shared.vnc noshared.vnc hextile.vnc zlib.vnc tight.vnc
1662@@ -20,7 +21,7 @@
1663 all: $(CLASSES) $(ARCHIVE)
1664
1665 $(CLASSES): $(SOURCES)
1666-	$(JC) -O $(SOURCES)
1667+	$(JC) $(JC_ARGS) -O $(SOURCES)
1668
1669 $(ARCHIVE): $(CLASSES)
1670 	$(JAR) cf $(ARCHIVE) $(CLASSES)
1671diff -Naur JavaViewer.orig/OptionsFrame.java JavaViewer/OptionsFrame.java
1672--- JavaViewer.orig/OptionsFrame.java	2005-11-21 18:50:16.000000000 -0500
1673+++ JavaViewer/OptionsFrame.java	2007-05-13 22:18:30.000000000 -0400
1674@@ -144,7 +144,10 @@
1675     choices[jpegQualityIndex].select("6");
1676     choices[cursorUpdatesIndex].select("Enable");
1677     choices[useCopyRectIndex].select("Yes");
1678-    choices[eightBitColorsIndex].select("64");
1679+// begin runge/x11vnc
1680+//    choices[eightBitColorsIndex].select("64");
1681+    choices[eightBitColorsIndex].select("Full");
1682+// end runge/x11vnc
1683     choices[mouseButtonIndex].select("Normal");
1684     choices[viewOnlyIndex].select("No");
1685     choices[shareDesktopIndex].select("Yes");
1686diff -Naur JavaViewer.orig/RfbProto.java JavaViewer/RfbProto.java
1687--- JavaViewer.orig/RfbProto.java	2006-05-24 15:14:40.000000000 -0400
1688+++ JavaViewer/RfbProto.java	2010-11-30 22:13:58.000000000 -0500
1689@@ -31,6 +31,7 @@
1690 import java.net.Socket;
1691 import java.util.*;
1692 import java.util.zip.*;
1693+import java.text.DateFormat;
1694
1695
1696 class RfbProto {
1697@@ -86,8 +87,11 @@
1698
1699 	// sf@2004 - FileTransfer part
1700 	ArrayList remoteDirsList;
1701+	ArrayList remoteDirsListInfo;
1702 	ArrayList remoteFilesList;
1703+	ArrayList remoteFilesListInfo;
1704 	ArrayList a;
1705+	ArrayList b;
1706 	boolean fFTInit = true; // sf@2004
1707 	boolean fFTAllowed = true;
1708 	boolean fAbort = false;
1709@@ -199,6 +203,10 @@
1710 	// playback.
1711 	int numUpdatesInSession;
1712
1713+// begin runge/x11vnc
1714+	int readServerDriveListCnt = -1;
1715+	long readServerDriveListTime = 0;
1716+// end   runge/x11vnc
1717 	//
1718 	// Constructor. Make TCP connection to RFB server.
1719 	//
1720@@ -207,7 +215,27 @@
1721 		viewer = v;
1722 		host = h;
1723 		port = p;
1724-		sock = new Socket(host, port);
1725+// begin runge/x11vnc
1726+//		sock = new Socket(host, port);
1727+    if (! viewer.disableSSL) {
1728+       System.out.println("new SSLSocketToMe");
1729+       SSLSocketToMe ssl;
1730+       try {
1731+               ssl = new SSLSocketToMe(host, port, v);
1732+       } catch (Exception e) {
1733+               throw new IOException(e.getMessage());
1734+       }
1735+
1736+       try {
1737+               sock = ssl.connectSock();
1738+       } catch (Exception es) {
1739+               throw new IOException(es.getMessage());
1740+       }
1741+    } else {
1742+       sock = new Socket(host, port);
1743+    }
1744+// end runge/x11vnc
1745+
1746 		is =
1747 			new DataInputStream(
1748 				new BufferedInputStream(sock.getInputStream(), 16384));
1749@@ -215,9 +243,12 @@
1750 		osw = new OutputStreamWriter(sock.getOutputStream());
1751 		inDirectory2 = false;
1752 		a = new ArrayList();
1753+		b = new ArrayList();
1754 		// sf@2004
1755 		remoteDirsList = new ArrayList();
1756+		remoteDirsListInfo = new ArrayList();
1757 		remoteFilesList = new ArrayList();
1758+		remoteFilesListInfo = new ArrayList();
1759
1760 		sendFileSource = "";
1761 	}
1762@@ -420,7 +451,13 @@
1763 	//
1764
1765 	int readServerMessageType() throws IOException {
1766-		int msgType = is.readUnsignedByte();
1767+		int msgType;
1768+		try {
1769+			msgType = is.readUnsignedByte();
1770+		} catch (Exception e) {
1771+			viewer.disconnect();
1772+			return -1;
1773+		}
1774
1775 		// If the session is being recorded:
1776 		if (rec != null) {
1777@@ -600,6 +637,7 @@
1778 		contentParamT = is.readUnsignedByte();
1779 		contentParamT = contentParamT << 8;
1780 		contentParam = contentParam | contentParamT;
1781+//System.out.println("FTM: contentType " + contentType + " contentParam " + contentParam);
1782 		if (contentType == rfbRDrivesList || contentType == rfbDirPacket)
1783 		{
1784 			readDriveOrDirectory(contentParam);
1785@@ -610,7 +648,7 @@
1786 		}
1787 		else if (contentType == rfbFilePacket)
1788 		{
1789-				receiveFileChunk();
1790+			receiveFileChunk();
1791 		}
1792 		else if (contentType == rfbEndOfFile)
1793 		{
1794@@ -618,6 +656,10 @@
1795 		}
1796 		else if (contentType == rfbAbortFileTransfer)
1797 		{
1798+			System.out.println("rfbAbortFileTransfer: fFileReceptionRunning="
1799+			    + fFileReceptionRunning + " fAbort="
1800+			    + fAbort + " fFileReceptionError="
1801+			    + fFileReceptionError);
1802 			if (fFileReceptionRunning)
1803 			{
1804 				endOfReceiveFile(false); // Error
1805@@ -626,6 +668,11 @@
1806 			{
1807 				// sf@2004 - Todo: Add TestPermission
1808 				// System.out.println("File Transfer Aborted!");
1809+
1810+				// runge: seems like we must at least read the remaining
1811+				// 8 bytes of the header, right?
1812+				int size = is.readInt();
1813+				int length = is.readInt();
1814 			}
1815
1816 		}
1817@@ -645,6 +692,7 @@
1818 		{
1819 			System.out.println("ContentType: " + contentType);
1820 		}
1821+//System.out.println("FTM: done");
1822 	}
1823
1824 	//Refactored from readRfbFileTransferMsg()
1825@@ -662,6 +710,7 @@
1826
1827 	//Refactored from readRfbFileTransferMsg()
1828 	public void readDriveOrDirectory(int contentParam) throws IOException {
1829+//System.out.println("RDOD: " + contentParam + " " + inDirectory2);
1830 		if (contentParam == rfbADrivesList)
1831 		{
1832 			readFTPMsgDriveList();
1833@@ -688,13 +737,21 @@
1834
1835 	// Internally used. Write an Rfb message to the server
1836 	void writeRfbFileTransferMsg(
1837-								int contentType,
1838-								int contentParam,
1839-								long size, // 0 : compression not supported - 1 : compression supported
1840-								long length,
1841-								String text) throws IOException
1842+		int contentType,
1843+		int contentParam,
1844+		long size, // 0 : compression not supported - 1 : compression supported
1845+		long length,
1846+		String text) throws IOException
1847 	{
1848 		byte b[] = new byte[12];
1849+		byte byteArray[];
1850+
1851+		if (viewer.dsmActive) {
1852+			// need to send the rfbFileTransfer msg type twice for the plugin...
1853+			byte b2[] = new byte[1];
1854+			b2[0] = (byte) rfbFileTransfer;
1855+			os.write(b2);
1856+		}
1857
1858 		b[0] = (byte) rfbFileTransfer;
1859 		b[1] = (byte) contentType;
1860@@ -702,7 +759,7 @@
1861
1862 		byte by = 0;
1863 		long c = 0;
1864-		length++;
1865+
1866 		c = size & 0xFF000000;
1867 		by = (byte) (c >>> 24);
1868 		b[4] = by;
1869@@ -716,6 +773,32 @@
1870 		by = (byte) c;
1871 		b[7] = by;
1872
1873+		if (text != null) {
1874+			byte byteArray0[] = text.getBytes();
1875+			int maxc = max_char(text);
1876+			if (maxc > 255) {
1877+				System.out.println("writeRfbFileTransferMsg: using getBytes(\"UTF-8\")");
1878+				byteArray0 = text.getBytes("UTF-8");
1879+			} else if (maxc > 127) {
1880+				System.out.println("writeRfbFileTransferMsg: using getBytes(\"ISO-8859-1\")");
1881+				byteArray0 = text.getBytes("ISO-8859-1");
1882+			}
1883+			byteArray = new byte[byteArray0.length + 1];
1884+			for (int i = 0; i < byteArray0.length; i++) {
1885+				byteArray[i] = byteArray0[i];
1886+			}
1887+			byteArray[byteArray.length - 1] = 0;
1888+System.out.println("writeRfbFileTransferMsg: length: " + length + " -> byteArray.length: " + byteArray.length);
1889+
1890+			// will equal length for ascii, ISO-8859-1, more for UTF-8
1891+			length = byteArray.length;
1892+
1893+			//length++;	// used to not include null byte at end.
1894+		} else {
1895+			String moo = "moo";
1896+			byteArray = moo.getBytes();
1897+		}
1898+
1899 		c = length & 0xFF000000;
1900 		by = (byte) (c >>> 24);
1901 		b[8] = by;
1902@@ -729,29 +812,91 @@
1903 		by = (byte) c;
1904 		b[11] = by;
1905 		os.write(b);
1906+
1907+//System.out.println("size: " + size + " length: " + length + " text: " + text);
1908
1909
1910 		if (text != null)
1911 		{
1912-			byte byteArray[] = text.getBytes();
1913-			byte byteArray2[] = new byte[byteArray.length + 1];
1914-			for (int i = 0; i < byteArray.length; i++) {
1915-				byteArray2[i] = byteArray[i];
1916+			os.write(byteArray);
1917+		}
1918+	}
1919+
1920+	int max_char(String text) {
1921+		int maxc = 0;
1922+		char chars[] = text.toCharArray();
1923+		for (int n = 0; n < chars.length; n++) {
1924+			if ((int) chars[n] > maxc) {
1925+				maxc = (int) chars[n];
1926 			}
1927-			byteArray2[byteArray2.length - 1] = 0;
1928-			os.write(byteArray2);
1929 		}
1930-
1931+		return maxc;
1932 	}
1933
1934+	String guess_encoding(char[] chars) {
1935+		boolean saw_high_char = false;
1936+
1937+		for (int i = 0; i < chars.length; i++) {
1938+			if (chars[i] == '\0') {
1939+				break;
1940+			}
1941+			if (chars[i] >= 128) {
1942+				saw_high_char = true;
1943+				break;
1944+			}
1945+		}
1946+		if (!saw_high_char) {
1947+			return "ASCII";
1948+		}
1949+		char prev = 1;
1950+		boolean valid_utf8 = true;
1951+		int n = 0;
1952+		for (int i = 0; i < chars.length; i++) {
1953+			if (chars[i] == '\0') {
1954+				break;
1955+			}
1956+			char c = chars[i];
1957+			if (prev < 128 && c >= 128) {
1958+				if (c >> 5 == 0x6) {
1959+					n = 1;
1960+				} else if (c >> 4 == 0xe) {
1961+					n = 2;
1962+				} else if (c >> 3 == 0x1e) {
1963+					n = 3;
1964+				} else if (c >> 2 == 0x3e) {
1965+					n = 4;
1966+				} else {
1967+					valid_utf8 = false;
1968+					break;
1969+				}
1970+			} else {
1971+				if (n > 0) {
1972+					if (c < 128) {
1973+						valid_utf8 = false;
1974+						break;
1975+					}
1976+					n--;
1977+				}
1978+			}
1979+
1980+			prev = c;
1981+		}
1982+		if (valid_utf8) {
1983+			return "UTF-8";
1984+		} else {
1985+			return "ISO-8859-1";
1986+		}
1987+	}
1988+
1989+
1990 	//Internally used. Write an rfb message to the server for sending files ONLY
1991 	int writeRfbFileTransferMsgForSendFile(
1992-											int contentType,
1993-											int contentParam,
1994-											long size,
1995-											long length,
1996-											String source
1997-											) throws IOException
1998+		int contentType,
1999+		int contentParam,
2000+		long size,
2001+		long length,
2002+		String source
2003+		) throws IOException
2004 	{
2005 		File f = new File(source);
2006 		fis = new FileInputStream(f);
2007@@ -768,50 +913,47 @@
2008
2009 		while (bytesRead!=-1)
2010 		{
2011-				counter += bytesRead;
2012-				myDeflater.setInput(byteBuffer, 0, bytesRead);
2013-				myDeflater.finish();
2014-				compressedSize = myDeflater.deflate(CompressionBuffer);
2015-				myDeflater.reset();
2016-				// If the compressed data is larger than the original one, we're dealing with
2017-				// already compressed data
2018-				if (compressedSize > bytesRead)
2019-					fCompress = false;
2020-				this.writeRfbFileTransferMsg(
2021-											contentType,
2022-											contentParam,
2023-											(fCompress ? 1 : 0),
2024-											(fCompress ? compressedSize-1 : bytesRead-1),
2025-											null
2026-											);
2027-				// Todo: Test write error !
2028-				os.write(
2029-						fCompress ? CompressionBuffer : byteBuffer,
2030-						0,
2031-						fCompress ? compressedSize : bytesRead
2032-						);
2033-
2034-				// Todo: test read error !
2035-				bytesRead = fis.read(byteBuffer);
2036-
2037-				// viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes");
2038-				viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length()));
2039-				viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%");
2040-
2041-				if (fAbort == true)
2042-				{
2043-					fAbort = false;
2044-					fError = true;
2045-					break;
2046-				}
2047-				try
2048-				{
2049-			        Thread.sleep(5);
2050-			    }
2051-				catch(InterruptedException e)
2052-				{
2053-			        System.err.println("Interrupted");
2054-			    }
2055+			counter += bytesRead;
2056+			myDeflater.setInput(byteBuffer, 0, bytesRead);
2057+			myDeflater.finish();
2058+			compressedSize = myDeflater.deflate(CompressionBuffer);
2059+			myDeflater.reset();
2060+			// If the compressed data is larger than the original one, we're dealing with
2061+			// already compressed data
2062+			if (compressedSize > bytesRead)
2063+				fCompress = false;
2064+			this.writeRfbFileTransferMsg(
2065+				contentType,
2066+				contentParam,
2067+				(fCompress ? 1 : 0),
2068+// RUNGE				(fCompress ? compressedSize-1 : bytesRead-1),
2069+				(fCompress ? compressedSize : bytesRead),
2070+				null
2071+			);
2072+			// Todo: Test write error !
2073+			os.write(fCompress ? CompressionBuffer : byteBuffer, 0, fCompress ? compressedSize : bytesRead);
2074+
2075+			// Todo: test read error !
2076+			bytesRead = fis.read(byteBuffer);
2077+
2078+			// viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes");
2079+			viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length()));
2080+			viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%");
2081+
2082+			if (fAbort == true)
2083+			{
2084+				fAbort = false;
2085+				fError = true;
2086+				break;
2087+			}
2088+			try
2089+			{
2090+		        Thread.sleep(5);
2091+		    }
2092+			catch(InterruptedException e)
2093+			{
2094+		        System.err.println("Interrupted");
2095+		    }
2096 		}
2097
2098 		writeRfbFileTransferMsg(fError ? rfbAbortFileTransfer : rfbEndOfFile, 0, 0, 0, null);
2099@@ -831,24 +973,30 @@
2100 			{
2101 				System.out.print((char) is.readUnsignedByte());
2102 			}
2103+			System.out.println("");
2104+
2105+			if (size == rfbRErrorCmd || size == -1) {
2106+				viewer.ftp.enableButtons();
2107+				viewer.ftp.connectionStatus.setText("Remote file not available for writing.");
2108+				viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for writing."), 0);
2109+				viewer.ftp.historyComboBox.setSelectedIndex(0);
2110+				return;
2111+			}
2112
2113-			int ret = writeRfbFileTransferMsgForSendFile(
2114-															rfbFilePacket,
2115-															0,
2116-															0,
2117-															0,
2118-															sendFileSource);
2119+			int ret = writeRfbFileTransferMsgForSendFile(rfbFilePacket, 0, 0, 0, sendFileSource);
2120
2121 			viewer.ftp.refreshRemoteLocation();
2122 			if (ret != 1)
2123 			{
2124 				viewer.ftp.connectionStatus.setText(" > Error - File NOT sent");
2125-				viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource) + "> was not correctly sent (aborted by user or error)",0);
2126+				viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource)
2127+				    + "> was not correctly sent (aborted or error). Data may still be buffered/in transit. Wait for remote listing...",0);
2128 			}
2129 			else
2130 			{
2131 				viewer.ftp.connectionStatus.setText(" > File sent");
2132-				viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource) + "> was sent to Remote Machine",0);
2133+				viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource)
2134+				    + "> was sent to Remote Machine. Note: data may still be buffered/in transit. Wait for remote listing...",0);
2135 			}
2136 			viewer.ftp.historyComboBox.setSelectedIndex(0);
2137 			viewer.ftp.enableButtons();
2138@@ -907,7 +1055,7 @@
2139 	//Handles acknowledgement that the file has been deleted on the server
2140 	void deleteRemoteFileFeedback() throws IOException
2141 	{
2142-		is.readInt();
2143+		int ret = is.readInt();
2144 		int length = is.readInt();
2145 		String f = "";
2146 		for (int i = 0; i < length; i++)
2147@@ -916,7 +1064,11 @@
2148 		}
2149
2150 		viewer.ftp.refreshRemoteLocation();
2151-		viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0);
2152+		if (ret == -1) {
2153+			viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Delete File On Remote Machine: "),0);
2154+		} else {
2155+			viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0);
2156+		}
2157 		viewer.ftp.historyComboBox.setSelectedIndex(0);
2158 	}
2159
2160@@ -926,12 +1078,7 @@
2161 		try
2162 		{
2163 			String temp = text;
2164-			writeRfbFileTransferMsg(
2165-									rfbCommand,
2166-									rfbCFileDelete,
2167-									0,
2168-									temp.length(),
2169-									temp);
2170+			writeRfbFileTransferMsg(rfbCommand, rfbCFileDelete, 0, temp.length(), temp);
2171 		}
2172 		catch (IOException e)
2173 		{
2174@@ -943,7 +1090,7 @@
2175 	// Handles acknowledgement that the directory has been created on the server
2176 	void createRemoteDirectoryFeedback() throws IOException
2177 	{
2178-		is.readInt();
2179+		int ret = is.readInt();
2180 		int length = is.readInt();
2181 		String f="";
2182 		for (int i = 0; i < length; i++)
2183@@ -951,7 +1098,11 @@
2184 			f += (char)is.readUnsignedByte();
2185 		}
2186 		viewer.ftp.refreshRemoteLocation();
2187-		viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0);
2188+		if (ret == -1) {
2189+			viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Create Directory on Remote Machine."),0);
2190+		} else {
2191+			viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0);
2192+		}
2193 		viewer.ftp.historyComboBox.setSelectedIndex(0);
2194 	}
2195
2196@@ -961,12 +1112,7 @@
2197 		try
2198 		{
2199 			String temp = text;
2200-			writeRfbFileTransferMsg(
2201-				rfbCommand,
2202-				rfbCDirCreate,
2203-				0,
2204-				temp.length(),
2205-				temp);
2206+			writeRfbFileTransferMsg(rfbCommand, rfbCDirCreate, 0, temp.length(), temp);
2207 		}
2208 		catch (IOException e)
2209 		{
2210@@ -979,15 +1125,13 @@
2211 	{
2212 		try
2213 		{
2214+//System.out.println("requestRemoteFile text: " + text);
2215+//System.out.println("requestRemoteFile leng: " + text.length());
2216 			String temp = text;
2217 			receivePath = localPath;
2218
2219-			writeRfbFileTransferMsg(
2220-									rfbFileTransferRequest,
2221-									0,
2222-									1, // 0 : compression not supported - 1 : compression supported
2223-									temp.length(),
2224-									temp);
2225+			// 0 : compression not supported - 1 : compression supported
2226+			writeRfbFileTransferMsg(rfbFileTransferRequest, 0, 1, temp.length(), temp);
2227 		}
2228 		catch (IOException e)
2229 		{
2230@@ -1004,6 +1148,9 @@
2231 		viewer.ftp.disableButtons();
2232 		int size = is.readInt();
2233 		int length = is.readInt();
2234+
2235+//System.out.println("receiveFileHeader size: " + size);
2236+//System.out.println("receiveFileHeader leng: " + length);
2237
2238 		String tempName = "";
2239 		for (int i = 0; i < length; i++)
2240@@ -1011,6 +1158,15 @@
2241 			tempName += (char) is.readUnsignedByte();
2242 		}
2243
2244+		if (size == rfbRErrorCmd || size == -1) {
2245+			fFileReceptionRunning = false;
2246+			viewer.ftp.enableButtons();
2247+			viewer.ftp.connectionStatus.setText("Remote file not available for reading.");
2248+			viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for reading."), 0);
2249+			viewer.ftp.historyComboBox.setSelectedIndex(0);
2250+			return;
2251+		}
2252+
2253 		// sf@2004 - Read the high part of file size (not yet in rfbFileTransferMsg for
2254 		// backward compatibility reasons...)
2255 		int sizeH = is.readInt();
2256@@ -1021,7 +1177,16 @@
2257 		fileSize=0;
2258 		fileChunkCounter = 0;
2259 		String fileName = receivePath;
2260-		fos = new FileOutputStream(fileName);
2261+		try {
2262+			fos = new FileOutputStream(fileName);
2263+		} catch (Exception e) {
2264+			fFileReceptionRunning = false;
2265+			writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null);
2266+			viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR opening Local File: <" + fileName ),0);
2267+			viewer.ftp.historyComboBox.setSelectedIndex(0);
2268+			viewer.ftp.enableButtons();
2269+			return;
2270+		}
2271 		writeRfbFileTransferMsg(rfbFileHeader, 0, 0, 0, null);
2272 	}
2273
2274@@ -1085,7 +1250,13 @@
2275 			fAbort = false;
2276 			fFileReceptionError = true;
2277 			writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null);
2278-
2279+
2280+			//runge for use with x11vnc/libvncserver, no rfbAbortFileTransfer reply sent.
2281+		        try {Thread.sleep(500);} catch (InterruptedException e) {}
2282+			viewer.ftp.enableButtons();
2283+			viewer.ftp.refreshLocalLocation();
2284+			viewer.ftp.connectionStatus.setText(" > Error - File NOT received");
2285+			viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + receivePath + "> not correctly received from Remote Machine (aborted by user or error)") ,0);
2286 		}
2287 		// sf@2004 - For old FT protocole only
2288 		/*
2289@@ -1104,7 +1275,7 @@
2290 		int length = is.readInt();
2291 		fileSize=0;
2292 		fos.close();
2293-
2294+
2295 		viewer.ftp.refreshLocalLocation();
2296 		if (fReceptionOk && !fFileReceptionError)
2297 		{
2298@@ -1132,12 +1303,7 @@
2299 		try
2300 		{
2301 			String temp = text;
2302-			writeRfbFileTransferMsg(
2303-									rfbDirContentRequest,
2304-									rfbRDirContent,
2305-									0,
2306-									temp.length(),
2307-									temp);
2308+			writeRfbFileTransferMsg(rfbDirContentRequest, rfbRDirContent, 0, temp.length(), temp);
2309 		}
2310 		catch (IOException e)
2311 		{
2312@@ -1197,11 +1363,80 @@
2313 					str += temp;
2314 				}
2315 			}
2316+			// runge
2317+			viewer.ftp.receivedRemoteDirectoryName(str);
2318 			// viewer.ftp.changeRemoteDirectory(str);
2319
2320 		}
2321 	}
2322
2323+	int zogswap(int n) {
2324+		long l = n;
2325+		if (l < 0) {
2326+			l += 0x100000000L;
2327+		}
2328+		l = l & 0xFFFFFFFF;
2329+		l = (l >> 24) | ((l & 0x00ff0000) >> 8) | ((l & 0x0000ff00) << 8) | (l << 24);
2330+		return (int) l;
2331+	}
2332+
2333+	int windozeToUnix(int L, int H) {
2334+		long L2 = zogswap(L);
2335+		long H2 = zogswap(H);
2336+		long unix = (H2 << 32) + L2;
2337+		unix -= 11644473600L * 10000000L;
2338+		unix /= 10000000L;
2339+		//System.out.println("unix time: " + unix + " H2: " + H2 + " L2: " + L2);
2340+		return (int) unix;
2341+	}
2342+
2343+	String timeStr(int t, int h) {
2344+		if (h == 0) {
2345+			// x11vnc/libvncserver unix
2346+			t = zogswap(t);
2347+		} else {
2348+			// ultra (except if h==0 by chance)
2349+			t = windozeToUnix(t, h);
2350+		}
2351+		long tl = (long) t;
2352+		Date date = new Date(tl * 1000);
2353+		if (true) {
2354+			return date.toString();
2355+		} else {
2356+			return DateFormat.getDateTimeInstance().format(date);
2357+		}
2358+	}
2359+
2360+	String dotPast(double f, int n) {
2361+		String fs = "" + f;
2362+		int i = fs.lastIndexOf(".") + n;
2363+		if (i >= 0) {
2364+			int len = fs.length();
2365+			if (i >= len) {
2366+				i = len-1;
2367+			}
2368+			fs = fs.substring(0, i);
2369+		}
2370+		return fs;
2371+	}
2372+	String sizeStr(int s) {
2373+		s = zogswap(s);
2374+		if (s < 0) {
2375+			return s + "? B";
2376+		} else if (s < 1024) {
2377+			return s + " B";
2378+		} else if (s < 1024 * 1024) {
2379+			double k = s / 1024.0;
2380+			String ks = dotPast(k, 3);
2381+
2382+			return s + " (" + ks + " KB)";
2383+		} else {
2384+			double m = s / (1024.0*1024.0);
2385+			String ms = dotPast(m, 3);
2386+			return s + " (" + ms + " MB)";
2387+		}
2388+	}
2389+
2390 	//Internally used to receive directory content from server
2391 	//Here, the server sends one file/directory with it's attributes
2392 	void readFTPMsgDirectoryListContent() throws IOException
2393@@ -1217,17 +1452,32 @@
2394 			dwReserved0,
2395 			dwReserved1;
2396 		long ftCreationTime, ftLastAccessTime, ftLastWriteTime;
2397+		int ftCreationTimeL, ftLastAccessTimeL, ftLastWriteTimeL;
2398+		int ftCreationTimeH, ftLastAccessTimeH, ftLastWriteTimeH;
2399 		char cFileName, cAlternateFileName;
2400 		int length = 0;
2401 		is.readInt();
2402 		length = is.readInt();
2403+
2404+		char[] chars = new char[4*length];
2405+		int char_cnt = 0;
2406+		for (int i = 0; i < chars.length; i++) {
2407+			chars[i] = '\0';
2408+		}
2409+
2410 		dwFileAttributes = is.readInt();
2411 		length -= 4;
2412-		ftCreationTime = is.readLong();
2413+		//ftCreationTime = is.readLong();
2414+		ftCreationTimeL = is.readInt();
2415+		ftCreationTimeH = is.readInt();
2416 		length -= 8;
2417-		ftLastAccessTime = is.readLong();
2418+		//ftLastAccessTime = is.readLong();
2419+		ftLastAccessTimeL = is.readInt();
2420+		ftLastAccessTimeH = is.readInt();
2421 		length -= 8;
2422-		ftLastWriteTime = is.readLong();
2423+		//ftLastWriteTime = is.readLong();
2424+		ftLastWriteTimeL = is.readInt();
2425+		ftLastWriteTimeH = is.readInt();
2426 		length -= 8;
2427 		nFileSizeHigh = is.readInt();
2428 		length -= 4;
2429@@ -1239,10 +1489,12 @@
2430 		length -= 4;
2431 		cFileName = (char) is.readUnsignedByte();
2432 		length--;
2433+		chars[char_cnt++] = cFileName;
2434 		while (cFileName != '\0')
2435 		{
2436 			fileName += cFileName;
2437 			cFileName = (char) is.readUnsignedByte();
2438+			chars[char_cnt++] = cFileName;
2439 			length--;
2440 		}
2441 		cAlternateFileName = (char) is.readByte();
2442@@ -1253,7 +1505,28 @@
2443 			cAlternateFileName = (char) is.readUnsignedByte();
2444 			length--;
2445 		}
2446-		if (dwFileAttributes == 268435456
2447+		String guessed = guess_encoding(chars);
2448+		if (!guessed.equals("ASCII")) {
2449+			System.out.println("guess: " + guessed + "\t" + fileName);
2450+		}
2451+		if (guessed.equals("UTF-8")) {
2452+			try {
2453+				byte[] bytes = new byte[char_cnt-1];
2454+				for (int i=0; i < char_cnt-1; i++) {
2455+					bytes[i] = (byte) chars[i];
2456+				}
2457+				String newstr = new String(bytes, "UTF-8");
2458+				fileName = newstr;
2459+			} catch (Exception e) {
2460+				System.out.println("failed to convert bytes to UTF-8 based string");
2461+			}
2462+		}
2463+		for (int i = 0; i < char_cnt; i++) {
2464+			//System.out.println("char[" + i + "]\t" + (int) chars[i]);
2465+		}
2466+		if (fileName.length() <= 0) {
2467+			;
2468+		} else if (dwFileAttributes == 268435456
2469 			|| dwFileAttributes == 369098752
2470 			|| dwFileAttributes == 285212672
2471 			|| dwFileAttributes == 271056896
2472@@ -1263,11 +1536,74 @@
2473 			|| dwFileAttributes == 369623040)
2474 		{
2475 			fileName = " [" + fileName + "]";
2476-			remoteDirsList.add(fileName); // sf@2004
2477-		}
2478-		else
2479-		{
2480-			remoteFilesList.add(" " + fileName); // sf@2004
2481+// begin runge/x11vnc
2482+//			remoteDirsList.add(fileName); // sf@2004
2483+			int i = -1;
2484+			String t1 = fileName.toLowerCase();
2485+			for (int j = 0; j < remoteDirsList.size(); j++) {
2486+				String t = (String) remoteDirsList.get(j);
2487+				String t2 = t.toLowerCase();
2488+				if (t1.compareTo(t2) < 0) {
2489+					i = j;
2490+					break;
2491+				}
2492+			}
2493+			//String s = "Lastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + "    " + fileName;
2494+			String f2 = fileName;
2495+			if (f2.length() < 24) {
2496+				for (int ik = f2.length(); ik < 24; ik++) {
2497+					f2 = f2 + " ";
2498+				}
2499+			}
2500+			String s = f2 + "    \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + "    \t\tSize: " + sizeStr(nFileSizeLow);
2501+			//s = fileName + " Lastmod: " + zogswap(ftLastWriteTimeL);
2502+			if (i >= 0) {
2503+				remoteDirsList.add(i, fileName);
2504+				remoteDirsListInfo.add(i, s);
2505+			} else {
2506+				remoteDirsList.add(fileName);
2507+				remoteDirsListInfo.add(s);
2508+			}
2509+// end runge/x11vnc
2510+		} else {
2511+// begin runge/x11vnc
2512+//			remoteFilesList.add(" " + fileName); // sf@2004
2513+
2514+			fileName = " " + fileName;
2515+			int i = -1;
2516+			String t1 = fileName.toLowerCase();
2517+			for (int j = 0; j < remoteFilesList.size(); j++) {
2518+				String t = (String) remoteFilesList.get(j);
2519+				String t2 = t.toLowerCase();
2520+				if (t1.compareTo(t2) < 0) {
2521+					i = j;
2522+					break;
2523+				}
2524+			}
2525+			String f2 = fileName;
2526+			if (f2.length() < 24) {
2527+				for (int ik = f2.length(); ik < 24; ik++) {
2528+					f2 = f2 + " ";
2529+				}
2530+			}
2531+
2532+if (false) {
2533+System.out.println("fileName:         " + f2);
2534+System.out.println("ftLastWriteTimeL: " + ftLastWriteTimeL);
2535+System.out.println("ftLastWriteTimeH: " + ftLastWriteTimeH);
2536+System.out.println("nFileSizeLow:     " + nFileSizeLow);
2537+}
2538+
2539+			String s = f2 + "    \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + "    \t\tSize: " + sizeStr(nFileSizeLow);
2540+			//s = fileName + " Lastmod: " + ftLastWriteTimeL + "/" + zogswap(ftLastWriteTimeL) + "  Size: " + nFileSizeLow + "/" + zogswap(nFileSizeLow);
2541+			if (i >= 0) {
2542+				remoteFilesList.add(i, fileName);
2543+				remoteFilesListInfo.add(i, s);
2544+			} else {
2545+				remoteFilesList.add(fileName);
2546+				remoteFilesListInfo.add(s);
2547+			}
2548+// end runge/x11vnc
2549 		}
2550
2551 		// a.add(fileName);
2552@@ -1282,14 +1618,32 @@
2553
2554 		// sf@2004
2555 		a.clear();
2556-		for (int i = 0; i < remoteDirsList.size(); i++)
2557+		b.clear();
2558+		for (int i = 0; i < remoteDirsList.size(); i++) {
2559 			a.add(remoteDirsList.get(i));
2560-		for (int i = 0; i < remoteFilesList.size(); i++)
2561+			b.add(remoteDirsListInfo.get(i));
2562+		}
2563+		for (int i = 0; i < remoteFilesList.size(); i++) {
2564 			a.add(remoteFilesList.get(i));
2565+
2566+			b.add(remoteFilesListInfo.get(i));
2567+		}
2568 		remoteDirsList.clear();
2569+		remoteDirsListInfo.clear();
2570 		remoteFilesList.clear();
2571+		remoteFilesListInfo.clear();
2572
2573-		viewer.ftp.printDirectory(a);
2574+// begin runge/x11vnc
2575+		// Hack for double listing at startup... probably libvncserver bug..
2576+		readServerDriveListCnt++;
2577+		if (readServerDriveListCnt == 2) {
2578+			if (System.currentTimeMillis() - readServerDriveListTime < 2000)  {
2579+//System.out.println("readServerDriveListCnt skip " + readServerDriveListCnt);
2580+				return;
2581+			}
2582+		}
2583+// end runge/x11vnc
2584+		viewer.ftp.printDirectory(a, b);
2585 	}
2586
2587 	//Internally used to signify the drive requested is not ready
2588@@ -1299,6 +1653,8 @@
2589 		System.out.println("Remote Drive unavailable");
2590 		viewer.ftp.connectionStatus.setText(" > WARNING - Remote Drive unavailable (possibly restricted access or media not present)");
2591 		viewer.ftp.remoteStatus.setText("WARNING: Remote Drive unavailable");
2592+		viewer.ftp.historyComboBox.insertItemAt(new String(" > WARNING: Remote Drive unavailable."), 0);
2593+		viewer.ftp.historyComboBox.setSelectedIndex(0);
2594 	}
2595
2596 	//Call this method to request the list of drives on the server.
2597@@ -1306,12 +1662,11 @@
2598 	{
2599 		try
2600 		{
2601-			viewer.rfb.writeRfbFileTransferMsg(
2602-												RfbProto.rfbDirContentRequest,
2603-												RfbProto.rfbRDrivesList,
2604-												0,
2605-												0,
2606-												null);
2607+			viewer.rfb.writeRfbFileTransferMsg(RfbProto.rfbDirContentRequest, RfbProto.rfbRDrivesList, 0, 0, null);
2608+// begin runge/x11vnc
2609+			readServerDriveListCnt = 0;
2610+			readServerDriveListTime = System.currentTimeMillis();
2611+// end   runge/x11vnc
2612 		}
2613 		catch (IOException e)
2614 		{
2615@@ -1355,21 +1710,21 @@
2616 		int h,
2617 		boolean incremental)
2618 		throws IOException {
2619-			if (!viewer.ftp.isVisible()) {
2620-		byte[] b = new byte[10];
2621+		if (!viewer.ftp.isVisible()) {
2622+			byte[] b = new byte[10];
2623
2624-		b[0] = (byte) FramebufferUpdateRequest;
2625-		b[1] = (byte) (incremental ? 1 : 0);
2626-		b[2] = (byte) ((x >> 8) & 0xff);
2627-		b[3] = (byte) (x & 0xff);
2628-		b[4] = (byte) ((y >> 8) & 0xff);
2629-		b[5] = (byte) (y & 0xff);
2630-		b[6] = (byte) ((w >> 8) & 0xff);
2631-		b[7] = (byte) (w & 0xff);
2632-		b[8] = (byte) ((h >> 8) & 0xff);
2633-		b[9] = (byte) (h & 0xff);
2634+			b[0] = (byte) FramebufferUpdateRequest;
2635+			b[1] = (byte) (incremental ? 1 : 0);
2636+			b[2] = (byte) ((x >> 8) & 0xff);
2637+			b[3] = (byte) (x & 0xff);
2638+			b[4] = (byte) ((y >> 8) & 0xff);
2639+			b[5] = (byte) (y & 0xff);
2640+			b[6] = (byte) ((w >> 8) & 0xff);
2641+			b[7] = (byte) (w & 0xff);
2642+			b[8] = (byte) ((h >> 8) & 0xff);
2643+			b[9] = (byte) (h & 0xff);
2644
2645-		os.write(b);
2646+			os.write(b);
2647 		}
2648 	}
2649
2650@@ -1482,7 +1837,13 @@
2651 		b[6] = (byte) ((text.length() >> 8) & 0xff);
2652 		b[7] = (byte) (text.length() & 0xff);
2653
2654-		System.arraycopy(text.getBytes(), 0, b, 8, text.length());
2655+		if (false && max_char(text) > 255) {
2656+			System.arraycopy(text.getBytes("UTF-8"), 0, b, 8, text.length());
2657+		} else if (max_char(text) > 127) {
2658+			System.arraycopy(text.getBytes("ISO-8859-1"), 0, b, 8, text.length());
2659+		} else {
2660+			System.arraycopy(text.getBytes(), 0, b, 8, text.length());
2661+		}
2662
2663 		os.write(b);
2664 	//	}
2665@@ -1506,6 +1867,37 @@
2666 	final static int META_MASK = InputEvent.META_MASK;
2667 	final static int ALT_MASK = InputEvent.ALT_MASK;
2668
2669+	void writeWheelEvent(MouseWheelEvent evt) throws IOException {
2670+		eventBufLen = 0;
2671+
2672+		int x = evt.getX();
2673+		int y = evt.getY();
2674+
2675+		if (x < 0) x = 0;
2676+		if (y < 0) y = 0;
2677+
2678+		int ptrmask;
2679+
2680+		int clicks = evt.getWheelRotation();
2681+		System.out.println("writeWheelEvent: clicks: " + clicks);
2682+		if (clicks > 0) {
2683+			ptrmask = 16;
2684+		} else if (clicks < 0) {
2685+			ptrmask = 8;
2686+		} else {
2687+			return;
2688+		}
2689+
2690+		eventBuf[eventBufLen++] = (byte) PointerEvent;
2691+		eventBuf[eventBufLen++] = (byte) ptrmask;
2692+		eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff);
2693+		eventBuf[eventBufLen++] = (byte) (x & 0xff);
2694+		eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff);
2695+		eventBuf[eventBufLen++] = (byte) (y & 0xff);
2696+
2697+		os.write(eventBuf, 0, eventBufLen);
2698+	}
2699+
2700 	//
2701 	// Write a pointer event message.  We may need to send modifier key events
2702 	// around it to set the correct modifier state.
2703@@ -1610,6 +2002,21 @@
2704
2705 		boolean down = (evt.getID() == KeyEvent.KEY_PRESSED);
2706
2707+		if (viewer.debugKeyboard) {
2708+			System.out.println("----------------------------------------");
2709+			System.out.println("evt.getKeyChar:      " + evt.getKeyChar());
2710+			System.out.println("getKeyText:          " + KeyEvent.getKeyText(evt.getKeyCode()));
2711+			System.out.println("evt.getKeyCode:      " + evt.getKeyCode());
2712+			System.out.println("evt.getID:           " + evt.getID());
2713+			System.out.println("evt.getKeyLocation:  " + evt.getKeyLocation());
2714+			System.out.println("evt.isActionKey:     " + evt.isActionKey());
2715+			System.out.println("evt.isControlDown:   " + evt.isControlDown());
2716+			System.out.println("evt.getModifiers:    " + evt.getModifiers());
2717+			System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers()));
2718+			System.out.println("evt.paramString:     " + evt.paramString());
2719+		}
2720+
2721+
2722 		int key;
2723 		if (evt.isActionKey()) {
2724
2725@@ -1685,6 +2092,9 @@
2726 				default :
2727 					return;
2728 			}
2729+			if (key == 0xffc2 && viewer.mapF5_to_atsign) {
2730+				key = 0x40;
2731+			}
2732
2733 		} else {
2734
2735@@ -1794,6 +2204,16 @@
2736 	int oldModifiers = 0;
2737
2738 	void writeModifierKeyEvents(int newModifiers) {
2739+		if(viewer.forbid_Ctrl_Alt) {
2740+			if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) {
2741+				int orig = newModifiers;
2742+				newModifiers &= ~ALT_MASK;
2743+				newModifiers &= ~CTRL_MASK;
2744+				if (viewer.debugKeyboard) {
2745+					System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers);
2746+				}
2747+			}
2748+		}
2749 		if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK))
2750 			writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0);
2751
2752diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
2753--- JavaViewer.orig/SSLSocketToMe.java	1969-12-31 19:00:00.000000000 -0500
2754+++ JavaViewer/SSLSocketToMe.java	2010-07-10 19:18:06.000000000 -0400
2755@@ -0,0 +1,2067 @@
2756+/*
2757+ * SSLSocketToMe.java: add SSL encryption to Java VNC Viewer.
2758+ *
2759+ * Copyright (c) 2006 Karl J. Runge <runge@karlrunge.com>
2760+ * All rights reserved.
2761+ *
2762+ *  This is free software; you can redistribute it and/or modify
2763+ *  it under the terms of the GNU General Public License as published by
2764+ *  the Free Software Foundation; version 2 of the License, or
2765+ *  (at your option) any later version.
2766+ *
2767+ *  This software is distributed in the hope that it will be useful,
2768+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
2769+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2770+ *  GNU General Public License for more details.
2771+ *
2772+ *  You should have received a copy of the GNU General Public License
2773+ *  along with this software; if not, write to the Free Software
2774+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
2775+ *  USA.
2776+ *
2777+ */
2778+
2779+import java.net.*;
2780+import java.io.*;
2781+import javax.net.ssl.*;
2782+import java.util.*;
2783+
2784+import java.security.*;
2785+import java.security.cert.*;
2786+import java.security.spec.*;
2787+import java.security.cert.Certificate;
2788+import java.security.cert.CertificateFactory;
2789+
2790+import java.awt.*;
2791+import java.awt.event.*;
2792+
2793+public class SSLSocketToMe {
2794+
2795+	/* basic member data: */
2796+	String host;
2797+	int port;
2798+	VncViewer viewer;
2799+
2800+	boolean debug = true;
2801+	boolean debug_certs = false;
2802+
2803+	/* sockets */
2804+	SSLSocket socket = null;
2805+	SSLSocketFactory factory;
2806+
2807+	/* fallback for Proxy connection */
2808+	boolean proxy_in_use = false;
2809+	boolean proxy_failure = false;
2810+	public DataInputStream is = null;
2811+	public OutputStream os = null;
2812+
2813+	/* strings from user WRT proxy: */
2814+	String proxy_auth_string = null;
2815+	String proxy_dialog_host = null;
2816+	int proxy_dialog_port = 0;
2817+
2818+	Socket proxySock;
2819+	DataInputStream proxy_is;
2820+	OutputStream proxy_os;
2821+
2822+	/* trust contexts */
2823+	SSLContext trustloc_ctx;
2824+	SSLContext trustall_ctx;
2825+	SSLContext trustsrv_ctx;
2826+	SSLContext trusturl_ctx;
2827+	SSLContext trustone_ctx;
2828+
2829+	/* corresponding trust managers */
2830+	TrustManager[] trustAllCerts;
2831+	TrustManager[] trustSrvCert;
2832+	TrustManager[] trustUrlCert;
2833+	TrustManager[] trustOneCert;
2834+
2835+	/* client-side SSL auth key (oneTimeKey=...) */
2836+	KeyManager[] mykey = null;
2837+
2838+	boolean user_wants_to_see_cert = true;
2839+	String cert_fail = null;
2840+
2841+	/* cert(s) we retrieve from Web server, VNC server, or serverCert param: */
2842+	java.security.cert.Certificate[] trustallCerts = null;
2843+	java.security.cert.Certificate[] trustsrvCerts = null;
2844+	java.security.cert.Certificate[] trusturlCerts = null;
2845+
2846+	/* utility to decode hex oneTimeKey=... and serverCert=... */
2847+	byte[] hex2bytes(String s) {
2848+		byte[] bytes = new byte[s.length()/2];
2849+		for (int i=0; i<s.length()/2; i++) {
2850+			int j = 2*i;
2851+			try {
2852+				int val = Integer.parseInt(s.substring(j, j+2), 16);
2853+				if (val > 127) {
2854+					val -= 256;
2855+				}
2856+				Integer I = new Integer(val);
2857+				bytes[i] = Byte.decode(I.toString()).byteValue();
2858+
2859+			} catch (Exception e) {
2860+				;
2861+			}
2862+		}
2863+		return bytes;
2864+	}
2865+
2866+	SSLSocketToMe(String h, int p, VncViewer v) throws Exception {
2867+		host = h;
2868+		port = p;
2869+		viewer = v;
2870+
2871+		debug_certs = v.debugCerts;
2872+
2873+		/* we will first try default factory for certification: */
2874+
2875+		factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
2876+
2877+		dbg("SSL startup: " + host + " " + port);
2878+
2879+
2880+		/* create trust managers to be used if initial handshake fails: */
2881+
2882+		trustAllCerts = new TrustManager[] {
2883+		    /*
2884+		     * this one accepts everything.  Only used if user
2885+		     * has disabled checking (trustAllVncCerts=yes)
2886+		     * or when we grab the cert to show it to them in
2887+		     * a dialog and ask them to manually verify/accept it.
2888+		     */
2889+		    new X509TrustManager() {
2890+			public java.security.cert.X509Certificate[]
2891+			    getAcceptedIssuers() {
2892+				return null;
2893+			}
2894+			public void checkClientTrusted(
2895+			    java.security.cert.X509Certificate[] certs,
2896+			    String authType) {
2897+				/* empty */
2898+			}
2899+			public void checkServerTrusted(
2900+			    java.security.cert.X509Certificate[] certs,
2901+			    String authType) {
2902+				/* empty */
2903+				dbg("ALL: an untrusted connect to grab cert.");
2904+			}
2905+		    }
2906+		};
2907+
2908+		trustUrlCert = new TrustManager[] {
2909+		    /*
2910+		     * this one accepts only the retrieved server
2911+		     * cert by SSLSocket by this applet and stored in
2912+		     * trusturlCerts.
2913+		     */
2914+		    new X509TrustManager() {
2915+			public java.security.cert.X509Certificate[]
2916+			    getAcceptedIssuers() {
2917+				return null;
2918+			}
2919+			public void checkClientTrusted(
2920+			    java.security.cert.X509Certificate[] certs,
2921+			    String authType) throws CertificateException {
2922+				throw new CertificateException("No Clients (URL)");
2923+			}
2924+			public void checkServerTrusted(
2925+			    java.security.cert.X509Certificate[] certs,
2926+			    String authType) throws CertificateException {
2927+				/* we want to check 'certs' against 'trusturlCerts' */
2928+				if (trusturlCerts == null) {
2929+					throw new CertificateException(
2930+					    "No Trust url Certs array.");
2931+				}
2932+				if (trusturlCerts.length < 1) {
2933+					throw new CertificateException(
2934+					    "No Trust url Certs.");
2935+				}
2936+				if (certs == null) {
2937+					throw new CertificateException(
2938+					    "No this-certs array.");
2939+				}
2940+				if (certs.length < 1) {
2941+					throw new CertificateException(
2942+					    "No this-certs Certs.");
2943+				}
2944+				if (certs.length != trusturlCerts.length) {
2945+					throw new CertificateException(
2946+					    "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length);
2947+				}
2948+				boolean ok = true;
2949+				for (int i = 0; i < certs.length; i++)  {
2950+					if (! trusturlCerts[i].equals(certs[i])) {
2951+						ok = false;
2952+						dbg("URL: cert mismatch at i=" + i);
2953+						dbg("URL: cert mismatch cert" + certs[i]);
2954+						dbg("URL: cert mismatch  url" + trusturlCerts[i]);
2955+						if (cert_fail == null) {
2956+							cert_fail = "cert-mismatch";
2957+						}
2958+					}
2959+					if (debug_certs) {
2960+						dbg("\n***********************************************");
2961+						dbg("URL: cert info at i=" + i);
2962+						dbg("URL: cert info cert" + certs[i]);
2963+						dbg("===============================================");
2964+						dbg("URL: cert info  url" + trusturlCerts[i]);
2965+						dbg("***********************************************");
2966+					}
2967+				}
2968+				if (!ok) {
2969+					throw new CertificateException(
2970+					    "Server Cert Chain != URL Cert Chain.");
2971+				}
2972+				dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1));
2973+			}
2974+		    }
2975+		};
2976+
2977+		trustSrvCert = new TrustManager[] {
2978+		    /*
2979+		     * this one accepts cert given to us in the serverCert
2980+		     * Applet Parameter we were started with.  It is
2981+		     * currently a fatal error if the VNC Server's cert
2982+		     * doesn't match it.
2983+		     */
2984+		    new X509TrustManager() {
2985+			public java.security.cert.X509Certificate[]
2986+			    getAcceptedIssuers() {
2987+				return null;
2988+			}
2989+			public void checkClientTrusted(
2990+			    java.security.cert.X509Certificate[] certs,
2991+			    String authType) throws CertificateException {
2992+				throw new CertificateException("No Clients (SRV)");
2993+			}
2994+			public void checkServerTrusted(
2995+			    java.security.cert.X509Certificate[] certs,
2996+			    String authType) throws CertificateException {
2997+				/* we want to check 'certs' against 'trustsrvCerts' */
2998+				if (trustsrvCerts == null) {
2999+					throw new CertificateException(
3000+					    "No Trust srv Certs array.");
3001+				}
3002+				if (trustsrvCerts.length < 1) {
3003+					throw new CertificateException(
3004+					    "No Trust srv Certs.");
3005+				}
3006+				if (certs == null) {
3007+					throw new CertificateException(
3008+					    "No this-certs array.");
3009+				}
3010+				if (certs.length < 1) {
3011+					throw new CertificateException(
3012+					    "No this-certs Certs.");
3013+				}
3014+				if (certs.length != trustsrvCerts.length) {
3015+					throw new CertificateException(
3016+					    "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length);
3017+				}
3018+				boolean ok = true;
3019+				for (int i = 0; i < certs.length; i++)  {
3020+					if (! trustsrvCerts[i].equals(certs[i])) {
3021+						ok = false;
3022+						dbg("SRV: cert mismatch at i=" + i);
3023+						dbg("SRV: cert mismatch cert" + certs[i]);
3024+						dbg("SRV: cert mismatch  srv" + trustsrvCerts[i]);
3025+						if (cert_fail == null) {
3026+							cert_fail = "server-cert-mismatch";
3027+						}
3028+					}
3029+					if (debug_certs) {
3030+						dbg("\n***********************************************");
3031+						dbg("SRV: cert info at i=" + i);
3032+						dbg("SRV: cert info cert" + certs[i]);
3033+						dbg("===============================================");
3034+						dbg("SRV: cert info  srv" + trustsrvCerts[i]);
3035+						dbg("***********************************************");
3036+					}
3037+				}
3038+				if (!ok) {
3039+					throw new CertificateException(
3040+					    "Server Cert Chain != serverCert Applet Parameter Cert Chain.");
3041+				}
3042+				dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1));
3043+			}
3044+		    }
3045+		};
3046+
3047+		trustOneCert = new TrustManager[] {
3048+		    /*
3049+		     * this one accepts only the retrieved server
3050+		     * cert by SSLSocket by this applet we stored in
3051+		     * trustallCerts that user has accepted or applet
3052+		     * parameter trustAllVncCerts=yes is set.  This is
3053+		     * for when we reconnect after the user has manually
3054+		     * accepted the trustall cert in the dialog (or set
3055+		     * trustAllVncCerts=yes applet param.)
3056+		     */
3057+		    new X509TrustManager() {
3058+			public java.security.cert.X509Certificate[]
3059+			    getAcceptedIssuers() {
3060+				return null;
3061+			}
3062+			public void checkClientTrusted(
3063+			    java.security.cert.X509Certificate[] certs,
3064+			    String authType) throws CertificateException {
3065+				throw new CertificateException("No Clients (ONE)");
3066+			}
3067+			public void checkServerTrusted(
3068+			    java.security.cert.X509Certificate[] certs,
3069+			    String authType) throws CertificateException {
3070+				/* we want to check 'certs' against 'trustallCerts' */
3071+				if (trustallCerts == null) {
3072+					throw new CertificateException(
3073+					    "No Trust All Server Certs array.");
3074+				}
3075+				if (trustallCerts.length < 1) {
3076+					throw new CertificateException(
3077+					    "No Trust All Server Certs.");
3078+				}
3079+				if (certs == null) {
3080+					throw new CertificateException(
3081+					    "No this-certs array.");
3082+				}
3083+				if (certs.length < 1) {
3084+					throw new CertificateException(
3085+					    "No this-certs Certs.");
3086+				}
3087+				if (certs.length != trustallCerts.length) {
3088+					throw new CertificateException(
3089+					    "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length);
3090+				}
3091+				boolean ok = true;
3092+				for (int i = 0; i < certs.length; i++)  {
3093+					if (! trustallCerts[i].equals(certs[i])) {
3094+						ok = false;
3095+						dbg("ONE: cert mismatch at i=" + i);
3096+						dbg("ONE: cert mismatch cert" + certs[i]);
3097+						dbg("ONE: cert mismatch  all" + trustallCerts[i]);
3098+					}
3099+					if (debug_certs) {
3100+						dbg("\n***********************************************");
3101+						dbg("ONE: cert info at i=" + i);
3102+						dbg("ONE: cert info cert" + certs[i]);
3103+						dbg("===============================================");
3104+						dbg("ONE: cert info  all" + trustallCerts[i]);
3105+						dbg("***********************************************");
3106+					}
3107+				}
3108+				if (!ok) {
3109+					throw new CertificateException(
3110+					    "Server Cert Chain != TRUSTALL Cert Chain.");
3111+				}
3112+				dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1));
3113+			}
3114+		    }
3115+		};
3116+
3117+		/*
3118+		 * The above TrustManagers are used:
3119+		 *
3120+		 * 1) to retrieve the server cert in case of failure to
3121+		 *    display it to the user in a dialog.
3122+		 * 2) to subsequently connect to the server if user agrees.
3123+		 */
3124+
3125+		/*
3126+		 * build oneTimeKey cert+key if supplied in applet parameter:
3127+		 */
3128+		if (viewer.oneTimeKey != null && viewer.oneTimeKey.equals("PROMPT")) {
3129+			ClientCertDialog d = new ClientCertDialog();
3130+			viewer.oneTimeKey = d.queryUser();
3131+		}
3132+		if (viewer.oneTimeKey != null && viewer.oneTimeKey.indexOf(",") > 0) {
3133+			int idx = viewer.oneTimeKey.indexOf(",");
3134+
3135+			String onetimekey = viewer.oneTimeKey.substring(0, idx);
3136+			byte[] key = hex2bytes(onetimekey);
3137+			String onetimecert = viewer.oneTimeKey.substring(idx+1);
3138+			byte[] cert = hex2bytes(onetimecert);
3139+
3140+			KeyFactory kf = KeyFactory.getInstance("RSA");
3141+			PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key );
3142+			PrivateKey ff = kf.generatePrivate (keysp);
3143+			if (debug_certs) {
3144+				dbg("one time key " + ff);
3145+			}
3146+
3147+			CertificateFactory cf = CertificateFactory.getInstance("X.509");
3148+			Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
3149+			Certificate[] certs = new Certificate[c.toArray().length];
3150+			if (c.size() == 1) {
3151+				Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
3152+				if (debug_certs) {
3153+					dbg("one time cert" + tmpcert);
3154+				}
3155+				certs[0] = tmpcert;
3156+			} else {
3157+				certs = (Certificate[]) c.toArray();
3158+			}
3159+
3160+			KeyStore ks = KeyStore.getInstance("JKS");
3161+			ks.load(null, null);
3162+			ks.setKeyEntry("onetimekey", ff, "".toCharArray(), certs);
3163+			String da = KeyManagerFactory.getDefaultAlgorithm();
3164+			KeyManagerFactory kmf = KeyManagerFactory.getInstance(da);
3165+			kmf.init(ks, "".toCharArray());
3166+
3167+			mykey = kmf.getKeyManagers();
3168+		}
3169+
3170+		/*
3171+		 * build serverCert cert if supplied in applet parameter:
3172+		 */
3173+		if (viewer.serverCert != null) {
3174+			CertificateFactory cf = CertificateFactory.getInstance("X.509");
3175+			byte[] cert = hex2bytes(viewer.serverCert);
3176+			Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
3177+			trustsrvCerts = new Certificate[c.toArray().length];
3178+			if (c.size() == 1) {
3179+				Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
3180+				trustsrvCerts[0] = tmpcert;
3181+			} else {
3182+				trustsrvCerts = (Certificate[]) c.toArray();
3183+			}
3184+		}
3185+
3186+		/* the trust loc certs context: */
3187+		try {
3188+			trustloc_ctx = SSLContext.getInstance("SSL");
3189+
3190+			/*
3191+			 * below is a failed attempt to get jvm's default
3192+			 * trust manager using null (below) makes it so
3193+			 * for HttpsURLConnection the server cannot be
3194+			 * verified (no prompting.)
3195+			 */
3196+			if (false) {
3197+				boolean didit = false;
3198+				TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
3199+				tmf.init((KeyStore) null);
3200+				TrustManager [] tml = tmf.getTrustManagers();
3201+				for (int i = 0; i < tml.length; i++) {
3202+					TrustManager tm = tml[i];
3203+					if (tm instanceof X509TrustManager) {
3204+						TrustManager tm1[] = new TrustManager[1];
3205+						tm1[0] = tm;
3206+						trustloc_ctx.init(mykey, tm1, null);
3207+						didit = true;
3208+						break;
3209+					}
3210+				}
3211+				if (!didit) {
3212+					trustloc_ctx.init(mykey, null, null);
3213+				}
3214+			} else {
3215+				/* we have to set trust manager to null */
3216+				trustloc_ctx.init(mykey, null, null);
3217+			}
3218+
3219+		} catch (Exception e) {
3220+			String msg = "SSL trustloc_ctx FAILED.";
3221+			dbg(msg);
3222+			throw new Exception(msg);
3223+		}
3224+
3225+		/* the trust all certs context: */
3226+		try {
3227+			trustall_ctx = SSLContext.getInstance("SSL");
3228+			trustall_ctx.init(mykey, trustAllCerts, new
3229+			    java.security.SecureRandom());
3230+
3231+		} catch (Exception e) {
3232+			String msg = "SSL trustall_ctx FAILED.";
3233+			dbg(msg);
3234+			throw new Exception(msg);
3235+		}
3236+
3237+		/* the trust url certs context: */
3238+		try {
3239+			trusturl_ctx = SSLContext.getInstance("SSL");
3240+			trusturl_ctx.init(mykey, trustUrlCert, new
3241+			    java.security.SecureRandom());
3242+
3243+		} catch (Exception e) {
3244+			String msg = "SSL trusturl_ctx FAILED.";
3245+			dbg(msg);
3246+			throw new Exception(msg);
3247+		}
3248+
3249+		/* the trust srv certs context: */
3250+		try {
3251+			trustsrv_ctx = SSLContext.getInstance("SSL");
3252+			trustsrv_ctx.init(mykey, trustSrvCert, new
3253+			    java.security.SecureRandom());
3254+
3255+		} catch (Exception e) {
3256+			String msg = "SSL trustsrv_ctx FAILED.";
3257+			dbg(msg);
3258+			throw new Exception(msg);
3259+		}
3260+
3261+		/* the trust the one cert from server context: */
3262+		try {
3263+			trustone_ctx = SSLContext.getInstance("SSL");
3264+			trustone_ctx.init(mykey, trustOneCert, new
3265+			    java.security.SecureRandom());
3266+
3267+		} catch (Exception e) {
3268+			String msg = "SSL trustone_ctx FAILED.";
3269+			dbg(msg);
3270+			throw new Exception(msg);
3271+		}
3272+	}
3273+
3274+	/*
3275+	 * we call this early on to 1) check for a proxy, 2) grab
3276+	 * Browser/JVM accepted HTTPS cert.
3277+	 */
3278+	public void check_for_proxy_and_grab_vnc_server_cert() {
3279+
3280+		trusturlCerts = null;
3281+		proxy_in_use = false;
3282+
3283+		if (viewer.ignoreProxy) {
3284+			/* applet param says skip it. */
3285+			/* the downside is we do not set trusturlCerts for comparison later... */
3286+			/* nor do we autodetect x11vnc for GET=1. */
3287+			return;
3288+		}
3289+
3290+		dbg("------------------------------------------------");
3291+		dbg("Into check_for_proxy_and_grab_vnc_server_cert():");
3292+
3293+		dbg("TRYING HTTPS:");
3294+		String ustr = "https://" + host + ":";
3295+		if (viewer.httpsPort != null) {
3296+			ustr += viewer.httpsPort;
3297+		} else {
3298+			ustr += port;
3299+		}
3300+		ustr += viewer.urlPrefix + "/check.https.proxy.connection";
3301+		dbg("ustr is: " + ustr);
3302+
3303+		try {
3304+			/* prepare for an HTTPS URL connection to host:port */
3305+			URL url = new URL(ustr);
3306+			HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
3307+
3308+			if (mykey != null) {
3309+				/* with oneTimeKey (mykey) we can't use the default SSL context */
3310+				if (trustsrvCerts != null) {
3311+					dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert.");
3312+					https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory());
3313+				} else if (trustloc_ctx != null) {
3314+					dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert.");
3315+					https.setSSLSocketFactory(trustloc_ctx.getSocketFactory());
3316+				}
3317+			}
3318+
3319+			https.setUseCaches(false);
3320+			https.setRequestMethod("GET");
3321+			https.setRequestProperty("Pragma", "No-Cache");
3322+			https.setRequestProperty("Proxy-Connection", "Keep-Alive");
3323+			https.setDoInput(true);
3324+
3325+			dbg("trying https.connect()");
3326+			https.connect();
3327+
3328+			dbg("trying https.getServerCertificates()");
3329+			trusturlCerts = https.getServerCertificates();
3330+
3331+			if (trusturlCerts == null) {
3332+				dbg("set trusturlCerts to null!");
3333+			} else {
3334+				dbg("set trusturlCerts to non-null");
3335+			}
3336+
3337+			if (https.usingProxy()) {
3338+				proxy_in_use = true;
3339+				dbg("An HTTPS proxy is in use. There may be connection problems.");
3340+			}
3341+
3342+			dbg("trying https.getContent()");
3343+			Object output = https.getContent();
3344+			dbg("trying https.disconnect()");
3345+			https.disconnect();
3346+			if (! viewer.GET) {
3347+				String header = https.getHeaderField("VNC-Server");
3348+				if (header != null && header.startsWith("x11vnc")) {
3349+					dbg("detected x11vnc server (1), setting GET=1");
3350+					viewer.GET = true;
3351+				}
3352+			}
3353+
3354+		} catch(Exception e) {
3355+			dbg("HttpsURLConnection: " + e.getMessage());
3356+		}
3357+
3358+		if (proxy_in_use) {
3359+			dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
3360+			dbg("------------------------------------------------");
3361+			return;
3362+		} else if (trusturlCerts != null && !viewer.forceProxy) {
3363+			/* Allow user to require HTTP check?  use forceProxy for now. */
3364+			dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct.");
3365+			dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
3366+			dbg("------------------------------------------------");
3367+			return;
3368+		}
3369+
3370+		/*
3371+		 * XXX need to remember scenario where this extra check
3372+		 * gives useful info.  User's Browser proxy settings?
3373+		 */
3374+		dbg("TRYING HTTP:");
3375+		ustr = "http://" + host + ":" + port;
3376+		ustr += viewer.urlPrefix + "/index.vnc";
3377+		dbg("ustr is: " + ustr);
3378+
3379+		try {
3380+			/* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */
3381+			URL url = new URL(ustr);
3382+			HttpURLConnection http = (HttpURLConnection)
3383+			    url.openConnection();
3384+
3385+			http.setUseCaches(false);
3386+			http.setRequestMethod("GET");
3387+			http.setRequestProperty("Pragma", "No-Cache");
3388+			http.setRequestProperty("Proxy-Connection", "Keep-Alive");
3389+			http.setDoInput(true);
3390+
3391+			dbg("trying http.connect()");
3392+			http.connect();
3393+
3394+			if (http.usingProxy()) {
3395+				proxy_in_use = true;
3396+				dbg("An HTTP proxy is in use. There may be connection problems.");
3397+			}
3398+			dbg("trying http.getContent()");
3399+			Object output = http.getContent();
3400+			dbg("trying http.disconnect()");
3401+			http.disconnect();
3402+			if (! viewer.GET) {
3403+				String header = http.getHeaderField("VNC-Server");
3404+				if (header != null && header.startsWith("x11vnc")) {
3405+					dbg("detected x11vnc server (2), setting GET=1");
3406+					viewer.GET = true;
3407+				}
3408+			}
3409+		} catch(Exception e) {
3410+			dbg("HttpURLConnection:  " + e.getMessage());
3411+		}
3412+		dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
3413+		dbg("------------------------------------------------");
3414+	}
3415+
3416+	public Socket connectSock() throws IOException {
3417+		/*
3418+		 * first try a https connection to detect a proxy, and
3419+		 * grab the VNC server cert at the same time:
3420+		 */
3421+		check_for_proxy_and_grab_vnc_server_cert();
3422+
3423+		boolean srv_cert = false;
3424+
3425+		if (trustsrvCerts != null) {
3426+			/* applet parameter suppled serverCert */
3427+			dbg("viewer.trustSrvCert-0 using trustsrv_ctx");
3428+			factory = trustsrv_ctx.getSocketFactory();
3429+			srv_cert = true;
3430+		} else if (viewer.trustAllVncCerts) {
3431+			/* trust all certs (no checking) */
3432+			dbg("viewer.trustAllVncCerts-0 using trustall_ctx");
3433+			factory = trustall_ctx.getSocketFactory();
3434+		} else if (trusturlCerts != null) {
3435+			/* trust certs the Browser/JVM accepted in check_for_proxy... */
3436+			dbg("using trusturl_ctx");
3437+			factory = trusturl_ctx.getSocketFactory();
3438+		} else {
3439+			/* trust the local defaults */
3440+			dbg("using trustloc_ctx");
3441+			factory = trustloc_ctx.getSocketFactory();
3442+		}
3443+
3444+		socket = null;
3445+
3446+		try {
3447+			if (proxy_in_use && viewer.forceProxy) {
3448+				throw new Exception("forcing proxy (forceProxy)");
3449+			} else if (viewer.CONNECT != null) {
3450+				throw new Exception("forcing CONNECT");
3451+			}
3452+
3453+			int timeout = 6;
3454+			if (timeout > 0) {
3455+				socket = (SSLSocket) factory.createSocket();
3456+				InetSocketAddress inetaddr = new InetSocketAddress(host, port);
3457+				dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port);
3458+				socket.connect(inetaddr, timeout * 1000);
3459+			} else {
3460+				socket = (SSLSocket) factory.createSocket(host, port);
3461+			}
3462+
3463+		} catch (Exception esock) {
3464+			dbg("socket error: " + esock.getMessage());
3465+			if (proxy_in_use || viewer.CONNECT != null) {
3466+				proxy_failure = true;
3467+				if (proxy_in_use) {
3468+					dbg("HTTPS proxy in use. Trying to go with it.");
3469+				} else {
3470+					dbg("viewer.CONNECT reverse proxy in use. Trying to go with it.");
3471+				}
3472+				try {
3473+					socket = proxy_socket(factory);
3474+				} catch (Exception e) {
3475+					dbg("proxy_socket error: " + e.getMessage());
3476+				}
3477+			} else {
3478+				/* n.b. socket is left in error state to cause ex. below. */
3479+			}
3480+		}
3481+
3482+		try {
3483+			socket.startHandshake();
3484+
3485+			dbg("The Server Connection Verified OK on 1st try.");
3486+
3487+			java.security.cert.Certificate[] currentTrustedCerts;
3488+			BrowserCertsDialog bcd;
3489+
3490+			SSLSession sess = socket.getSession();
3491+			currentTrustedCerts = sess.getPeerCertificates();
3492+
3493+			if (viewer.trustAllVncCerts) {
3494+				dbg("viewer.trustAllVncCerts-1  keeping socket.");
3495+			} else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) {
3496+				try {
3497+					socket.close();
3498+				} catch (Exception e) {
3499+					dbg("socket is grumpy.");
3500+				}
3501+				socket = null;
3502+				throw new SSLHandshakeException("no current certs");
3503+			}
3504+
3505+			String serv = "";
3506+			try {
3507+				CertInfo ci = new CertInfo(currentTrustedCerts[0]);
3508+				serv = ci.get_certinfo("CN");
3509+			} catch (Exception e) {
3510+				;
3511+			}
3512+
3513+			if (viewer.trustAllVncCerts) {
3514+				dbg("viewer.trustAllVncCerts-2  skipping browser certs dialog");
3515+				user_wants_to_see_cert = false;
3516+			} else if (viewer.serverCert != null && trustsrvCerts != null) {
3517+				dbg("viewer.serverCert-1  skipping browser certs dialog");
3518+				user_wants_to_see_cert = false;
3519+			} else if (viewer.trustUrlVncCert) {
3520+				dbg("viewer.trustUrlVncCert-1  skipping browser certs dialog");
3521+				user_wants_to_see_cert = false;
3522+			} else {
3523+				/* have a dialog with the user: */
3524+				bcd = new BrowserCertsDialog(serv, host + ":" + port);
3525+				dbg("browser certs dialog begin.");
3526+				bcd.queryUser();
3527+				dbg("browser certs dialog finished.");
3528+
3529+				if (bcd.showCertDialog) {
3530+					String msg = "user wants to see cert";
3531+					dbg(msg);
3532+					user_wants_to_see_cert = true;
3533+					if (cert_fail == null) {
3534+						cert_fail = "user-view";
3535+					}
3536+					throw new SSLHandshakeException(msg);
3537+				} else {
3538+					user_wants_to_see_cert = false;
3539+					dbg("browser certs dialog: user said yes, accept it");
3540+				}
3541+			}
3542+
3543+		} catch (SSLHandshakeException eh)  {
3544+			dbg("SSLHandshakeException: could not automatically verify Server.");
3545+			dbg("msg: " + eh.getMessage());
3546+
3547+
3548+			/* send a cleanup string just in case: */
3549+			String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n";
3550+
3551+			try {
3552+				OutputStream os = socket.getOutputStream();
3553+				os.write(getoutstr.getBytes());
3554+				socket.close();
3555+			} catch (Exception e) {
3556+				dbg("socket is grumpy!");
3557+			}
3558+
3559+			/* reload */
3560+
3561+			socket = null;
3562+
3563+			String reason = null;
3564+
3565+			if (srv_cert) {
3566+				/* for serverCert usage we make this a fatal error. */
3567+				throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'");
3568+				/* see below in TrustDialog were we describe this case to user anyway */
3569+			}
3570+
3571+			/*
3572+			 * Reconnect, trusting any cert, so we can grab
3573+			 * the cert to show it to the user in a dialog
3574+			 * for him to manually accept.  This connection
3575+			 * is not used for anything else.
3576+			 */
3577+			factory = trustall_ctx.getSocketFactory();
3578+			if (proxy_failure) {
3579+				socket = proxy_socket(factory);
3580+			} else {
3581+				socket = (SSLSocket) factory.createSocket(host, port);
3582+			}
3583+
3584+			if (debug_certs) {
3585+				dbg("trusturlCerts: " + trusturlCerts);
3586+				dbg("trustsrvCerts: " + trustsrvCerts);
3587+			}
3588+			if (trusturlCerts == null && cert_fail == null) {
3589+				cert_fail = "missing-certs";
3590+			}
3591+
3592+			try {
3593+				socket.startHandshake();
3594+
3595+				dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK.");
3596+
3597+				/* grab the cert: */
3598+				try {
3599+					SSLSession sess = socket.getSession();
3600+					trustallCerts = sess.getPeerCertificates();
3601+				} catch (Exception e) {
3602+					throw new Exception("Could not get " +
3603+					    "Peer Certificate");
3604+				}
3605+				if (debug_certs) {
3606+					dbg("trustallCerts: " + trustallCerts);
3607+				}
3608+
3609+				if (viewer.trustAllVncCerts) {
3610+					dbg("viewer.trustAllVncCerts-3.  skipping dialog, trusting everything.");
3611+				} else if (! browser_cert_match()) {
3612+					/*
3613+					 * close socket now, we will reopen after
3614+					 * dialog if user agrees to use the cert.
3615+					 */
3616+					try {
3617+						OutputStream os = socket.getOutputStream();
3618+						os.write(getoutstr.getBytes());
3619+						socket.close();
3620+					} catch (Exception e) {
3621+						dbg("socket is grumpy!!");
3622+					}
3623+					socket = null;
3624+
3625+					/* dialog with user to accept cert or not: */
3626+
3627+					TrustDialog td= new TrustDialog(host, port,
3628+					    trustallCerts);
3629+
3630+					if (cert_fail == null) {
3631+						;
3632+					} else if (cert_fail.equals("user-view")) {
3633+						reason = "Reason for this Dialog:\n\n"
3634+						       + "        You Asked to View the Certificate.";
3635+					} else if (cert_fail.equals("server-cert-mismatch")) {
3636+						/* this is now fatal error, see above. */
3637+						reason = "Reason for this Dialog:\n\n"
3638+						       + "        The VNC Server's Certificate does not match the Certificate\n"
3639+						       + "        specified in the supplied 'serverCert' Applet Parameter.";
3640+					} else if (cert_fail.equals("cert-mismatch")) {
3641+						reason = "Reason for this Dialog:\n\n"
3642+						       + "        The VNC Server's Certificate does not match the Website's\n"
3643+						       + "        HTTPS Certificate (that you previously accepted; either\n"
3644+						       + "        manually or automatically via Certificate Authority.)";
3645+					} else if (cert_fail.equals("missing-certs")) {
3646+						reason = "Reason for this Dialog:\n\n"
3647+						       + "        Not all Certificates could be obtained to check.";
3648+					}
3649+
3650+					if (! td.queryUser(reason)) {
3651+						String msg = "User decided against it.";
3652+						dbg(msg);
3653+						throw new IOException(msg);
3654+					}
3655+				}
3656+
3657+			} catch (Exception ehand2)  {
3658+				dbg("** Could not TrustAll Verify Server!");
3659+
3660+				throw new IOException(ehand2.getMessage());
3661+			}
3662+
3663+			/* reload again: */
3664+
3665+			if (socket != null) {
3666+				try {
3667+					socket.close();
3668+				} catch (Exception e) {
3669+					dbg("socket is grumpy!!!");
3670+				}
3671+				socket = null;
3672+			}
3673+
3674+			/*
3675+			 * Now connect a 3rd time, using the cert
3676+			 * retrieved during connection 2 (sadly, that
3677+			 * the user likely blindly agreed to...)
3678+			 */
3679+
3680+			factory = trustone_ctx.getSocketFactory();
3681+			if (proxy_failure) {
3682+				socket = proxy_socket(factory);
3683+			} else {
3684+				socket = (SSLSocket) factory.createSocket(host, port);
3685+			}
3686+
3687+			try {
3688+				socket.startHandshake();
3689+				dbg("TrustAll/TrustOne Server Connection Verified #3.");
3690+
3691+			} catch (Exception ehand3)  {
3692+				dbg("** Could not TrustAll/TrustOne Verify Server #3.");
3693+
3694+				throw new IOException(ehand3.getMessage());
3695+			}
3696+		}
3697+
3698+		/* we have socket (possibly null) at this point, so proceed: */
3699+
3700+		/* handle x11vnc GET=1, if applicable: */
3701+		if (socket != null && viewer.GET) {
3702+			String str = "GET ";
3703+			str += viewer.urlPrefix;
3704+			str += "/request.https.vnc.connection";
3705+			str += " HTTP/1.0\r\n";
3706+			str += "Pragma: No-Cache\r\n";
3707+			str += "\r\n";
3708+
3709+			System.out.println("sending: " + str);
3710+    			OutputStream os = socket.getOutputStream();
3711+			String type = "os";
3712+
3713+			if (type == "os") {
3714+				os.write(str.getBytes());
3715+				os.flush();
3716+				System.out.println("used OutputStream");
3717+			} else if (type == "bs") {
3718+				BufferedOutputStream bs = new BufferedOutputStream(os);
3719+				bs.write(str.getBytes());
3720+				bs.flush();
3721+				System.out.println("used BufferedOutputStream");
3722+			} else if (type == "ds") {
3723+				DataOutputStream ds = new DataOutputStream(os);
3724+				ds.write(str.getBytes());
3725+				ds.flush();
3726+				System.out.println("used DataOutputStream");
3727+			}
3728+			if (false) {
3729+				String rep = "";
3730+				DataInputStream is = new DataInputStream(
3731+				    new BufferedInputStream(socket.getInputStream(), 16384));
3732+				while (true) {
3733+					rep += readline(is);
3734+					if (rep.indexOf("\r\n\r\n") >= 0) {
3735+						break;
3736+					}
3737+				}
3738+				System.out.println("rep: " + rep);
3739+			}
3740+		}
3741+
3742+		dbg("SSL returning socket to caller.");
3743+		dbg("");
3744+
3745+		/* could be null, let caller handle that. */
3746+		return (Socket) socket;
3747+	}
3748+
3749+	boolean browser_cert_match() {
3750+		String msg = "Browser URL accept previously accepted cert";
3751+
3752+		if (user_wants_to_see_cert) {
3753+			return false;
3754+		}
3755+
3756+		if (viewer.serverCert != null || trustsrvCerts != null) {
3757+			if (cert_fail == null) {
3758+				cert_fail = "server-cert-mismatch";
3759+			}
3760+		}
3761+		if (trustallCerts != null && trusturlCerts != null) {
3762+		    if (trustallCerts.length == trusturlCerts.length) {
3763+			boolean ok = true;
3764+			/* check toath trustallCerts (socket) equals trusturlCerts (browser) */
3765+			for (int i = 0; i < trusturlCerts.length; i++)  {
3766+				if (! trustallCerts[i].equals(trusturlCerts[i])) {
3767+					dbg("BCM: cert mismatch at i=" + i);
3768+					dbg("BCM: cert mismatch  url" + trusturlCerts[i]);
3769+					dbg("BCM: cert mismatch  all" + trustallCerts[i]);
3770+					ok = false;
3771+				}
3772+			}
3773+			if (ok) {
3774+				System.out.println(msg);
3775+				if (cert_fail == null) {
3776+					cert_fail = "did-not-fail";
3777+				}
3778+				return true;
3779+			} else {
3780+				if (cert_fail == null) {
3781+					cert_fail = "cert-mismatch";
3782+				}
3783+				return false;
3784+			}
3785+		    }
3786+		}
3787+		if (cert_fail == null) {
3788+			cert_fail = "missing-certs";
3789+		}
3790+		return false;
3791+	}
3792+
3793+	private void dbg(String s) {
3794+		if (debug) {
3795+			System.out.println(s);
3796+		}
3797+	}
3798+
3799+	private int gint(String s) {
3800+		int n = -1;
3801+		try {
3802+			Integer I = new Integer(s);
3803+			n = I.intValue();
3804+		} catch (Exception ex) {
3805+			return -1;
3806+		}
3807+		return n;
3808+	}
3809+
3810+	/* this will do the proxy CONNECT negotiation and hook us up.  */
3811+
3812+	private void proxy_helper(String proxyHost, int proxyPort) {
3813+
3814+		boolean proxy_auth = false;
3815+		String proxy_auth_basic_realm = "";
3816+		String hp = host + ":" + port;
3817+		dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp);
3818+
3819+		/* we loop here a few times trying for the password case */
3820+		for (int k=0; k < 2; k++) {
3821+			dbg("proxy_in_use psocket: " + k);
3822+
3823+			if (proxySock != null) {
3824+				try {
3825+					proxySock.close();
3826+				} catch (Exception e) {
3827+					dbg("proxy socket is grumpy.");
3828+				}
3829+			}
3830+
3831+			proxySock = psocket(proxyHost, proxyPort);
3832+			if (proxySock == null) {
3833+				dbg("1-a sadly, returning a null socket");
3834+				return;
3835+			}
3836+
3837+			String req1 = "CONNECT " + hp + " HTTP/1.1\r\n"
3838+			    + "Host: " + hp + "\r\n";
3839+
3840+			dbg("requesting via proxy: " + req1);
3841+
3842+			if (proxy_auth) {
3843+				if (proxy_auth_string == null) {
3844+					ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm);
3845+					pp.queryUser();
3846+					proxy_auth_string = pp.getAuth();
3847+				}
3848+				//dbg("auth1: " + proxy_auth_string);
3849+
3850+				String auth2 = Base64Coder.encodeString(proxy_auth_string);
3851+				//dbg("auth2: " + auth2);
3852+
3853+				req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n";
3854+				//dbg("req1: " + req1);
3855+
3856+				dbg("added Proxy-Authorization: Basic ... to request");
3857+			}
3858+			req1 += "\r\n";
3859+
3860+			try {
3861+				proxy_os.write(req1.getBytes());
3862+				String reply = readline(proxy_is);
3863+
3864+				dbg("proxy replied: " + reply.trim());
3865+
3866+				if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) {
3867+					proxy_auth = true;
3868+					proxySock.close();
3869+				} else if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) {
3870+					proxySock.close();
3871+					proxySock = psocket(proxyHost, proxyPort);
3872+					if (proxySock == null) {
3873+						dbg("2-a sadly, returning a null socket");
3874+						return;
3875+					}
3876+				}
3877+			} catch(Exception e) {
3878+				dbg("some proxy socket problem: " + e.getMessage());
3879+			}
3880+
3881+			/* read the rest of the HTTP headers */
3882+			while (true) {
3883+				String line = readline(proxy_is);
3884+				dbg("proxy line: " + line.trim());
3885+				if (proxy_auth) {
3886+					String uc = line.toLowerCase();
3887+					if (uc.indexOf("proxy-authenticate:") == 0) {
3888+						if (uc.indexOf(" basic ") >= 0) {
3889+							int idx = uc.indexOf(" realm");
3890+							if (idx >= 0) {
3891+								proxy_auth_basic_realm = uc.substring(idx+1);
3892+							}
3893+						}
3894+					}
3895+				}
3896+				if (line.equals("\r\n") || line.equals("\n")) {
3897+					break;
3898+				}
3899+			}
3900+			if (!proxy_auth || proxy_auth_basic_realm.equals("")) {
3901+				/* we only try once for the non-password case: */
3902+				break;
3903+			}
3904+		}
3905+	}
3906+
3907+	public SSLSocket proxy_socket(SSLSocketFactory factory) {
3908+		Properties props = null;
3909+		String proxyHost = null;
3910+		int proxyPort = 0;
3911+		String proxyHost_nossl = null;
3912+		int proxyPort_nossl = 0;
3913+		String str;
3914+
3915+		/* see if we can guess the proxy info from Properties: */
3916+		try {
3917+			props = System.getProperties();
3918+		} catch (Exception e) {
3919+			/* sandboxed applet might not be able to read it. */
3920+			dbg("props failed: " + e.getMessage());
3921+		}
3922+		if (viewer.proxyHost != null) {
3923+			dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters.");
3924+			proxyHost = viewer.proxyHost;
3925+			if (viewer.proxyPort != null) {
3926+				proxyPort = gint(viewer.proxyPort);
3927+			} else {
3928+				proxyPort = 8080;
3929+			}
3930+
3931+		} else if (props != null) {
3932+			dbg("\n---------------\nAll props:");
3933+			props.list(System.out);
3934+			dbg("\n---------------\n\n");
3935+
3936+			/* scrape throught properties looking for proxy info: */
3937+
3938+			for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) {
3939+				String s = (String) e.nextElement();
3940+				String v = System.getProperty(s);
3941+				String s2 = s.toLowerCase();
3942+				String v2 = v.toLowerCase();
3943+
3944+				if (s2.indexOf("proxy.https.host") >= 0) {
3945+					proxyHost = v2;
3946+					continue;
3947+				}
3948+				if (s2.indexOf("proxy.https.port") >= 0) {
3949+					proxyPort = gint(v2);
3950+					continue;
3951+				}
3952+				if (s2.indexOf("proxy.http.host") >= 0) {
3953+					proxyHost_nossl = v2;
3954+					continue;
3955+				}
3956+				if (s2.indexOf("proxy.http.port") >= 0) {
3957+					proxyPort_nossl = gint(v2);
3958+					continue;
3959+				}
3960+			}
3961+
3962+			for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) {
3963+				String s = (String) e.nextElement();
3964+				String v = System.getProperty(s);
3965+				String s2 = s.toLowerCase();
3966+				String v2 = v.toLowerCase();
3967+
3968+				if (proxyHost != null && proxyPort > 0) {
3969+					break;
3970+				}
3971+
3972+				// look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082
3973+				if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) {
3974+					continue;
3975+				}
3976+				if (v2.indexOf("http") < 0) {
3977+					continue;
3978+				}
3979+
3980+				String[] pieces = v.split("[,;]");
3981+				for (int i = 0; i < pieces.length; i++) {
3982+					String p = pieces[i];
3983+					int j = p.indexOf("https");
3984+					if (j < 0) {
3985+						j = p.indexOf("http");
3986+						if (j < 0) {
3987+							continue;
3988+						}
3989+					}
3990+					j = p.indexOf("=", j);
3991+					if (j < 0) {
3992+						continue;
3993+					}
3994+					p = p.substring(j+1);
3995+					String [] hp = p.split(":");
3996+					if (hp.length != 2) {
3997+						continue;
3998+					}
3999+					if (hp[0].length() > 1 && hp[1].length() > 1) {
4000+
4001+						proxyPort = gint(hp[1]);
4002+						if (proxyPort < 0) {
4003+							continue;
4004+						}
4005+						proxyHost = new String(hp[0]);
4006+						break;
4007+					}
4008+				}
4009+			}
4010+		}
4011+		if (proxyHost != null) {
4012+			if (proxyHost_nossl != null && proxyPort_nossl > 0) {
4013+				dbg("Using http proxy info instead of https.");
4014+				proxyHost = proxyHost_nossl;
4015+				proxyPort = proxyPort_nossl;
4016+			}
4017+		}
4018+
4019+		if (proxy_in_use) {
4020+			if (proxy_dialog_host != null && proxy_dialog_port > 0) {
4021+				proxyHost = proxy_dialog_host;
4022+				proxyPort = proxy_dialog_port;
4023+			}
4024+			if (proxyHost != null) {
4025+				dbg("Lucky us! we figured out the Proxy parameters: " + proxyHost + " " + proxyPort);
4026+			} else {
4027+				/* ask user to help us: */
4028+				ProxyDialog pd = new ProxyDialog(proxyHost, proxyPort);
4029+				pd.queryUser();
4030+				proxyHost = pd.getHost();
4031+				proxyPort = pd.getPort();
4032+				proxy_dialog_host = new String(proxyHost);
4033+				proxy_dialog_port = proxyPort;
4034+				dbg("User said host: " + pd.getHost() + " port: " + pd.getPort());
4035+			}
4036+
4037+			proxy_helper(proxyHost, proxyPort);
4038+			if (proxySock == null) {
4039+				return null;
4040+			}
4041+		} else if (viewer.CONNECT != null) {
4042+			dbg("viewer.CONNECT psocket:");
4043+			proxySock = psocket(host, port);
4044+			if (proxySock == null) {
4045+				dbg("1-b sadly, returning a null socket");
4046+				return null;
4047+			}
4048+		}
4049+
4050+		if (viewer.CONNECT != null) {
4051+			String hp = viewer.CONNECT;
4052+			String req2 = "CONNECT " + hp + " HTTP/1.1\r\n"
4053+			    + "Host: " + hp + "\r\n\r\n";
4054+
4055+			dbg("requesting2: " + req2);
4056+
4057+			try {
4058+				proxy_os.write(req2.getBytes());
4059+				String reply = readline(proxy_is);
4060+
4061+				dbg("proxy replied2: " + reply.trim());
4062+
4063+				if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) {
4064+					proxySock.close();
4065+					proxySock = psocket(proxyHost, proxyPort);
4066+					if (proxySock == null) {
4067+						dbg("2-b sadly, returning a null socket");
4068+						return null;
4069+					}
4070+				}
4071+			} catch(Exception e) {
4072+				dbg("proxy socket problem-2: " + e.getMessage());
4073+			}
4074+
4075+			while (true) {
4076+				String line = readline(proxy_is);
4077+				dbg("proxy line2: " + line.trim());
4078+				if (line.equals("\r\n") || line.equals("\n")) {
4079+					break;
4080+				}
4081+			}
4082+		}
4083+
4084+		Socket sslsock = null;
4085+		try {
4086+			sslsock = factory.createSocket(proxySock, host, port, true);
4087+		} catch(Exception e) {
4088+			dbg("sslsock prob: " + e.getMessage());
4089+			dbg("3 sadly, returning a null socket");
4090+		}
4091+
4092+		return (SSLSocket) sslsock;
4093+	}
4094+
4095+	Socket psocket(String h, int p) {
4096+		Socket psock = null;
4097+		try {
4098+			psock = new Socket(h, p);
4099+			proxy_is = new DataInputStream(new BufferedInputStream(
4100+			    psock.getInputStream(), 16384));
4101+			proxy_os = psock.getOutputStream();
4102+		} catch(Exception e) {
4103+			dbg("psocket prob: " + e.getMessage());
4104+			return null;
4105+		}
4106+
4107+		return psock;
4108+	}
4109+
4110+	String readline(DataInputStream i) {
4111+		byte[] ba = new byte[1];
4112+		String s = new String("");
4113+		ba[0] = 0;
4114+		try {
4115+			while (ba[0] != 0xa) {
4116+				ba[0] = (byte) i.readUnsignedByte();
4117+				s += new String(ba);
4118+			}
4119+		} catch (Exception e) {
4120+			;
4121+		}
4122+		return s;
4123+	}
4124+}
4125+
4126+class TrustDialog implements ActionListener {
4127+	String msg, host, text;
4128+	int port;
4129+	java.security.cert.Certificate[] trustallCerts = null;
4130+	boolean viewing_cert = false;
4131+	boolean trust_this_session = false;
4132+
4133+	/*
4134+	 * this is the gui to show the user the cert and info and ask
4135+	 * them if they want to continue using this cert.
4136+	 */
4137+
4138+	Button ok, cancel, viewcert;
4139+	TextArea textarea;
4140+	Checkbox accept, deny;
4141+	Dialog dialog;
4142+
4143+	String s1 = "Accept this certificate temporarily for this session";
4144+	String s2 = "Do not accept this certificate and do not connect to"
4145+	    + " this VNC server";
4146+	String ln = "\n---------------------------------------------------\n\n";
4147+
4148+	TrustDialog (String h, int p, java.security.cert.Certificate[] s) {
4149+		host = h;
4150+		port = p;
4151+		trustallCerts = s;
4152+
4153+		msg = "VNC Server " + host + ":" + port + " Not Verified";
4154+	}
4155+
4156+	public boolean queryUser(String reason) {
4157+
4158+		/* create and display the dialog for unverified cert. */
4159+
4160+		Frame frame = new Frame(msg);
4161+
4162+		dialog = new Dialog(frame, true);
4163+
4164+		String infostr = "";
4165+		if (trustallCerts.length == 1) {
4166+			CertInfo ci = new CertInfo(trustallCerts[0]);
4167+			infostr = ci.get_certinfo("all");
4168+		}
4169+		if (reason != null) {
4170+			reason += "\n\n";
4171+		}
4172+
4173+		text = "\n"
4174++ "Unable to verify the identity of\n"
4175++ "\n"
4176++ "        " + host + ":" + port + "\n"
4177++ "\n"
4178++ infostr
4179++ "\n"
4180++ "as a trusted VNC server.\n"
4181++ "\n"
4182++ reason
4183++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n"
4184++ "is due to one of the following:\n"
4185++ "\n"
4186++ " - Your requesting to View the Certificate before accepting.\n"
4187++ "\n"
4188++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n"
4189++ "   Authority not recognized by your Web Browser or Java Plugin runtime.\n"
4190++ "\n"
4191++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n"
4192++ "   the Apache Web server has a certificate *different* from the VNC server's.\n"
4193++ "\n"
4194++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n"
4195++ "   obtained by this applet to compare the VNC Server Certificate against.\n"
4196++ "\n"
4197++ " - The VNC Server's Certificate does not match the one specified in the\n"
4198++ "   supplied 'serverCert' Java Applet Parameter.\n"
4199++ "\n"
4200++ " - A Man-In-The-Middle attack impersonating as the VNC server that you wish\n"
4201++ "   to connect to.  (Wouldn't that be exciting!!)\n"
4202++ "\n"
4203++ "By safely copying the VNC server's Certificate (or using a common Certificate\n"
4204++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n"
4205++ "automatically authenticate this VNC Server.\n"
4206++ "\n"
4207++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n"
4208++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n"
4209++ "certificate (except for the Apache portal case above where they don't match.)\n"
4210++ "\n"
4211++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n"
4212++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n"
4213++ "and thereby see no dialog from this VNC Viewer applet.\n"
4214+;
4215+
4216+		/* the accept / do-not-accept radio buttons: */
4217+		CheckboxGroup checkbox = new CheckboxGroup();
4218+		accept = new Checkbox(s1, true, checkbox);
4219+		deny   = new Checkbox(s2, false, checkbox);
4220+
4221+		/* put the checkboxes in a panel: */
4222+		Panel check = new Panel();
4223+		check.setLayout(new GridLayout(2, 1));
4224+
4225+		check.add(accept);
4226+		check.add(deny);
4227+
4228+		/* make the 3 buttons: */
4229+		ok = new Button("OK");
4230+		cancel = new Button("Cancel");
4231+		viewcert = new Button("View Certificate");
4232+
4233+		ok.addActionListener(this);
4234+		cancel.addActionListener(this);
4235+		viewcert.addActionListener(this);
4236+
4237+		/* put the buttons in their own panel: */
4238+		Panel buttonrow = new Panel();
4239+		buttonrow.setLayout(new FlowLayout(FlowLayout.LEFT));
4240+		buttonrow.add(viewcert);
4241+		buttonrow.add(ok);
4242+		buttonrow.add(cancel);
4243+
4244+		/* label at the top: */
4245+		Label label = new Label(msg, Label.CENTER);
4246+		label.setFont(new Font("Helvetica", Font.BOLD, 16));
4247+
4248+		/* textarea in the middle */
4249+		textarea = new TextArea(text, 38, 64,
4250+		    TextArea.SCROLLBARS_VERTICAL_ONLY);
4251+		textarea.setEditable(false);
4252+
4253+		/* put the two panels in their own panel at bottom: */
4254+		Panel bot = new Panel();
4255+		bot.setLayout(new GridLayout(2, 1));
4256+		bot.add(check);
4257+		bot.add(buttonrow);
4258+
4259+		/* now arrange things inside the dialog: */
4260+		dialog.setLayout(new BorderLayout());
4261+
4262+		dialog.add("North", label);
4263+		dialog.add("South", bot);
4264+		dialog.add("Center", textarea);
4265+
4266+		dialog.pack();
4267+		dialog.resize(dialog.preferredSize());
4268+
4269+		dialog.show();	/* block here til OK or Cancel pressed. */
4270+
4271+		return trust_this_session;
4272+	}
4273+
4274+	public synchronized void actionPerformed(ActionEvent evt) {
4275+
4276+		if (evt.getSource() == viewcert) {
4277+			/* View Certificate button clicked */
4278+			if (viewing_cert) {
4279+				/* show the original info text: */
4280+				textarea.setText(text);
4281+				viewcert.setLabel("View Certificate");
4282+				viewing_cert = false;
4283+			} else {
4284+				int i;
4285+				/* show all (likely just one) certs: */
4286+				textarea.setText("");
4287+				for (i=0; i < trustallCerts.length; i++) {
4288+					int j = i + 1;
4289+					textarea.append("Certificate[" +
4290+					    j + "]\n\n");
4291+					textarea.append(
4292+					    trustallCerts[i].toString());
4293+					textarea.append(ln);
4294+				}
4295+				viewcert.setLabel("View Info");
4296+				viewing_cert = true;
4297+
4298+				textarea.setCaretPosition(0);
4299+			}
4300+
4301+		} else if (evt.getSource() == ok) {
4302+			/* OK button clicked */
4303+			if (accept.getState()) {
4304+				trust_this_session = true;
4305+			} else {
4306+				trust_this_session = false;
4307+			}
4308+			//dialog.dispose();
4309+			dialog.hide();
4310+
4311+		} else if (evt.getSource() == cancel) {
4312+			/* Cancel button clicked */
4313+			trust_this_session = false;
4314+
4315+			//dialog.dispose();
4316+			dialog.hide();
4317+		}
4318+	}
4319+
4320+	String get_certinfo() {
4321+		String all = "";
4322+		String fields[] = {"CN", "OU", "O", "L", "C"};
4323+		int i;
4324+		if (trustallCerts.length < 1) {
4325+			all = "";
4326+			return all;
4327+		}
4328+		String cert = trustallCerts[0].toString();
4329+
4330+		/*
4331+		 * For now we simply scrape the cert string, there must
4332+		 * be an API for this... perhaps optionValue?
4333+		 */
4334+
4335+		for (i=0; i < fields.length; i++) {
4336+			int f, t, t1, t2;
4337+			String sub, mat = fields[i] + "=";
4338+
4339+			f = cert.indexOf(mat, 0);
4340+			if (f > 0) {
4341+				t1 = cert.indexOf(", ", f);
4342+				t2 = cert.indexOf("\n", f);
4343+				if (t1 < 0 && t2 < 0) {
4344+					continue;
4345+				} else if (t1 < 0) {
4346+					t = t2;
4347+				} else if (t2 < 0) {
4348+					t = t1;
4349+				} else if (t1 < t2) {
4350+					t = t1;
4351+				} else {
4352+					t = t2;
4353+				}
4354+				if (t > f) {
4355+					sub = cert.substring(f, t);
4356+					all = all + "        " + sub + "\n";
4357+				}
4358+			}
4359+		}
4360+		return all;
4361+	}
4362+}
4363+
4364+class ProxyDialog implements ActionListener {
4365+	String guessedHost = null;
4366+	String guessedPort = null;
4367+	/*
4368+	 * this is the gui to show the user the cert and info and ask
4369+	 * them if they want to continue using this cert.
4370+	 */
4371+
4372+	Button ok;
4373+	Dialog dialog;
4374+	TextField entry;
4375+	String reply = "";
4376+
4377+	ProxyDialog (String h, int p) {
4378+		guessedHost = h;
4379+		try {
4380+			guessedPort = Integer.toString(p);
4381+		} catch (Exception e) {
4382+			guessedPort = "8080";
4383+		}
4384+	}
4385+
4386+	public void queryUser() {
4387+
4388+		/* create and display the dialog for unverified cert. */
4389+
4390+		Frame frame = new Frame("Need Proxy host:port");
4391+
4392+		dialog = new Dialog(frame, true);
4393+
4394+
4395+		Label label = new Label("Please Enter your https Proxy info as host:port", Label.CENTER);
4396+		//label.setFont(new Font("Helvetica", Font.BOLD, 16));
4397+		entry = new TextField(30);
4398+		ok = new Button("OK");
4399+		ok.addActionListener(this);
4400+
4401+		String guess = "";
4402+		if (guessedHost != null) {
4403+			guess = guessedHost + ":" + guessedPort;
4404+		}
4405+		entry.setText(guess);
4406+
4407+		dialog.setLayout(new BorderLayout());
4408+		dialog.add("North", label);
4409+		dialog.add("Center", entry);
4410+		dialog.add("South", ok);
4411+		dialog.pack();
4412+		dialog.resize(dialog.preferredSize());
4413+
4414+		dialog.show();	/* block here til OK or Cancel pressed. */
4415+		return;
4416+	}
4417+
4418+	public String getHost() {
4419+		int i = reply.indexOf(":");
4420+		if (i < 0) {
4421+			return "unknown";
4422+		}
4423+		String h = reply.substring(0, i);
4424+		return h;
4425+	}
4426+
4427+	public int getPort() {
4428+		int i = reply.indexOf(":");
4429+		int p = 8080;
4430+		if (i < 0) {
4431+			return p;
4432+		}
4433+		i++;
4434+		String ps = reply.substring(i);
4435+		try {
4436+			Integer I = new Integer(ps);
4437+			p = I.intValue();
4438+		} catch (Exception e) {
4439+			;
4440+		}
4441+		return p;
4442+	}
4443+
4444+	public synchronized void actionPerformed(ActionEvent evt) {
4445+		System.out.println(evt.getActionCommand());
4446+		if (evt.getSource() == ok) {
4447+			reply = entry.getText();
4448+			//dialog.dispose();
4449+			dialog.hide();
4450+		}
4451+	}
4452+}
4453+
4454+class ProxyPasswdDialog implements ActionListener {
4455+	String guessedHost = null;
4456+	String guessedPort = null;
4457+	String guessedUser = null;
4458+	String guessedPasswd = null;
4459+	String realm = null;
4460+	/*
4461+	 * this is the gui to show the user the cert and info and ask
4462+	 * them if they want to continue using this cert.
4463+	 */
4464+
4465+	Button ok;
4466+	Dialog dialog;
4467+	TextField entry1;
4468+	TextField entry2;
4469+	String reply1 = "";
4470+	String reply2 = "";
4471+
4472+	ProxyPasswdDialog (String h, int p, String realm) {
4473+		guessedHost = h;
4474+		try {
4475+			guessedPort = Integer.toString(p);
4476+		} catch (Exception e) {
4477+			guessedPort = "8080";
4478+		}
4479+		this.realm = realm;
4480+	}
4481+
4482+	public void queryUser() {
4483+
4484+		/* create and display the dialog for unverified cert. */
4485+
4486+		Frame frame = new Frame("Proxy Requires Username and Password");
4487+
4488+		dialog = new Dialog(frame, true);
4489+
4490+		//Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER);
4491+		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);
4492+		entry1 = new TextField(30);
4493+		entry2 = new TextField(30);
4494+		entry2.setEchoChar('*');
4495+		ok = new Button("OK");
4496+		ok.addActionListener(this);
4497+
4498+		dialog.setLayout(new BorderLayout());
4499+		dialog.add("North", label);
4500+		dialog.add("Center", entry1);
4501+		dialog.add("South",  entry2);
4502+		dialog.add("East", ok);
4503+		dialog.pack();
4504+		dialog.resize(dialog.preferredSize());
4505+
4506+		dialog.show();	/* block here til OK or Cancel pressed. */
4507+		return;
4508+	}
4509+
4510+	public String getAuth() {
4511+		return reply1 + ":" + reply2;
4512+	}
4513+
4514+	public synchronized void actionPerformed(ActionEvent evt) {
4515+		System.out.println(evt.getActionCommand());
4516+		if (evt.getSource() == ok) {
4517+			reply1 = entry1.getText();
4518+			reply2 = entry2.getText();
4519+			//dialog.dispose();
4520+			dialog.hide();
4521+		}
4522+	}
4523+}
4524+
4525+class ClientCertDialog implements ActionListener {
4526+
4527+	Button ok;
4528+	Dialog dialog;
4529+	TextField entry;
4530+	String reply = "";
4531+
4532+	ClientCertDialog() {
4533+		;
4534+	}
4535+
4536+	public String queryUser() {
4537+
4538+		/* create and display the dialog for unverified cert. */
4539+
4540+		Frame frame = new Frame("Enter SSL Client Cert+Key String");
4541+
4542+		dialog = new Dialog(frame, true);
4543+
4544+
4545+		Label label = new Label("Please Enter the SSL Client Cert+Key String 308204c0...,...522d2d0a", Label.CENTER);
4546+		entry = new TextField(30);
4547+		ok = new Button("OK");
4548+		ok.addActionListener(this);
4549+
4550+		dialog.setLayout(new BorderLayout());
4551+		dialog.add("North", label);
4552+		dialog.add("Center", entry);
4553+		dialog.add("South", ok);
4554+		dialog.pack();
4555+		dialog.resize(dialog.preferredSize());
4556+
4557+		dialog.show();	/* block here til OK or Cancel pressed. */
4558+		return reply;
4559+	}
4560+
4561+	public synchronized void actionPerformed(ActionEvent evt) {
4562+		System.out.println(evt.getActionCommand());
4563+		if (evt.getSource() == ok) {
4564+			reply = entry.getText();
4565+			//dialog.dispose();
4566+			dialog.hide();
4567+		}
4568+	}
4569+}
4570+
4571+class BrowserCertsDialog implements ActionListener {
4572+	Button yes, no;
4573+	Dialog dialog;
4574+	String vncServer;
4575+	String hostport;
4576+	public boolean showCertDialog = true;
4577+
4578+	BrowserCertsDialog(String serv, String hp) {
4579+		vncServer = serv;
4580+		hostport = hp;
4581+	}
4582+
4583+	public void queryUser() {
4584+
4585+		/* create and display the dialog for unverified cert. */
4586+
4587+		Frame frame = new Frame("Use Browser/JVM Certs?");
4588+
4589+		dialog = new Dialog(frame, true);
4590+
4591+		String m = "";
4592+m += "\n";
4593+m += "This VNC Viewer applet does not have its own keystore to track\n";
4594+m += "SSL certificates, and so cannot authenticate the certificate\n";
4595+m += "of the VNC Server:\n";
4596+m += "\n";
4597+m += "        " + hostport + "\n\n        " + vncServer + "\n";
4598+m += "\n";
4599+m += "on its own.\n";
4600+m += "\n";
4601+m += "However, it has noticed that your Web Browser and/or Java VM Plugin\n";
4602+m += "has previously accepted the same certificate.  You may have set\n";
4603+m += "this up permanently or just for this session, or the server\n";
4604+m += "certificate was signed by a CA cert that your Web Browser or\n";
4605+m += "Java VM Plugin has.\n";
4606+m += "\n";
4607+m += "If the VNC Server connection times out while you are reading this\n";
4608+m += "dialog, then restart the connection and try again.\n";
4609+m += "\n";
4610+m += "Should this VNC Viewer applet now connect to the above VNC server?\n";
4611+m += "\n";
4612+
4613+		TextArea textarea = new TextArea(m, 22, 64,
4614+		    TextArea.SCROLLBARS_VERTICAL_ONLY);
4615+		textarea.setEditable(false);
4616+		yes = new Button("Yes");
4617+		yes.addActionListener(this);
4618+		no = new Button("No, Let Me See the Certificate.");
4619+		no.addActionListener(this);
4620+
4621+		dialog.setLayout(new BorderLayout());
4622+		dialog.add("North", textarea);
4623+		dialog.add("Center", yes);
4624+		dialog.add("South", no);
4625+		dialog.pack();
4626+		dialog.resize(dialog.preferredSize());
4627+
4628+		dialog.show();	/* block here til Yes or No pressed. */
4629+		System.out.println("done show()");
4630+		return;
4631+	}
4632+
4633+	public synchronized void actionPerformed(ActionEvent evt) {
4634+		System.out.println(evt.getActionCommand());
4635+		if (evt.getSource() == yes) {
4636+			showCertDialog = false;
4637+			//dialog.dispose();
4638+			dialog.hide();
4639+		} else if (evt.getSource() == no) {
4640+			showCertDialog = true;
4641+			//dialog.dispose();
4642+			dialog.hide();
4643+		}
4644+		System.out.println("done actionPerformed()");
4645+	}
4646+}
4647+
4648+class CertInfo {
4649+	String fields[] = {"CN", "OU", "O", "L", "C"};
4650+	java.security.cert.Certificate cert;
4651+	String certString = "";
4652+
4653+	CertInfo(java.security.cert.Certificate c) {
4654+		cert = c;
4655+		certString = cert.toString();
4656+	}
4657+
4658+	String get_certinfo(String which) {
4659+		int i;
4660+		String cs = new String(certString);
4661+		String all = "";
4662+
4663+		/*
4664+		 * For now we simply scrape the cert string, there must
4665+		 * be an API for this... perhaps optionValue?
4666+		 */
4667+		for (i=0; i < fields.length; i++) {
4668+			int f, t, t1, t2;
4669+			String sub, mat = fields[i] + "=";
4670+
4671+			f = cs.indexOf(mat, 0);
4672+			if (f > 0) {
4673+				t1 = cs.indexOf(", ", f);
4674+				t2 = cs.indexOf("\n", f);
4675+				if (t1 < 0 && t2 < 0) {
4676+					continue;
4677+				} else if (t1 < 0) {
4678+					t = t2;
4679+				} else if (t2 < 0) {
4680+					t = t1;
4681+				} else if (t1 < t2) {
4682+					t = t1;
4683+				} else {
4684+					t = t2;
4685+				}
4686+				if (t > f) {
4687+					sub = cs.substring(f, t);
4688+					all = all + "        " + sub + "\n";
4689+					if (which.equals(fields[i])) {
4690+						return sub;
4691+					}
4692+				}
4693+			}
4694+		}
4695+		if (which.equals("all")) {
4696+			return all;
4697+		} else {
4698+			return "";
4699+		}
4700+	}
4701+}
4702+
4703+class Base64Coder {
4704+
4705+	// Mapping table from 6-bit nibbles to Base64 characters.
4706+	private static char[]    map1 = new char[64];
4707+	   static {
4708+	      int i=0;
4709+	      for (char c='A'; c<='Z'; c++) map1[i++] = c;
4710+	      for (char c='a'; c<='z'; c++) map1[i++] = c;
4711+	      for (char c='0'; c<='9'; c++) map1[i++] = c;
4712+	      map1[i++] = '+'; map1[i++] = '/'; }
4713+
4714+	// Mapping table from Base64 characters to 6-bit nibbles.
4715+	private static byte[]    map2 = new byte[128];
4716+	   static {
4717+	      for (int i=0; i<map2.length; i++) map2[i] = -1;
4718+	      for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; }
4719+
4720+	/**
4721+	* Encodes a string into Base64 format.
4722+	* No blanks or line breaks are inserted.
4723+	* @param s  a String to be encoded.
4724+	* @return   A String with the Base64 encoded data.
4725+	*/
4726+	public static String encodeString (String s) {
4727+	   return new String(encode(s.getBytes())); }
4728+
4729+	/**
4730+	* Encodes a byte array into Base64 format.
4731+	* No blanks or line breaks are inserted.
4732+	* @param in  an array containing the data bytes to be encoded.
4733+	* @return    A character array with the Base64 encoded data.
4734+	*/
4735+	public static char[] encode (byte[] in) {
4736+	   return encode(in,in.length); }
4737+
4738+	/**
4739+	* Encodes a byte array into Base64 format.
4740+	* No blanks or line breaks are inserted.
4741+	* @param in   an array containing the data bytes to be encoded.
4742+	* @param iLen number of bytes to process in <code>in</code>.
4743+	* @return     A character array with the Base64 encoded data.
4744+	*/
4745+	public static char[] encode (byte[] in, int iLen) {
4746+	   int oDataLen = (iLen*4+2)/3;       // output length without padding
4747+	   int oLen = ((iLen+2)/3)*4;         // output length including padding
4748+	   char[] out = new char[oLen];
4749+	   int ip = 0;
4750+	   int op = 0;
4751+	   while (ip < iLen) {
4752+	      int i0 = in[ip++] & 0xff;
4753+	      int i1 = ip < iLen ? in[ip++] & 0xff : 0;
4754+	      int i2 = ip < iLen ? in[ip++] & 0xff : 0;
4755+	      int o0 = i0 >>> 2;
4756+	      int o1 = ((i0 &   3) << 4) | (i1 >>> 4);
4757+	      int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
4758+	      int o3 = i2 & 0x3F;
4759+	      out[op++] = map1[o0];
4760+	      out[op++] = map1[o1];
4761+	      out[op] = op < oDataLen ? map1[o2] : '='; op++;
4762+	      out[op] = op < oDataLen ? map1[o3] : '='; op++; }
4763+	   return out; }
4764+
4765+	/**
4766+	* Decodes a string from Base64 format.
4767+	* @param s  a Base64 String to be decoded.
4768+	* @return   A String containing the decoded data.
4769+	* @throws   IllegalArgumentException if the input is not valid Base64 encoded data.
4770+	*/
4771+	public static String decodeString (String s) {
4772+	   return new String(decode(s)); }
4773+
4774+	/**
4775+	* Decodes a byte array from Base64 format.
4776+	* @param s  a Base64 String to be decoded.
4777+	* @return   An array containing the decoded data bytes.
4778+	* @throws   IllegalArgumentException if the input is not valid Base64 encoded data.
4779+	*/
4780+	public static byte[] decode (String s) {
4781+	   return decode(s.toCharArray()); }
4782+
4783+	/**
4784+	* Decodes a byte array from Base64 format.
4785+	* No blanks or line breaks are allowed within the Base64 encoded data.
4786+	* @param in  a character array containing the Base64 encoded data.
4787+	* @return    An array containing the decoded data bytes.
4788+	* @throws    IllegalArgumentException if the input is not valid Base64 encoded data.
4789+	*/
4790+	public static byte[] decode (char[] in) {
4791+	   int iLen = in.length;
4792+	   if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
4793+	   while (iLen > 0 && in[iLen-1] == '=') iLen--;
4794+	   int oLen = (iLen*3) / 4;
4795+	   byte[] out = new byte[oLen];
4796+	   int ip = 0;
4797+	   int op = 0;
4798+	   while (ip < iLen) {
4799+	      int i0 = in[ip++];
4800+	      int i1 = in[ip++];
4801+	      int i2 = ip < iLen ? in[ip++] : 'A';
4802+	      int i3 = ip < iLen ? in[ip++] : 'A';
4803+	      if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
4804+		 throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
4805+	      int b0 = map2[i0];
4806+	      int b1 = map2[i1];
4807+	      int b2 = map2[i2];
4808+	      int b3 = map2[i3];
4809+	      if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
4810+		 throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
4811+	      int o0 = ( b0       <<2) | (b1>>>4);
4812+	      int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
4813+	      int o2 = ((b2 &   3)<<6) |  b3;
4814+	      out[op++] = (byte)o0;
4815+	      if (op<oLen) out[op++] = (byte)o1;
4816+	      if (op<oLen) out[op++] = (byte)o2; }
4817+	   return out; }
4818+
4819+	// Dummy constructor.
4820+	private Base64Coder() {}
4821+
4822+}
4823diff -Naur JavaViewer.orig/VncCanvas.java JavaViewer/VncCanvas.java
4824--- JavaViewer.orig/VncCanvas.java	2005-11-21 18:50:18.000000000 -0500
4825+++ JavaViewer/VncCanvas.java	2010-11-30 22:57:50.000000000 -0500
4826@@ -27,6 +27,13 @@
4827 import java.lang.*;
4828 import java.util.zip.*;
4829
4830+// begin runge/x11vnc
4831+import java.util.Collections;
4832+// end runge/x11vnc
4833+
4834+// begin runge/x11vnc
4835+// all the MouseWheel stuff below.
4836+// end runge/x11vnc
4837
4838 //
4839 // VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
4840@@ -34,7 +41,7 @@
4841
4842 class VncCanvas
4843 	extends Canvas
4844-	implements KeyListener, MouseListener, MouseMotionListener {
4845+	implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener {
4846
4847 	VncViewer viewer;
4848 	RfbProto rfb;
4849@@ -85,6 +92,22 @@
4850
4851 		cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
4852
4853+// begin runge/x11vnc
4854+// kludge to not show any Java cursor in the canvas since we are
4855+// showing the soft cursor (should be a user setting...)
4856+Cursor dot = Toolkit.getDefaultToolkit().createCustomCursor(
4857+    Toolkit.getDefaultToolkit().createImage(new byte[4]), new Point(0,0),
4858+    "dot");
4859+this.setCursor(dot);
4860+
4861+// while we are at it... get rid of the keyboard traversals that
4862+// make it so we can't type a Tab character:
4863+this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
4864+    Collections.EMPTY_SET);
4865+this.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
4866+    Collections.EMPTY_SET);
4867+// end runge/x11vnc
4868+
4869 		colors = new Color[256];
4870 		// sf@2005 - Now Default
4871 		for (int i = 0; i < 256; i++)
4872@@ -186,6 +209,7 @@
4873 			inputEnabled = true;
4874 			addMouseListener(this);
4875 			addMouseMotionListener(this);
4876+			addMouseWheelListener(this);
4877 			if (viewer.showControls) {
4878 				viewer.buttonPanel.enableRemoteAccessControls(true);
4879 			}
4880@@ -193,6 +217,7 @@
4881 			inputEnabled = false;
4882 			removeMouseListener(this);
4883 			removeMouseMotionListener(this);
4884+			removeMouseWheelListener(this);
4885 			if (viewer.showControls) {
4886 				viewer.buttonPanel.enableRemoteAccessControls(false);
4887 			}
4888@@ -202,6 +227,9 @@
4889
4890 	public void setPixelFormat() throws IOException {
4891 		// sf@2005 - Adding more color modes
4892+		if (viewer.graftFtp) {
4893+			return;
4894+		}
4895 		if (viewer.options.eightBitColors > 0)
4896 		{
4897 			viewer.options.oldEightBitColors = viewer.options.eightBitColors;
4898@@ -237,6 +265,9 @@
4899 		}
4900 		else
4901 		{
4902+// begin runge/x11vnc
4903+			viewer.options.oldEightBitColors = viewer.options.eightBitColors;
4904+// end runge/x11vnc
4905 			rfb.writeSetPixelFormat(
4906 				32,
4907 				24,
4908@@ -376,12 +407,14 @@
4909 		// Start/stop session recording if necessary.
4910 		viewer.checkRecordingStatus();
4911
4912-		rfb.writeFramebufferUpdateRequest(
4913-			0,
4914-			0,
4915-			rfb.framebufferWidth,
4916-			rfb.framebufferHeight,
4917-			false);
4918+		if (!viewer.graftFtp) {
4919+			rfb.writeFramebufferUpdateRequest(
4920+				0,
4921+				0,
4922+				rfb.framebufferWidth,
4923+				rfb.framebufferHeight,
4924+				false);
4925+		}
4926
4927 		//
4928 		// main dispatch loop
4929@@ -390,6 +423,9 @@
4930 		while (true) {
4931 			// Read message type from the server.
4932 			int msgType = rfb.readServerMessageType();
4933+			if (viewer.ftpOnly && msgType != RfbProto.rfbFileTransfer) {
4934+				System.out.println("msgType:" + msgType);
4935+			}
4936
4937 			// Process the message depending on its type.
4938 			switch (msgType) {
4939@@ -1332,6 +1368,9 @@
4940 	public void mouseDragged(MouseEvent evt) {
4941 		processLocalMouseEvent(evt, true);
4942 	}
4943+	public void mouseWheelMoved(MouseWheelEvent evt) {
4944+		processLocalMouseWheelEvent(evt);
4945+	}
4946
4947 	public void processLocalKeyEvent(KeyEvent evt) {
4948 		if (viewer.rfb != null && rfb.inNormalProtocol) {
4949@@ -1367,6 +1406,19 @@
4950 		evt.consume();
4951 	}
4952
4953+	public void processLocalMouseWheelEvent(MouseWheelEvent evt) {
4954+		if (viewer.rfb != null && rfb.inNormalProtocol) {
4955+			synchronized(rfb) {
4956+			try {
4957+				rfb.writeWheelEvent(evt);
4958+			} catch (Exception e) {
4959+				e.printStackTrace();
4960+			}
4961+				rfb.notify();
4962+			}
4963+		}
4964+	}
4965+
4966 	public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
4967 		if (viewer.rfb != null && rfb.inNormalProtocol) {
4968 			if (moved) {
4969@@ -1532,9 +1584,14 @@
4970 							else
4971 							{
4972 								result =
4973-									0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF)
4974-										<< 16 | (pixBuf[i * 4 + 2] & 0xFF)
4975-										<< 8 | (pixBuf[i * 4 + 3] & 0xFF);
4976+// begin runge/x11vnc
4977+//									0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF)
4978+//										<< 16 | (pixBuf[i * 4 + 2] & 0xFF)
4979+//										<< 8 | (pixBuf[i * 4 + 3] & 0xFF);
4980+									0xFF000000 | (pixBuf[i * 4 + 2] & 0xFF)
4981+										<< 16 | (pixBuf[i * 4 + 1] & 0xFF)
4982+										<< 8 | (pixBuf[i * 4 + 0] & 0xFF);
4983+// end runge/x11vnc
4984 							}
4985 						} else {
4986 							result = 0; // Transparent pixel
4987@@ -1565,9 +1622,14 @@
4988 						else
4989 						{
4990 							result =
4991-								0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF)
4992-									<< 16 | (pixBuf[i * 4 + 2] & 0xFF)
4993-									<< 8 | (pixBuf[i * 4 + 3] & 0xFF);
4994+// begin runge/x11vnc
4995+//								0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF)
4996+//									<< 16 | (pixBuf[i * 4 + 2] & 0xFF)
4997+//									<< 8 | (pixBuf[i * 4 + 3] & 0xFF);
4998+								0xFF000000 | (pixBuf[i * 4 + 2] & 0xFF)
4999+									<< 16 | (pixBuf[i * 4 + 1] & 0xFF)
5000+									<< 8 | (pixBuf[i * 4 + 0] & 0xFF);
5001+// end runge/x11vnc
5002 						}
5003 					} else {
5004 						result = 0; // Transparent pixel
5005diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
5006--- JavaViewer.orig/VncViewer.java	2006-05-24 15:14:40.000000000 -0400
5007+++ JavaViewer/VncViewer.java	2010-03-27 18:00:28.000000000 -0400
5008@@ -41,6 +41,7 @@
5009 import java.io.*;
5010 import java.net.*;
5011 import javax.swing.*;
5012+import java.util.Date;
5013
5014 public class VncViewer extends java.applet.Applet
5015   implements java.lang.Runnable, WindowListener {
5016@@ -80,11 +81,11 @@
5017   GridBagLayout gridbag;
5018   ButtonPanel buttonPanel;
5019   AuthPanel authenticator;
5020-  VncCanvas vc;
5021+  VncCanvas vc = null;
5022   OptionsFrame options;
5023   ClipboardFrame clipboard;
5024   RecordingFrame rec;
5025-  FTPFrame ftp; // KMC: FTP Frame declaration
5026+  FTPFrame ftp = null; // KMC: FTP Frame declaration
5027
5028   // Control session recording.
5029   Object recordingSync;
5030@@ -96,7 +97,7 @@
5031
5032   // Variables read from parameter values.
5033   String host;
5034-  int port;
5035+  int port, vncserverport;
5036   String passwordParam;
5037   String encPasswordParam;
5038   boolean showControls;
5039@@ -115,28 +116,75 @@
5040   int i;
5041   // mslogon support 2 end
5042
5043+// begin runge/x11vnc
5044+boolean disableSSL;
5045+boolean GET;
5046+String CONNECT;
5047+String urlPrefix;
5048+String httpsPort;
5049+String oneTimeKey;
5050+String serverCert;
5051+String ftpDropDown;
5052+String proxyHost;
5053+String proxyPort;
5054+boolean forceProxy;
5055+boolean ignoreProxy;
5056+boolean trustAllVncCerts;
5057+boolean trustUrlVncCert;
5058+boolean debugCerts;
5059+boolean debugKeyboard;
5060+boolean mapF5_to_atsign;
5061+boolean forbid_Ctrl_Alt;
5062+
5063+boolean ignoreMSLogonCheck;
5064+boolean delayAuthPanel;
5065+boolean ftpOnly;
5066+boolean graftFtp;
5067+boolean dsmActive;
5068+
5069+boolean gotAuth;
5070+int authGot;
5071+// end runge/x11vnc
5072+
5073+
5074   //
5075   // init()
5076   //
5077
5078+public void ftp_init() {
5079+	boolean show = false;
5080+	if (ftp != null) {
5081+		show = true;
5082+	}
5083+	ftp = null;
5084+
5085+	ftp = new FTPFrame(this); // KMC: FTPFrame creation
5086+
5087+	if (show) {
5088+		ftp.doOpen();
5089+		rfb.readServerDriveList();
5090+	}
5091+}
5092+
5093   public void init() {
5094
5095     readParameters();
5096
5097     if (inSeparateFrame) {
5098-      vncFrame = new Frame("Ultr@VNC");
5099-      if (!inAnApplet) {
5100-	vncFrame.add("Center", this);
5101-      }
5102-      vncContainer = vncFrame;
5103+	vncFrame = new Frame("Ultr@VNC");
5104+	if (!inAnApplet) {
5105+		vncFrame.add("Center", this);
5106+	}
5107+	vncContainer = vncFrame;
5108     } else {
5109-      vncContainer = this;
5110+	vncContainer = this;
5111     }
5112
5113     recordingSync = new Object();
5114
5115     options = new OptionsFrame(this);
5116     clipboard = new ClipboardFrame(this);
5117+
5118     // authenticator = new AuthPanel(false);   // mslogon support : go to connectAndAuthenticate()
5119     if (RecordingFrame.checkSecurity())
5120       rec = new RecordingFrame(this);
5121@@ -147,10 +195,11 @@
5122     cursorUpdatesDef = null;
5123     eightBitColorsDef = null;
5124
5125-    if (inSeparateFrame)
5126+    if (inSeparateFrame && vncFrame != null)
5127       vncFrame.addWindowListener(this);
5128
5129-    ftp = new FTPFrame(this); // KMC: FTPFrame creation
5130+    ftp_init();
5131+
5132     rfbThread = new Thread(this);
5133     rfbThread.start();
5134   }
5135@@ -186,6 +235,30 @@
5136       gbc.weightx = 1.0;
5137       gbc.weighty = 1.0;
5138
5139+	if (ftpOnly) {
5140+		if (showControls) {
5141+			buttonPanel.enableButtons();
5142+		}
5143+		ActionListener taskPerformer = new ActionListener() {
5144+			public void actionPerformed(ActionEvent evt) {
5145+				vncFrame.setVisible(false);
5146+				ftp.setSavedLocations();
5147+				if (ftp.isVisible()) {
5148+					ftp.doClose();
5149+				} else {
5150+					ftp.doOpen();
5151+				}
5152+				rfb.readServerDriveList();
5153+			}
5154+		};
5155+		Timer t = new Timer(300, taskPerformer);
5156+		t.setRepeats(false);
5157+		t.start();
5158+
5159+		vc.processNormalProtocol();
5160+		return;
5161+	}
5162+
5163 	// Add ScrollPanel to applet mode
5164
5165 	// Create a panel which itself is resizeable and can hold
5166@@ -286,6 +359,24 @@
5167
5168   void connectAndAuthenticate() throws Exception {
5169
5170+	if (graftFtp) {
5171+		rfb = new RfbProto(host, port, this);
5172+		rfb.desktopName = "ftponly";
5173+		rfb.framebufferWidth  = 12;
5174+		rfb.framebufferHeight = 12;
5175+		rfb.bitsPerPixel = 32;
5176+		rfb.depth = 24;
5177+		rfb.trueColour = true;
5178+		rfb.redMax   = 255;
5179+		rfb.greenMax = 255;
5180+		rfb.blueMax  = 255;
5181+		rfb.redShift   = 16;
5182+		rfb.greenShift = 8;
5183+		rfb.blueShift  = 0;
5184+		rfb.inNormalProtocol = true;
5185+		return;
5186+	}
5187+
5188     // If "ENCPASSWORD" parameter is set, decrypt the password into
5189     // the passwordParam string.
5190
5191@@ -336,7 +427,22 @@
5192     //
5193
5194
5195-    prologueDetectAuthProtocol() ;
5196+// begin runge/x11vnc
5197+	gotAuth = false;
5198+	if (delayAuthPanel) {
5199+      		if (tryAuthenticate(null, null)) {
5200+			if (inSeparateFrame) {
5201+				vncFrame.pack();
5202+				vncFrame.show();
5203+			}
5204+      			return;
5205+      		}
5206+	}
5207+//  prologueDetectAuthProtocol() ;
5208+    if (ignoreMSLogonCheck == false) {
5209+        prologueDetectAuthProtocol() ;
5210+    }
5211+// end runge/x11vnc
5212
5213     authenticator = new AuthPanel(mslogon);
5214
5215@@ -371,6 +477,7 @@
5216      //mslogon support end
5217     }
5218
5219+    int tries = 0;
5220     while (true) {
5221       // Wait for user entering a password, or a username and a password
5222       synchronized(authenticator) {
5223@@ -390,6 +497,13 @@
5224 	break;
5225       //mslogon support end
5226
5227+// begin runge/x11vnc
5228+      gotAuth = false;
5229+      if (++tries > 2) {
5230+         throw new Exception("Incorrect password entered " + tries + " times.");
5231+      }
5232+// end runge/x11vnc
5233+
5234       // Retry on authentication failure.
5235       authenticator.retry();
5236     }
5237@@ -405,9 +519,11 @@
5238
5239   void prologueDetectAuthProtocol() throws Exception {
5240
5241-    rfb = new RfbProto(host, port, this);
5242+	if (!gotAuth) {
5243+		rfb = new RfbProto(host, port, this);
5244
5245-    rfb.readVersionMsg();
5246+		rfb.readVersionMsg();
5247+	}
5248
5249     System.out.println("RFB server supports protocol version " +
5250 		       rfb.serverMajor + "." + rfb.serverMinor);
5251@@ -431,16 +547,36 @@
5252
5253   boolean tryAuthenticate(String us, String pw) throws Exception {
5254
5255-    rfb = new RfbProto(host, port, this);
5256+	int authScheme;
5257
5258-    rfb.readVersionMsg();
5259+	if (!gotAuth) {
5260+		rfb = new RfbProto(host, port, this);
5261
5262-    System.out.println("RFB server supports protocol version " +
5263-		       rfb.serverMajor + "." + rfb.serverMinor);
5264+		rfb.readVersionMsg();
5265
5266-    rfb.writeVersionMsg();
5267+		System.out.println("RFB server supports protocol version: " +
5268+			       rfb.serverMajor + "." + rfb.serverMinor);
5269
5270-    int authScheme = rfb.readAuthScheme();
5271+		rfb.writeVersionMsg();
5272+
5273+		authScheme = rfb.readAuthScheme();
5274+
5275+		gotAuth = true;
5276+		authGot = authScheme;
5277+	} else {
5278+		authScheme = authGot;
5279+	}
5280+// begin runge/x11vnc
5281+	if (delayAuthPanel && pw == null) {
5282+		if (authScheme == RfbProto.NoAuth) {
5283+			System.out.println("No authentication needed");
5284+			return true;
5285+		} else {
5286+			return false;
5287+		}
5288+	}
5289+System.out.println("as: " + authScheme);
5290+// end runge/x11vnc
5291
5292     switch (authScheme) {
5293
5294@@ -629,6 +765,10 @@
5295
5296   void doProtocolInitialisation() throws IOException {
5297
5298+	if (graftFtp) {
5299+		return;
5300+	}
5301+
5302     rfb.writeClientInit();
5303
5304     rfb.readServerInit();
5305@@ -774,9 +914,28 @@
5306 	fatalError("HOST parameter not specified");
5307       }
5308     }
5309+    Date d = new Date();
5310+    System.out.println("-\nSSL VNC Java Applet starting.  " + d);
5311
5312-    String str = readParameter("PORT", true);
5313-    port = Integer.parseInt(str);
5314+    port = 0;
5315+    String str = readParameter("PORT", false);
5316+    if (str != null) {
5317+	port = Integer.parseInt(str);
5318+    }
5319+    // When there is a proxy VNCSERVERPORT may be inaccessible (inside firewall).
5320+    vncserverport = 0;
5321+    str = readParameter("VNCSERVERPORT", false);
5322+    if (str != null) {
5323+	vncserverport = Integer.parseInt(str);
5324+    }
5325+    if (port == 0 && vncserverport == 0) {
5326+	fatalError("Neither PORT nor VNCSERVERPORT parameters specified");
5327+    }
5328+    if (port == 0) {
5329+	// Nevertheless, fall back to vncserverport if we have to.
5330+	System.out.println("using vncserverport: '" + vncserverport + "' for PORT.");
5331+	port = vncserverport;
5332+    }
5333
5334     if (inAnApplet) {
5335       str = readParameter("Open New Window", false);
5336@@ -804,6 +963,158 @@
5337     deferScreenUpdates = readIntParameter("Defer screen updates", 20);
5338     deferCursorUpdates = readIntParameter("Defer cursor updates", 10);
5339     deferUpdateRequests = readIntParameter("Defer update requests", 50);
5340+
5341+// begin runge/x11vnc
5342+    // SSL
5343+    disableSSL = false;
5344+    str = readParameter("DisableSSL", false);
5345+    if (str != null && str.equalsIgnoreCase("Yes"))
5346+      disableSSL = true;
5347+
5348+    httpsPort = readParameter("httpsPort", false);
5349+
5350+    // Extra GET, CONNECT string:
5351+    CONNECT = readParameter("CONNECT", false);
5352+    if (CONNECT != null) {
5353+	CONNECT = CONNECT.replaceAll(" ", ":");
5354+    }
5355+
5356+    GET = false;
5357+    str = readParameter("GET", false);
5358+    if (str != null && str.equalsIgnoreCase("Yes")) {
5359+      GET = true;
5360+    }
5361+    if (str != null && str.equalsIgnoreCase("1")) {
5362+      GET = true;
5363+    }
5364+
5365+    urlPrefix = readParameter("urlPrefix", false);
5366+    if (urlPrefix != null) {
5367+	urlPrefix = urlPrefix.replaceAll("%2F", "/");
5368+	urlPrefix = urlPrefix.replaceAll("%2f", "/");
5369+	urlPrefix = urlPrefix.replaceAll("_2F_", "/");
5370+	if (urlPrefix.indexOf("/") != 0) {
5371+		urlPrefix = "/" + urlPrefix;
5372+	}
5373+    } else {
5374+    	urlPrefix = "";
5375+    }
5376+    System.out.println("urlPrefix: '" + urlPrefix + "'");
5377+
5378+    ftpDropDown = readParameter("ftpDropDown", false);
5379+    if (ftpDropDown != null) {
5380+	ftpDropDown = ftpDropDown.replaceAll("%2F", "/");
5381+	ftpDropDown = ftpDropDown.replaceAll("%2f", "/");
5382+	ftpDropDown = ftpDropDown.replaceAll("_2F_", "/");
5383+	ftpDropDown = ftpDropDown.replaceAll("%20", " ");
5384+	System.out.println("ftpDropDown: '" + ftpDropDown + "'");
5385+    }
5386+
5387+
5388+    oneTimeKey = readParameter("oneTimeKey", false);
5389+    if (oneTimeKey != null) {
5390+    	System.out.println("oneTimeKey is set.");
5391+    }
5392+
5393+    serverCert = readParameter("serverCert", false);
5394+    if (serverCert != null) {
5395+    	System.out.println("serverCert is set.");
5396+    }
5397+
5398+    forceProxy = false;
5399+    proxyHost = null;
5400+    proxyPort = null;
5401+    str = readParameter("forceProxy", false);
5402+    if (str != null) {
5403+	    if (str.equalsIgnoreCase("Yes")) {
5404+		forceProxy = true;
5405+	    } else if (str.equalsIgnoreCase("No")) {
5406+		forceProxy = false;
5407+	    } else {
5408+		forceProxy = true;
5409+		String[] pieces = str.split(" ");
5410+		proxyHost = new String(pieces[0]);
5411+		if (pieces.length >= 2) {
5412+			proxyPort = new String(pieces[1]);
5413+		} else {
5414+			proxyPort = new String("8080");
5415+		}
5416+	    }
5417+    }
5418+    str = readParameter("proxyHost", false);
5419+    if (str != null) {
5420+	proxyHost = new String(str);
5421+    }
5422+    str = readParameter("proxyPort", false);
5423+    if (str != null) {
5424+	proxyPort = new String(str);
5425+    }
5426+    if (proxyHost != null && proxyPort == null) {
5427+    	proxyPort = new String("8080");
5428+    }
5429+
5430+    ignoreProxy = false;
5431+    str = readParameter("ignoreProxy", false);
5432+    if (str != null && str.equalsIgnoreCase("Yes")) {
5433+	ignoreProxy = true;
5434+    }
5435+
5436+    trustAllVncCerts = false;
5437+    str = readParameter("trustAllVncCerts", false);
5438+    if (str != null && str.equalsIgnoreCase("Yes")) {
5439+	trustAllVncCerts = true;
5440+    }
5441+    trustUrlVncCert = false;
5442+    str = readParameter("trustUrlVncCert", false);
5443+    if (str != null && str.equalsIgnoreCase("Yes")) {
5444+	trustUrlVncCert = true;
5445+    }
5446+    debugCerts = false;
5447+    str = readParameter("debugCerts", false);
5448+    if (str != null && str.equalsIgnoreCase("Yes")) {
5449+	debugCerts = true;
5450+    }
5451+    debugKeyboard = false;
5452+    str = readParameter("debugKeyboard", false);
5453+    if (str != null && str.equalsIgnoreCase("Yes")) {
5454+	debugKeyboard = true;
5455+    }
5456+    mapF5_to_atsign = false;
5457+    str = readParameter("mapF5_to_atsign", false);
5458+    if (str != null && str.equalsIgnoreCase("Yes")) {
5459+	mapF5_to_atsign = true;
5460+    }
5461+    forbid_Ctrl_Alt = false;
5462+    str = readParameter("forbid_Ctrl_Alt", false);
5463+    if (str != null && str.equalsIgnoreCase("Yes")) {
5464+	forbid_Ctrl_Alt = true;
5465+    }
5466+    ignoreMSLogonCheck = false;
5467+    str = readParameter("ignoreMSLogonCheck", false);
5468+    if (str != null && str.equalsIgnoreCase("Yes")) {
5469+	ignoreMSLogonCheck = true;
5470+    }
5471+    ftpOnly = false;
5472+    str = readParameter("ftpOnly", false);
5473+    if (str != null && str.equalsIgnoreCase("Yes")) {
5474+	ftpOnly = true;
5475+    }
5476+    graftFtp = false;
5477+    str = readParameter("graftFtp", false);
5478+    if (str != null && str.equalsIgnoreCase("Yes")) {
5479+	graftFtp = true;
5480+    }
5481+    dsmActive = false;
5482+    str = readParameter("dsmActive", false);
5483+    if (str != null && str.equalsIgnoreCase("Yes")) {
5484+	dsmActive = true;
5485+    }
5486+    delayAuthPanel = false;
5487+    str = readParameter("delayAuthPanel", false);
5488+    if (str != null && str.equalsIgnoreCase("Yes")) {
5489+	delayAuthPanel = true;
5490+    }
5491+// end runge/x11vnc
5492   }
5493
5494   public String readParameter(String name, boolean required) {
5495