diff -Naur JavaViewer.orig/ButtonPanel.java JavaViewer/ButtonPanel.java --- JavaViewer.orig/ButtonPanel.java 2004-12-12 20:51:02.000000000 -0500 +++ JavaViewer/ButtonPanel.java 2007-05-16 15:50:54.000000000 -0400 @@ -163,9 +163,12 @@ } else if (evt.getSource() == ftpButton) { +// begin runge/x11vnc + viewer.ftp.setSavedLocations(); +// end runge/x11vnc viewer.ftp.setVisible(!viewer.ftp.isVisible()); viewer.rfb.readServerDriveList(); - + } } } diff -Naur JavaViewer.orig/FTPFrame.java JavaViewer/FTPFrame.java --- JavaViewer.orig/FTPFrame.java 2005-03-15 23:53:14.000000000 -0500 +++ JavaViewer/FTPFrame.java 2007-05-16 16:05:15.000000000 -0400 @@ -26,6 +26,10 @@ import java.util.Vector; import javax.swing.*; +// begin runge/x11vnc +import java.util.Arrays; +// end runge/x11vnc + /* * Created on Feb 25, 2004 @@ -80,6 +84,17 @@ private File localSelection = null; // Holds the currently selected local file private String remoteSelection = null; // Holds the currently selected remote file public String selectedTable = null; + +// begin runge/x11vnc + private javax.swing.JButton viewButton = null; + public File saveLocalDirectory = null; + public long saveLocalDirectoryTime = 0; + public int saveLocalDirectoryCount = 0; + public String saveRemoteDirectory = null; + public long saveRemoteDirectoryTime = 0; + public int saveRemoteDirectoryCount = 0; + private boolean localCurrentIsDir = true; +// end runge/x11vnc // sf@2004 - Separate directories and files for better lisibility private ArrayList DirsList; @@ -125,11 +140,59 @@ void refreshRemoteLocation() { + remoteList.clear(); remoteFileTable.setListData(remoteList); +System.out.println("refreshRemoteLocation '" + remoteLocation.getText() + "'"); // runge/x11vnc viewer.rfb.readServerDirectory(remoteLocation.getText()); } +// begin runge/x11vnc + public void setSavedLocations() { + saveLocalDirectory = currentLocalDirectory; + saveLocalDirectoryTime = System.currentTimeMillis(); + saveLocalDirectoryCount = 0; + + if (remoteLocation != null) { + saveRemoteDirectory = remoteLocation.getText(); +System.out.println("RemoteSave '" + saveRemoteDirectory + "'"); + } + saveRemoteDirectoryTime = System.currentTimeMillis(); + saveRemoteDirectoryCount = 0; + } + + private File saveLocalHack(File dir) { + saveLocalDirectoryCount++; +System.out.println("L " + saveLocalDirectoryCount + " dt: " + (System.currentTimeMillis() - saveLocalDirectoryTime) + " - " + saveLocalDirectory); + if (System.currentTimeMillis() > saveLocalDirectoryTime + 2000 || saveLocalDirectoryCount > 2) { + saveLocalDirectory = null; + } + if (saveLocalDirectory != null) { + currentLocalDirectory = saveLocalDirectory; + localLocation.setText(saveLocalDirectory.toString()); + return saveLocalDirectory; + } else { + return dir; + } + } + + private String saveRemoteHack(String indrive) { + saveRemoteDirectoryCount++; +System.out.println("R " + saveRemoteDirectoryCount + " - " + saveRemoteDirectory); + if (saveRemoteDirectory != null && saveRemoteDirectoryCount > 1) { + saveRemoteDirectory = null; + } + if (saveRemoteDirectory != null) { + if (! saveRemoteDirectory.equals("")) { +System.out.println("saveRemoteHack setText + refreshRemoteLocation '" + saveRemoteDirectory + "'"); + return saveRemoteDirectory; + } + } + return indrive; + } +// end runge/x11vnc + + /* * Prints the list of drives on the remote directory and returns a String[]. * str takes as string like A:fC:lD:lE:lF:lG:cH:c @@ -185,6 +248,7 @@ stopButton.setVisible(true); stopButton.setEnabled(true); receiveButton.setEnabled(false); + viewButton.setEnabled(false); // runge/x11vnc remoteTopButton.setEnabled(false); sendButton.setEnabled(false); remoteFileTable.setEnabled(false); @@ -207,6 +271,7 @@ stopButton.setVisible(false); stopButton.setEnabled(false); receiveButton.setEnabled(true); + viewButton.setEnabled(true); // runge/x11vnc remoteTopButton.setEnabled(true); sendButton.setEnabled(true); remoteFileTable.setEnabled(true); @@ -405,6 +470,7 @@ buttonPanel = new javax.swing.JPanel(); buttonPanel.setLayout(null); buttonPanel.add(getReceiveButton(), null); + buttonPanel.add(getViewButton(), null); // runge/x11vnc buttonPanel.add(getNewFolderButton(), null); buttonPanel.add(getCloseButton(), null); buttonPanel.add(getDeleteButton(), null); @@ -422,7 +488,7 @@ private javax.swing.JButton getSendButton() { if (sendButton == null) { sendButton = new javax.swing.JButton(); - sendButton.setBounds(20, 30, 97, 25); + sendButton.setBounds(15, 30, 107, 25); // runge/x11vnc sendButton.setText("Send >>"); sendButton.setName("sendButton"); sendButton.addActionListener(this); @@ -438,7 +504,7 @@ private javax.swing.JButton getReceiveButton() { if (receiveButton == null) { receiveButton = new javax.swing.JButton(); - receiveButton.setBounds(20, 60, 97, 25); + receiveButton.setBounds(15, 60, 107, 25); // runge/x11vnc receiveButton.setText("<< Receive"); receiveButton.setName("receiveButton"); receiveButton.addActionListener(this); @@ -453,7 +519,7 @@ private javax.swing.JButton getDeleteButton() { if (deleteButton == null) { deleteButton = new javax.swing.JButton(); - deleteButton.setBounds(20, 110, 97, 25); + deleteButton.setBounds(15, 110, 107, 25); // runge/x11vnc deleteButton.setText("Delete File"); deleteButton.setName("deleteButton"); deleteButton.addActionListener(this); @@ -468,7 +534,7 @@ private javax.swing.JButton getNewFolderButton() { if (newFolderButton == null) { newFolderButton = new javax.swing.JButton(); - newFolderButton.setBounds(20, 140, 97, 25); + newFolderButton.setBounds(15, 140, 107, 25); // runge/x11vnc newFolderButton.setText("New Folder"); newFolderButton.setName("newFolderButton"); newFolderButton.addActionListener(this); @@ -476,6 +542,24 @@ return newFolderButton; } +// begin runge/x11vnc + /** + * This method initializes viewButton + * + * @return javax.swing.JButton + */ + private javax.swing.JButton getViewButton() { + if (viewButton == null) { + viewButton = new javax.swing.JButton(); + viewButton.setBounds(15, 170, 107, 25); + viewButton.setText("View File"); + viewButton.setName("viewButton"); + viewButton.addActionListener(this); + } + return viewButton; + } +// end runge/x11vnc + /** * This method initializes stopButton * @@ -486,7 +570,7 @@ if (stopButton == null) { stopButton = new javax.swing.JButton(); - stopButton.setBounds(20, 200, 97, 25); + stopButton.setBounds(15, 200, 107, 25); // runge/x11vnc stopButton.setText("Stop"); stopButton.setName("stopButton"); stopButton.addActionListener(this); @@ -503,7 +587,7 @@ private javax.swing.JButton getCloseButton() { if (closeButton == null) { closeButton = new javax.swing.JButton(); - closeButton.setBounds(20, 325, 97, 25); + closeButton.setBounds(15, 325, 107, 25); // runge/x11vnc closeButton.setText("Close"); closeButton.setName("closeButton"); closeButton.addActionListener(this); @@ -829,12 +913,19 @@ { doReceive(); } +// begin runge/x11vnc + else if (evt.getSource() == viewButton) + { + doView(); + } +// end runge/x11vnc else if (evt.getSource() == localDrivesComboBox) { changeLocalDrive(); } else if (evt.getSource() == remoteDrivesComboBox) { +System.out.println("remoteDrivesComboBox"); // runge/x11vnc changeRemoteDrive(); remoteList.clear(); remoteFileTable.setListData(remoteList); @@ -845,6 +936,7 @@ } else if (evt.getSource() == remoteTopButton) { +System.out.println("remoteTopButton"); // runge/x11vnc changeRemoteDrive(); } else if(evt.getSource() == deleteButton) @@ -979,6 +1071,56 @@ viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath); } +// begin runge/x11vnc + private void doView() + { + System.out.println("View Button Pressed"); + + if (selectedTable == null) { + return; + } + if (selectedTable.equals("remote")) { + viewRemote(); + } else if (selectedTable.equals("local")) { + viewLocal(); + } + } + + private File doReceiveTmp() + { + + if (remoteFileTable == null) { + return null; + } + String sFileName = ((String) this.remoteFileTable.getSelectedValue()); + if (sFileName == null) { + return null; + } + + // sf@2004 - Directory can't be transfered + if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) + { + return null; + } + + File tmp = null; + try { + tmp = File.createTempFile("ULTRAFTP", ".txt"); + } catch (Exception e) { + return null; + } + + //updateHistory("Downloaded " + localSelection.toString()); + String remoteFileName = this.remoteLocation.getText(); + remoteFileName+= ((String) this.remoteFileTable.getSelectedValue()).substring(1); + + String localDestinationPath = tmp.getAbsolutePath(); + viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath); + System.out.println("ReceiveTmp: " + localDestinationPath); + return tmp; + } +// end runge/x11vnc + private void doSend() { System.out.println("Send Button Pressed"); @@ -1035,8 +1177,11 @@ if (!updateDriveList) { String drive = remoteDrivesComboBox.getSelectedItem().toString().substring(0,1)+ ":\\"; +System.out.println("changeRemoteDrive-A " + drive); // runge/x11vnc + drive = saveRemoteHack(drive); viewer.rfb.readServerDirectory(drive); remoteLocation.setText(drive); +System.out.println("changeRemoteDrive-B " + drive); // runge/x11vnc } remoteList.clear(); remoteFileTable.setListData(remoteList); @@ -1048,6 +1193,7 @@ private void changeLocalDrive() { File currentDrive = new File(localDrivesComboBox.getSelectedItem().toString()); +System.out.println("changeLocalDrive " + currentDrive.toString()); // runge/x11vnc if(currentDrive.canRead()) { localSelection = null; @@ -1060,6 +1206,7 @@ localStatus.setText("WARNING: Drive " + localDrivesComboBox.getSelectedItem().toString()); connectionStatus.setText(" > WARNING - Local Drive unavailable (possibly restricted access or media not present)"); } + } /** * Determines which FileTable was double-clicked and updates the table @@ -1101,7 +1248,7 @@ String name = (remoteFileTable.getSelectedValue().toString()).substring(1); if( !name.substring(0, 2).equals(" [")) remoteSelection = remoteLocation.getText() + name.substring(0, name.length()); - + } /* @@ -1115,10 +1262,38 @@ localFileTable.setBackground(new Color(255, 255, 255)); File currentSelection = new File(currentLocalDirectory, getTrimmedSelection()); - if(currentSelection.isFile()) +// begin runge/x11vnc + // localSelection = currentSelection.getAbsoluteFile(); + if(currentSelection.isFile()) { localSelection = currentSelection.getAbsoluteFile(); + localCurrentIsDir = false; + } else { + localCurrentIsDir = true; + } +// end runge/x11vnc } + +// begin runge/x11vnc + private void viewRemote() { + File tmp = doReceiveTmp(); + if (tmp == null) { + return; + } + TextViewer tv = new TextViewer("Remote: " + remoteSelection, tmp, true); + } + private void viewLocal() { + if (localSelection == null) { + return; + } + if (localCurrentIsDir) { + return; + } + File loc = new File(localSelection.toString()); + TextViewer tv = new TextViewer("Local: " + localSelection.toString(), loc, false); + } +// end runge/x11vnc + /** * Updates the Remote File Table based on selection. Called from mouseClicked handler */ @@ -1149,6 +1324,7 @@ remoteSelection = remoteLocation.getText() + name.substring(0, name.length()); drive = remoteLocation.getText(); // ?? + viewRemote(); // runge/x11vnc } else { @@ -1163,6 +1339,7 @@ } //remoteLocation.setText(drive); } + /** * Updates the Local File Table based on selection. Called from MouseClicked handler */ @@ -1188,6 +1365,7 @@ else if (currentSelection.isFile()) { localSelection = currentSelection.getAbsoluteFile(); + viewLocal(); // runge/x11vnc } else if (currentSelection.isDirectory()) { @@ -1247,9 +1425,20 @@ */ public void changeLocalDirectory(File dir) { + dir = saveLocalHack(dir); // runge/x11vnc + currentLocalDirectory = dir; // Updates Global File allFiles[] = dir.listFiles(); // Reads files String[] contents = dir.list(); +// begin runge/x11vnc +System.out.println("changeLocalDirectory: " + dir.toString()); + if (contents != null) { + java.util.Arrays.sort(contents, String.CASE_INSENSITIVE_ORDER); + for (int i = 0; i < contents.length; i++) { + allFiles[i] = new File(dir, contents[i]); + } + } +// end runge/x11vnc localList.clear(); localList.addElement(" [..]"); @@ -1296,3 +1485,135 @@ } } // @jve:visual-info decl-index=0 visual-constraint="10,10" + +// begin runge/x11vnc +class TextViewer extends JFrame implements ActionListener { + + JTextArea textArea = new JTextArea(35, 80); + File file = null; + JButton refreshButton; + JButton dismissButton; + Timer tim = null; + int rcnt = 0; + int tms = 250; + boolean delete_it = false; + + public TextViewer(String s, File f, boolean d) { + + delete_it = d; + file = f; + + JScrollPane scrollPane = new JScrollPane(textArea, + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + + textArea.setEditable(false); + textArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); + + + refreshButton = new JButton(); + refreshButton.setText("Reload"); + refreshButton.setName("refreshButton"); + refreshButton.addActionListener(this); + + dismissButton = new JButton(); + dismissButton.setText("Dismiss"); + dismissButton.setName("dismissButton"); + dismissButton.addActionListener(this); + + JPanel buttons = new JPanel(); + buttons.setLayout(new BorderLayout()); + buttons.add(refreshButton, BorderLayout.WEST); + buttons.add(dismissButton, BorderLayout.EAST); + + JPanel content = new JPanel(); + content.setLayout(new BorderLayout()); + content.add(scrollPane, BorderLayout.CENTER); + content.add(buttons, BorderLayout.SOUTH); + + ActionListener tsk = new ActionListener() { + public void actionPerformed(ActionEvent evt) { + // System.out.println("tsk"); + refresh(); + } + }; + tim = new Timer(tms, tsk); + tim.start(); + + this.setContentPane(content); + this.setTitle("TextViewer - " + s); + this.pack(); + this.setVisible(true); + } + + private void refresh() { + + rcnt++; + if (rcnt * tms > 3000 && tim != null) { + tim.stop(); + tim = null; + } + BufferedReader input = null; + StringBuffer contents = new StringBuffer(); + try { + if (input == null) { + input = new BufferedReader(new FileReader(file)); + } + String line = null; + int i = 0; + while (( line = input.readLine()) != null) { + if (i == 0) { + // System.out.println("read"); + } + i++; + contents.append(line); + contents.append(System.getProperty("line.separator")); + } + } catch (Exception e) { + ; + } finally { + try { + if (input != null) { + input.close(); + input = null; + } + } catch (Exception e) { + ; + } + } + + textArea.setText(contents.toString()); + textArea.setCaretPosition(0); + } + + public void actionPerformed(ActionEvent evt) { + + if (evt.getSource() == refreshButton) { + refresh(); + } + if (evt.getSource() == dismissButton) { + cleanse(); + this.dispose(); + } + } + + private void cleanse() { + if (delete_it && file != null) { + try { + file.delete(); + file = null; + } catch (Exception e) { + ; + } + } + } + + protected void finalize() throws Throwable { + try { + cleanse(); + } finally { + super.finalize(); + } + } +} +// end runge/x11vnc diff -Naur JavaViewer.orig/OptionsFrame.java JavaViewer/OptionsFrame.java --- JavaViewer.orig/OptionsFrame.java 2005-11-21 18:50:16.000000000 -0500 +++ JavaViewer/OptionsFrame.java 2007-05-13 22:18:30.000000000 -0400 @@ -144,7 +144,10 @@ choices[jpegQualityIndex].select("6"); choices[cursorUpdatesIndex].select("Enable"); choices[useCopyRectIndex].select("Yes"); - choices[eightBitColorsIndex].select("64"); +// begin runge/x11vnc +// choices[eightBitColorsIndex].select("64"); + choices[eightBitColorsIndex].select("Full"); +// end runge/x11vnc choices[mouseButtonIndex].select("Normal"); choices[viewOnlyIndex].select("No"); choices[shareDesktopIndex].select("Yes"); diff -Naur JavaViewer.orig/RfbProto.java JavaViewer/RfbProto.java --- JavaViewer.orig/RfbProto.java 2006-05-24 15:14:40.000000000 -0400 +++ JavaViewer/RfbProto.java 2007-05-16 15:57:07.000000000 -0400 @@ -199,6 +199,10 @@ // playback. int numUpdatesInSession; +// begin runge/x11vnc + int readServerDriveListCnt = -1; + long readServerDriveListTime = 0; +// end runge/x11vnc // // Constructor. Make TCP connection to RFB server. // @@ -207,7 +211,27 @@ viewer = v; host = h; port = p; - sock = new Socket(host, port); +// begin runge/x11vnc +// sock = new Socket(host, port); + if (! viewer.disableSSL) { + System.out.println("new SSLSocketToMe"); + SSLSocketToMe ssl; + try { + ssl = new SSLSocketToMe(host, port, v); + } catch (Exception e) { + throw new IOException(e.getMessage()); + } + + try { + sock = ssl.connectSock(); + } catch (Exception es) { + throw new IOException(es.getMessage()); + } + } else { + sock = new Socket(host, port); + } +// end runge/x11vnc + is = new DataInputStream( new BufferedInputStream(sock.getInputStream(), 16384)); @@ -1263,11 +1287,47 @@ || dwFileAttributes == 369623040) { fileName = " [" + fileName + "]"; - remoteDirsList.add(fileName); // sf@2004 +// begin runge/x11vnc +// remoteDirsList.add(fileName); // sf@2004 + int i = -1; + String t1 = fileName.toLowerCase(); + for (int j = 0; j < remoteDirsList.size(); j++) { + String t = (String) remoteDirsList.get(j); + String t2 = t.toLowerCase(); + if (t1.compareTo(t2) < 0) { + i = j; + break; + } + } + if (i >= 0) { + remoteDirsList.add(i, fileName); + } else { + remoteDirsList.add(fileName); + } +// end runge/x11vnc } else { - remoteFilesList.add(" " + fileName); // sf@2004 +// begin runge/x11vnc +// remoteFilesList.add(" " + fileName); // sf@2004 + + fileName = " " + fileName; + int i = -1; + String t1 = fileName.toLowerCase(); + for (int j = 0; j < remoteFilesList.size(); j++) { + String t = (String) remoteFilesList.get(j); + String t2 = t.toLowerCase(); + if (t1.compareTo(t2) < 0) { + i = j; + break; + } + } + if (i >= 0) { + remoteFilesList.add(i, fileName); + } else { + remoteFilesList.add(fileName); + } +// end runge/x11vnc } // a.add(fileName); @@ -1289,6 +1349,16 @@ remoteDirsList.clear(); remoteFilesList.clear(); +// begin runge/x11vnc + // Hack for double listing at startup... probably libvncserver bug.. + readServerDriveListCnt++; + if (readServerDriveListCnt == 2) { + if (System.currentTimeMillis() - readServerDriveListTime < 1500) { +System.out.println("readServerDriveListCnt skip " + readServerDriveListCnt); + return; + } + } +// end runge/x11vnc viewer.ftp.printDirectory(a); } @@ -1312,6 +1382,10 @@ 0, 0, null); +// begin runge/x11vnc + readServerDriveListCnt = 0; + readServerDriveListTime = System.currentTimeMillis(); +// end runge/x11vnc } catch (IOException e) { diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java --- JavaViewer.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500 +++ JavaViewer/SSLSocketToMe.java 2007-04-29 20:40:35.000000000 -0400 @@ -0,0 +1,1421 @@ +/* + * SSLSocketToMe.java: add SSL encryption to Java VNC Viewer. + * + * Copyright (c) 2006 Karl J. Runge + * All rights reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + */ + +import java.net.*; +import java.io.*; +import javax.net.ssl.*; +import java.util.*; + +import java.security.*; +import java.security.cert.*; +import java.security.spec.*; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; + +import java.awt.*; +import java.awt.event.*; + +public class SSLSocketToMe { + + /* basic member data: */ + String host; + int port; + VncViewer viewer; + boolean debug = true; + + /* sockets */ + SSLSocket socket = null; + SSLSocketFactory factory; + + /* fallback for Proxy connection */ + boolean proxy_in_use = false; + boolean proxy_is_https = false; + boolean proxy_failure = false; + public DataInputStream is = null; + public OutputStream os = null; + + String proxy_dialog_host = null; + int proxy_dialog_port = 0; + + Socket proxySock; + DataInputStream proxy_is; + OutputStream proxy_os; + + /* trust contexts */ + SSLContext trustloc_ctx; + SSLContext trustall_ctx; + SSLContext trusturl_ctx; + SSLContext trustone_ctx; + + TrustManager[] trustAllCerts; + TrustManager[] trustUrlCert; + TrustManager[] trustOneCert; + + boolean use_url_cert_for_auth = true; + boolean user_wants_to_see_cert = true; + + /* cert(s) we retrieve from VNC server */ + java.security.cert.Certificate[] trustallCerts = null; + java.security.cert.Certificate[] trusturlCerts = null; + + byte[] hex2bytes(String s) { + byte[] bytes = new byte[s.length()/2]; + for (int i=0; i 127) { + val -= 256; + } + Integer I = new Integer(val); + bytes[i] = Byte.decode(I.toString()).byteValue(); + + } catch (Exception e) { + ; + } + } + return bytes; + } + + SSLSocketToMe(String h, int p, VncViewer v) throws Exception { + host = h; + port = p; + viewer = v; + + /* we will first try default factory for certification: */ + + factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); + + dbg("SSL startup: " + host + " " + port); + + /* create trust managers used if initial handshake fails: */ + + trustAllCerts = new TrustManager[] { + /* + * this one accepts everything. + */ + new X509TrustManager() { + public java.security.cert.X509Certificate[] + getAcceptedIssuers() { + return null; + } + public void checkClientTrusted( + java.security.cert.X509Certificate[] certs, + String authType) { + /* empty */ + } + public void checkServerTrusted( + java.security.cert.X509Certificate[] certs, + String authType) { + /* empty */ + dbg("ALL: an untrusted connect to grab cert."); + } + } + }; + + trustUrlCert = new TrustManager[] { + /* + * this one accepts only the retrieved server cert + * by SSLSocket by this applet. + */ + new X509TrustManager() { + public java.security.cert.X509Certificate[] + getAcceptedIssuers() { + return null; + } + public void checkClientTrusted( + java.security.cert.X509Certificate[] certs, + String authType) throws CertificateException { + throw new CertificateException("No Clients"); + } + public void checkServerTrusted( + java.security.cert.X509Certificate[] certs, + String authType) throws CertificateException { + if (trusturlCerts == null) { + throw new CertificateException( + "No Trust url Certs array."); + } + if (trusturlCerts.length < 1) { + throw new CertificateException( + "No Trust url Certs."); + } + if (trusturlCerts.length > 1) { + int i; + boolean ok = true; + for (i = 0; i < trusturlCerts.length - 1; i++) { + if (! trusturlCerts[i].equals(trusturlCerts[i+1])) { + ok = false; + } + } + if (! ok) { + throw new CertificateException( + "Too many Trust url Certs: " + + trusturlCerts.length + ); + } + } + if (certs == null) { + throw new CertificateException( + "No this-certs array."); + } + if (certs.length < 1) { + throw new CertificateException( + "No this-certs Certs."); + } + if (certs.length > 1) { + int i; + boolean ok = true; + for (i = 0; i < certs.length - 1; i++) { + if (! certs[i].equals(certs[i+1])) { + ok = false; + } + } + if (! ok) { + throw new CertificateException( + "Too many this-certs: " + + certs.length + ); + } + } + if (! trusturlCerts[0].equals(certs[0])) { + throw new CertificateException( + "Server Cert Changed != URL."); + } + dbg("URL: trusturlCerts[0] matches certs[0]"); + } + } + }; + trustOneCert = new TrustManager[] { + /* + * this one accepts only the retrieved server cert + * by SSLSocket by this applet. + */ + new X509TrustManager() { + public java.security.cert.X509Certificate[] + getAcceptedIssuers() { + return null; + } + public void checkClientTrusted( + java.security.cert.X509Certificate[] certs, + String authType) throws CertificateException { + throw new CertificateException("No Clients"); + } + public void checkServerTrusted( + java.security.cert.X509Certificate[] certs, + String authType) throws CertificateException { + if (trustallCerts == null) { + throw new CertificateException( + "No Trust All Server Certs array."); + } + if (trustallCerts.length < 1) { + throw new CertificateException( + "No Trust All Server Certs."); + } + if (trustallCerts.length > 1) { + int i; + boolean ok = true; + for (i = 0; i < trustallCerts.length - 1; i++) { + if (! trustallCerts[i].equals(trustallCerts[i+1])) { + ok = false; + } + } + if (! ok) { + throw new CertificateException( + "Too many Trust All Server Certs: " + + trustallCerts.length + ); + } + } + if (certs == null) { + throw new CertificateException( + "No this-certs array."); + } + if (certs.length < 1) { + throw new CertificateException( + "No this-certs Certs."); + } + if (certs.length > 1) { + int i; + boolean ok = true; + for (i = 0; i < certs.length - 1; i++) { + if (! certs[i].equals(certs[i+1])) { + ok = false; + } + } + if (! ok) { + throw new CertificateException( + "Too many this-certs: " + + certs.length + ); + } + } + if (! trustallCerts[0].equals(certs[0])) { + throw new CertificateException( + "Server Cert Changed != TRUSTALL."); + } + dbg("ONE: trustallCerts[0] matches certs[0]"); + } + } + }; + + /* + * They are used: + * + * 1) to retrieve the server cert in case of failure to + * display it to the user. + * 2) to subsequently connect to the server if user agrees. + */ + + KeyManager[] mykey = null; + + if (viewer.oneTimeKey != null && viewer.oneTimeKey.equals("PROMPT")) { + ClientCertDialog d = new ClientCertDialog(); + viewer.oneTimeKey = d.queryUser(); + } + + if (viewer.oneTimeKey != null && viewer.oneTimeKey.indexOf(",") > 0) { + int idx = viewer.oneTimeKey.indexOf(","); + + String onetimekey = viewer.oneTimeKey.substring(0, idx); + byte[] key = hex2bytes(onetimekey); + String onetimecert = viewer.oneTimeKey.substring(idx+1); + byte[] cert = hex2bytes(onetimecert); + + KeyFactory kf = KeyFactory.getInstance("RSA"); + PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key ); + PrivateKey ff = kf.generatePrivate (keysp); + //dbg("ff " + ff); + String cert_str = new String(cert); + + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + Collection c = cf.generateCertificates(new ByteArrayInputStream(cert)); + Certificate[] certs = new Certificate[c.toArray().length]; + if (c.size() == 1) { + Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert)); + //dbg("tmpcert" + tmpcert); + certs[0] = tmpcert; + } else { + certs = (Certificate[]) c.toArray(); + } + + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, null); + ks.setKeyEntry("onetimekey", ff, "".toCharArray(), certs); + String da = KeyManagerFactory.getDefaultAlgorithm(); + KeyManagerFactory kmf = KeyManagerFactory.getInstance(da); + kmf.init(ks, "".toCharArray()); + + mykey = kmf.getKeyManagers(); + } + + + /* trust loc certs: */ + try { + trustloc_ctx = SSLContext.getInstance("SSL"); + trustloc_ctx.init(mykey, null, new + java.security.SecureRandom()); + + } catch (Exception e) { + String msg = "SSL trustloc_ctx FAILED."; + dbg(msg); + throw new Exception(msg); + } + + /* trust all certs: */ + try { + trustall_ctx = SSLContext.getInstance("SSL"); + trustall_ctx.init(mykey, trustAllCerts, new + java.security.SecureRandom()); + + } catch (Exception e) { + String msg = "SSL trustall_ctx FAILED."; + dbg(msg); + throw new Exception(msg); + } + + /* trust url certs: */ + try { + trusturl_ctx = SSLContext.getInstance("SSL"); + trusturl_ctx.init(mykey, trustUrlCert, new + java.security.SecureRandom()); + + } catch (Exception e) { + String msg = "SSL trusturl_ctx FAILED."; + dbg(msg); + throw new Exception(msg); + } + + /* trust the one cert from server: */ + try { + trustone_ctx = SSLContext.getInstance("SSL"); + trustone_ctx.init(mykey, trustOneCert, new + java.security.SecureRandom()); + + } catch (Exception e) { + String msg = "SSL trustone_ctx FAILED."; + dbg(msg); + throw new Exception(msg); + } + } + + boolean browser_cert_match() { + String msg = "Browser URL accept previously accepted cert"; + + if (user_wants_to_see_cert) { + return false; + } + + if (trustallCerts != null && trusturlCerts != null) { + if (trustallCerts.length == 1 && trusturlCerts.length == 1) { + if (trustallCerts[0].equals(trusturlCerts[0])) { + System.out.println(msg); + return true; + } + } + } + return false; + } + + public void check_for_proxy() { + + boolean result = false; + + trusturlCerts = null; + proxy_in_use = false; + if (viewer.ignoreProxy) { + return; + } + + String ustr = "https://" + host + ":"; + if (viewer.httpsPort != null) { + ustr += viewer.httpsPort; + } else { + ustr += port; // hmmm + } + ustr += viewer.urlPrefix + "/check.https.proxy.connection"; + dbg("ustr is: " + ustr); + + + try { + URL url = new URL(ustr); + HttpsURLConnection https = (HttpsURLConnection) + url.openConnection(); + + https.setUseCaches(false); + https.setRequestMethod("GET"); + https.setRequestProperty("Pragma", "No-Cache"); + https.setRequestProperty("Proxy-Connection", + "Keep-Alive"); + https.setDoInput(true); + + https.connect(); + + trusturlCerts = https.getServerCertificates(); + if (trusturlCerts == null) { + dbg("set trusturlCerts to null..."); + } else { + dbg("set trusturlCerts to non-null"); + } + + if (https.usingProxy()) { + proxy_in_use = true; + proxy_is_https = true; + dbg("HTTPS proxy in use. There may be connection problems."); + } + Object output = https.getContent(); + https.disconnect(); + result = true; + + } catch(Exception e) { + dbg("HttpsURLConnection: " + e.getMessage()); + } + + if (proxy_in_use) { + return; + } + + ustr = "http://" + host + ":" + port; + ustr += viewer.urlPrefix + "/index.vnc"; + + try { + URL url = new URL(ustr); + HttpURLConnection http = (HttpURLConnection) + url.openConnection(); + + http.setUseCaches(false); + http.setRequestMethod("GET"); + http.setRequestProperty("Pragma", "No-Cache"); + http.setRequestProperty("Proxy-Connection", + "Keep-Alive"); + http.setDoInput(true); + + http.connect(); + + if (http.usingProxy()) { + proxy_in_use = true; + proxy_is_https = false; + dbg("HTTP proxy in use. There may be connection problems."); + } + Object output = http.getContent(); + http.disconnect(); + + } catch(Exception e) { + dbg("HttpURLConnection: " + e.getMessage()); + } + } + + public Socket connectSock() throws IOException { + + /* + * first try a https connection to detect a proxy, and + * also grab the VNC server cert. + */ + check_for_proxy(); + + if (viewer.trustAllVncCerts) { + dbg("viewer.trustAllVncCerts-0 using trustall_ctx"); + factory = trustall_ctx.getSocketFactory(); + } else if (use_url_cert_for_auth && trusturlCerts != null) { + dbg("using trusturl_ctx"); + factory = trusturl_ctx.getSocketFactory(); + } else { + dbg("using trustloc_ctx"); + factory = trustloc_ctx.getSocketFactory(); + } + + socket = null; + try { + if (proxy_in_use && viewer.forceProxy) { + throw new Exception("forcing proxy (forceProxy)"); + } else if (viewer.CONNECT != null) { + throw new Exception("forcing CONNECT"); + } + + socket = (SSLSocket) factory.createSocket(host, port); + + } catch (Exception esock) { + dbg("esock: " + esock.getMessage()); + if (proxy_in_use || viewer.CONNECT != null) { + proxy_failure = true; + if (proxy_in_use) { + dbg("HTTPS proxy in use. Trying to go with it."); + } else { + dbg("viewer.CONNECT reverse proxy in use. Trying to go with it."); + } + try { + socket = proxy_socket(factory); + } catch (Exception e) { + dbg("err proxy_socket: " + e.getMessage()); + } + } + } + + try { + socket.startHandshake(); + dbg("Server Connection Verified on 1st try."); + + java.security.cert.Certificate[] currentTrustedCerts; + BrowserCertsDialog bcd; + + SSLSession sess = socket.getSession(); + currentTrustedCerts = sess.getPeerCertificates(); + + if (viewer.trustAllVncCerts) { + dbg("viewer.trustAllVncCerts-1"); + } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) { + socket.close(); + socket = null; + throw new SSLHandshakeException("no current certs"); + } + + String serv = ""; + try { + CertInfo ci = new CertInfo(currentTrustedCerts[0]); + serv = ci.get_certinfo("CN"); + } catch (Exception e) { + ; + } + + if (viewer.trustAllVncCerts) { + dbg("viewer.trustAllVncCerts-2"); + user_wants_to_see_cert = false; + } else if (viewer.trustUrlVncCert) { + dbg("viewer.trustUrlVncCert-1"); + user_wants_to_see_cert = false; + } else { + bcd = new BrowserCertsDialog(serv, host + ":" + port); + bcd.queryUser(); + if (bcd.showCertDialog) { + String msg = "user wants to see cert"; + dbg(msg); + user_wants_to_see_cert = true; + throw new SSLHandshakeException(msg); + } else { + user_wants_to_see_cert = false; + dbg("bcd: user said yes, accept it"); + } + } + + } catch (SSLHandshakeException eh) { + dbg("Could not automatically verify Server."); + dbg("msg: " + eh.getMessage()); + + socket.close(); + socket = null; + + /* + * Reconnect, trusting any cert, so we can grab + * the cert to show it to the user. The connection + * is not used for anything else. + */ + factory = trustall_ctx.getSocketFactory(); + if (proxy_failure) { + socket = proxy_socket(factory); + } else { + socket = (SSLSocket) factory.createSocket(host, port); + } + + try { + socket.startHandshake(); + dbg("TrustAll Server Connection Verified."); + + /* grab the cert: */ + try { + SSLSession sess = socket.getSession(); + trustallCerts = sess.getPeerCertificates(); + } catch (Exception e) { + throw new Exception("Could not get " + + "Peer Certificate"); + } + + if (viewer.trustAllVncCerts) { + dbg("viewer.trustAllVncCerts-3"); + } else if (! browser_cert_match()) { + /* + * close socket now, we will reopen after + * dialog if user agrees to use the cert. + */ + socket.close(); + socket = null; + + /* dialog with user to accept cert or not: */ + + TrustDialog td= new TrustDialog(host, port, + trustallCerts); + + if (! td.queryUser()) { + String msg = "User decided against it."; + dbg(msg); + throw new IOException(msg); + } + } + + } catch (Exception ehand2) { + dbg("** Could not TrustAll Verify Server."); + + throw new IOException(ehand2.getMessage()); + } + + /* + * Now connect a 3rd time, using the cert + * retrieved during connection 2 (that the user + * likely blindly agreed to). + */ + + factory = trustone_ctx.getSocketFactory(); + if (proxy_failure) { + socket = proxy_socket(factory); + } else { + socket = (SSLSocket) factory.createSocket(host, port); + } + + try { + socket.startHandshake(); + dbg("TrustAll Server Connection Verified #3."); + + } catch (Exception ehand3) { + dbg("** Could not TrustAll Verify Server #3."); + + throw new IOException(ehand3.getMessage()); + } + } + + if (socket != null && viewer.GET) { + String str = "GET "; + str += viewer.urlPrefix; + str += "/request.https.vnc.connection"; + str += " HTTP/1.0\r\n"; + str += "Pragma: No-Cache\r\n"; + str += "\r\n"; + System.out.println("sending GET: " + str); + OutputStream os = socket.getOutputStream(); + os.write(str.getBytes()); + os.flush(); + if (false) { + String rep = ""; + DataInputStream is = new DataInputStream( + new BufferedInputStream(socket.getInputStream(), 16384)); + while (true) { + rep += readline(is); + if (rep.indexOf("\r\n\r\n") >= 0) { + break; + } + } + System.out.println("rep: " + rep); + } + } + + dbg("SSL returning socket to caller."); + return (Socket) socket; + } + + private void dbg(String s) { + if (debug) { + System.out.println(s); + } + } + + private int gint(String s) { + int n = -1; + try { + Integer I = new Integer(s); + n = I.intValue(); + } catch (Exception ex) { + return -1; + } + return n; + } + + public SSLSocket proxy_socket(SSLSocketFactory factory) { + Properties props = null; + String proxyHost = null; + int proxyPort = 0; + String proxyHost_nossl = null; + int proxyPort_nossl = 0; + String str; + + /* see if we can guess the proxy info from Properties: */ + try { + props = System.getProperties(); + } catch (Exception e) { + dbg("props failed: " + e.getMessage()); + } + if (props != null) { + dbg("\n---------------\nAll props:"); + props.list(System.out); + dbg("\n---------------\n\n"); + + for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) { + String s = (String) e.nextElement(); + String v = System.getProperty(s); + String s2 = s.toLowerCase(); + String v2 = v.toLowerCase(); + + if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) { + continue; + } + if (v2.indexOf("https") < 0) { + continue; + } + + if (s2.indexOf("proxy.https.host") >= 0) { + proxyHost = v2; + continue; + } + if (s2.indexOf("proxy.https.port") >= 0) { + proxyPort = gint(v2); + continue; + } + if (s2.indexOf("proxy.http.host") >= 0) { + proxyHost_nossl = v2; + continue; + } + if (s2.indexOf("proxy.http.port") >= 0) { + proxyPort_nossl = gint(v2); + continue; + } + + String[] pieces = v.split("[,;]"); + for (int i = 0; i < pieces.length; i++) { + String p = pieces[i]; + int j = p.indexOf("https"); + if (j < 0) { + continue; + } + j = p.indexOf("=", j); + if (j < 0) { + continue; + } + p = p.substring(j+1); + String [] hp = p.split(":"); + if (hp.length != 2) { + continue; + } + if (hp[0].length() > 1 && hp[1].length() > 1) { + + proxyPort = gint(hp[1]); + if (proxyPort < 0) { + continue; + } + proxyHost = new String(hp[0]); + break; + } + } + } + } + if (proxyHost != null) { + if (proxyHost_nossl != null && proxyPort_nossl > 0) { + dbg("Using http proxy info instead of https."); + proxyHost = proxyHost_nossl; + proxyPort = proxyPort_nossl; + } + } + + if (proxy_in_use) { + if (proxy_dialog_host != null && proxy_dialog_port > 0) { + proxyHost = proxy_dialog_host; + proxyPort = proxy_dialog_port; + } + if (proxyHost != null) { + dbg("Lucky us! we figured out the Proxy parameters: " + proxyHost + " " + proxyPort); + } else { + /* ask user to help us: */ + ProxyDialog pd = new ProxyDialog(proxyHost, proxyPort); + pd.queryUser(); + proxyHost = pd.getHost(); + proxyPort = pd.getPort(); + proxy_dialog_host = new String(proxyHost); + proxy_dialog_port = proxyPort; + dbg("User said host: " + pd.getHost() + " port: " + pd.getPort()); + } + + dbg("proxy_in_use psocket:"); + proxySock = psocket(proxyHost, proxyPort); + if (proxySock == null) { + dbg("1-a sadly, returning a null socket"); + return null; + } + String hp = host + ":" + port; + + String req1 = "CONNECT " + hp + " HTTP/1.1\r\n" + + "Host: " + hp + "\r\n\r\n"; + + dbg("requesting1: " + req1); + + try { + proxy_os.write(req1.getBytes()); + String reply = readline(proxy_is); + + dbg("proxy replied1: " + reply.trim()); + + if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) { + proxySock.close(); + proxySock = psocket(proxyHost, proxyPort); + if (proxySock == null) { + dbg("2-a sadly, returning a null socket"); + return null; + } + } + } catch(Exception e) { + dbg("sock prob1: " + e.getMessage()); + } + + while (true) { + String line = readline(proxy_is); + dbg("proxy line1: " + line.trim()); + if (line.equals("\r\n") || line.equals("\n")) { + break; + } + } + } else if (viewer.CONNECT != null) { + dbg("viewer.CONNECT psocket:"); + proxySock = psocket(host, port); + if (proxySock == null) { + dbg("1-b sadly, returning a null socket"); + return null; + } + } + + if (viewer.CONNECT != null) { + String hp = viewer.CONNECT; + String req2 = "CONNECT " + hp + " HTTP/1.1\r\n" + + "Host: " + hp + "\r\n\r\n"; + + dbg("requesting2: " + req2); + + try { + proxy_os.write(req2.getBytes()); + String reply = readline(proxy_is); + + dbg("proxy replied2: " + reply.trim()); + + if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) { + proxySock.close(); + proxySock = psocket(proxyHost, proxyPort); + if (proxySock == null) { + dbg("2-b sadly, returning a null socket"); + return null; + } + } + } catch(Exception e) { + dbg("sock prob2: " + e.getMessage()); + } + + while (true) { + String line = readline(proxy_is); + dbg("proxy line2: " + line.trim()); + if (line.equals("\r\n") || line.equals("\n")) { + break; + } + } + + } + + Socket sslsock = null; + try { + sslsock = factory.createSocket(proxySock, host, port, true); + } catch(Exception e) { + dbg("sslsock prob: " + e.getMessage()); + dbg("3 sadly, returning a null socket"); + } + + return (SSLSocket) sslsock; + } + + Socket psocket(String h, int p) { + Socket psock = null; + try { + psock = new Socket(h, p); + proxy_is = new DataInputStream(new BufferedInputStream( + psock.getInputStream(), 16384)); + proxy_os = psock.getOutputStream(); + } catch(Exception e) { + dbg("psocket prob: " + e.getMessage()); + return null; + } + + return psock; + } + + String readline(DataInputStream i) { + byte[] ba = new byte[1]; + String s = new String(""); + ba[0] = 0; + try { + while (ba[0] != 0xa) { + ba[0] = (byte) i.readUnsignedByte(); + s += new String(ba); + } + } catch (Exception e) { + ; + } + return s; + } +} + +class TrustDialog implements ActionListener { + String msg, host, text; + int port; + java.security.cert.Certificate[] trustallCerts = null; + boolean viewing_cert = false; + boolean trust_this_session = false; + + /* + * this is the gui to show the user the cert and info and ask + * them if they want to continue using this cert. + */ + + Button ok, cancel, viewcert; + TextArea textarea; + Checkbox accept, deny; + Dialog dialog; + + String s1 = "Accept this certificate temporarily for this session"; + String s2 = "Do not accept this certificate and do not connect to" + + " this VNC server"; + String ln = "\n---------------------------------------------------\n\n"; + + TrustDialog (String h, int p, java.security.cert.Certificate[] s) { + host = h; + port = p; + trustallCerts = s; + + msg = "VNC Server " + host + ":" + port + " Not Verified"; + } + + public boolean queryUser() { + + /* create and display the dialog for unverified cert. */ + + Frame frame = new Frame(msg); + + dialog = new Dialog(frame, true); + + String infostr = ""; + if (trustallCerts.length == 1) { + CertInfo ci = new CertInfo(trustallCerts[0]); + infostr = ci.get_certinfo("all"); + } + + text = "\n" ++ "Unable to verify the identity of\n" ++ "\n" ++ " " + host + ":" + port + "\n" ++ "\n" ++ infostr ++ "\n" ++ "as a trusted VNC server.\n" ++ "\n" ++ "This may be due to:\n" ++ "\n" ++ " - Your requesting to View the Certificate before accepting.\n" ++ "\n" ++ " - The VNC server using a Self-Signed Certificate.\n" ++ "\n" ++ " - The VNC server using a Certificate Authority not recognized by your\n" ++ " Browser or Java Plugin runtime.\n" ++ "\n" ++ " - The use of an Apache SSL portal employing CONNECT proxying and the\n" ++ " Apache web server has a certificate different from the VNC server's. \n" ++ "\n" ++ " - A Man-In-The-Middle attack impersonating as the VNC server you wish\n" ++ " to connect to. (Wouldn't that be exciting!!)\n" ++ "\n" ++ "By safely copying the VNC server's Certificate (or using a common\n" ++ "Certificate Authority certificate) you can configure your Web Browser or\n" ++ "Java Plugin to automatically authenticate this Server.\n" ++ "\n" ++ "If you do so, then you will only have to click \"Yes\" when this VNC\n" ++ "Viewer applet asks you whether to trust your Browser/Java Plugin's\n" ++ "acceptance of the certificate. (except for the Apache portal case above.)\n" +; + + /* the accept / do-not-accept radio buttons: */ + CheckboxGroup checkbox = new CheckboxGroup(); + accept = new Checkbox(s1, true, checkbox); + deny = new Checkbox(s2, false, checkbox); + + /* put the checkboxes in a panel: */ + Panel check = new Panel(); + check.setLayout(new GridLayout(2, 1)); + + check.add(accept); + check.add(deny); + + /* make the 3 buttons: */ + ok = new Button("OK"); + cancel = new Button("Cancel"); + viewcert = new Button("View Certificate"); + + ok.addActionListener(this); + cancel.addActionListener(this); + viewcert.addActionListener(this); + + /* put the buttons in their own panel: */ + Panel buttonrow = new Panel(); + buttonrow.setLayout(new FlowLayout(FlowLayout.LEFT)); + buttonrow.add(viewcert); + buttonrow.add(ok); + buttonrow.add(cancel); + + /* label at the top: */ + Label label = new Label(msg, Label.CENTER); + label.setFont(new Font("Helvetica", Font.BOLD, 16)); + + /* textarea in the middle */ + textarea = new TextArea(text, 36, 64, + TextArea.SCROLLBARS_VERTICAL_ONLY); + textarea.setEditable(false); + + /* put the two panels in their own panel at bottom: */ + Panel bot = new Panel(); + bot.setLayout(new GridLayout(2, 1)); + bot.add(check); + bot.add(buttonrow); + + /* now arrange things inside the dialog: */ + dialog.setLayout(new BorderLayout()); + + dialog.add("North", label); + dialog.add("South", bot); + dialog.add("Center", textarea); + + dialog.pack(); + dialog.resize(dialog.preferredSize()); + + dialog.show(); /* block here til OK or Cancel pressed. */ + + return trust_this_session; + } + + public synchronized void actionPerformed(ActionEvent evt) { + + if (evt.getSource() == viewcert) { + /* View Certificate button clicked */ + if (viewing_cert) { + /* show the original info text: */ + textarea.setText(text); + viewcert.setLabel("View Certificate"); + viewing_cert = false; + } else { + int i; + /* show all (likely just one) certs: */ + textarea.setText(""); + for (i=0; i < trustallCerts.length; i++) { + int j = i + 1; + textarea.append("Certificate[" + + j + "]\n\n"); + textarea.append( + trustallCerts[i].toString()); + textarea.append(ln); + } + viewcert.setLabel("View Info"); + viewing_cert = true; + + textarea.setCaretPosition(0); + } + + } else if (evt.getSource() == ok) { + /* OK button clicked */ + if (accept.getState()) { + trust_this_session = true; + } else { + trust_this_session = false; + } + dialog.dispose(); + + } else if (evt.getSource() == cancel) { + /* Cancel button clicked */ + trust_this_session = false; + + dialog.dispose(); + } + } + + String get_certinfo() { + String all = ""; + String fields[] = {"CN", "OU", "O", "L", "C"}; + int i; + if (trustallCerts.length < 1) { + all = ""; + return all; + } + String cert = trustallCerts[0].toString(); + + /* + * For now we simply scrape the cert string, there must + * be an API for this... perhaps optionValue? + */ + + for (i=0; i < fields.length; i++) { + int f, t, t1, t2; + String sub, mat = fields[i] + "="; + + f = cert.indexOf(mat, 0); + if (f > 0) { + t1 = cert.indexOf(", ", f); + t2 = cert.indexOf("\n", f); + if (t1 < 0 && t2 < 0) { + continue; + } else if (t1 < 0) { + t = t2; + } else if (t2 < 0) { + t = t1; + } else if (t1 < t2) { + t = t1; + } else { + t = t2; + } + if (t > f) { + sub = cert.substring(f, t); + all = all + " " + sub + "\n"; + } + } + } + return all; + } +} + +class ProxyDialog implements ActionListener { + String guessedHost = null; + String guessedPort = null; + /* + * this is the gui to show the user the cert and info and ask + * them if they want to continue using this cert. + */ + + Button ok; + Dialog dialog; + TextField entry; + String reply = ""; + + ProxyDialog (String h, int p) { + guessedHost = h; + try { + guessedPort = Integer.toString(p); + } catch (Exception e) { + guessedPort = "8080"; + } + } + + public void queryUser() { + + /* create and display the dialog for unverified cert. */ + + Frame frame = new Frame("Need Proxy host:port"); + + dialog = new Dialog(frame, true); + + + Label label = new Label("Please Enter your https Proxy info as host:port", Label.CENTER); + //label.setFont(new Font("Helvetica", Font.BOLD, 16)); + entry = new TextField(30); + ok = new Button("OK"); + ok.addActionListener(this); + + String guess = ""; + if (guessedHost != null) { + guess = guessedHost + ":" + guessedPort; + } + entry.setText(guess); + + dialog.setLayout(new BorderLayout()); + dialog.add("North", label); + dialog.add("Center", entry); + dialog.add("South", ok); + dialog.pack(); + dialog.resize(dialog.preferredSize()); + + dialog.show(); /* block here til OK or Cancel pressed. */ + return; + } + + public String getHost() { + int i = reply.indexOf(":"); + if (i < 0) { + return "unknown"; + } + String h = reply.substring(0, i); + return h; + } + + public int getPort() { + int i = reply.indexOf(":"); + int p = 8080; + if (i < 0) { + return p; + } + i++; + String ps = reply.substring(i); + try { + Integer I = new Integer(ps); + p = I.intValue(); + } catch (Exception e) { + ; + } + return p; + } + + public synchronized void actionPerformed(ActionEvent evt) { + System.out.println(evt.getActionCommand()); + if (evt.getSource() == ok) { + reply = entry.getText(); + dialog.dispose(); + } + } +} + +class ClientCertDialog implements ActionListener { + + Button ok; + Dialog dialog; + TextField entry; + String reply = ""; + + ClientCertDialog() { + ; + } + + public String queryUser() { + + /* create and display the dialog for unverified cert. */ + + Frame frame = new Frame("Enter SSL Client Cert+Key String"); + + dialog = new Dialog(frame, true); + + + Label label = new Label("Please Enter the SSL Client Cert+Key String 308204c0...,...522d2d0a", Label.CENTER); + entry = new TextField(30); + ok = new Button("OK"); + ok.addActionListener(this); + + dialog.setLayout(new BorderLayout()); + dialog.add("North", label); + dialog.add("Center", entry); + dialog.add("South", ok); + dialog.pack(); + dialog.resize(dialog.preferredSize()); + + dialog.show(); /* block here til OK or Cancel pressed. */ + return reply; + } + + public synchronized void actionPerformed(ActionEvent evt) { + System.out.println(evt.getActionCommand()); + if (evt.getSource() == ok) { + reply = entry.getText(); + dialog.dispose(); + } + } +} + +class BrowserCertsDialog implements ActionListener { + Button yes, no; + Dialog dialog; + String vncServer; + String hostport; + public boolean showCertDialog = true; + + BrowserCertsDialog(String serv, String hp) { + vncServer = serv; + hostport = hp; + } + + public void queryUser() { + + /* create and display the dialog for unverified cert. */ + + Frame frame = new Frame("Use Browser/JVM Certs?"); + + dialog = new Dialog(frame, true); + + String m = ""; +m += "\n"; +m += "This VNC Viewer applet does not have its own keystore to track\n"; +m += "SSL certificates, and so cannot authenticate the certificate\n"; +m += "of the VNC Server:\n"; +m += "\n"; +m += " " + hostport + "\n\n " + vncServer + "\n"; +m += "\n"; +m += "on its own.\n"; +m += "\n"; +m += "However, it has noticed that your Web Browser or Java VM Plugin\n"; +m += "has previously accepted the same certificate. You may have set\n"; +m += "this up permanently or just for this session, or the server\n"; +m += "certificate was signed by a CA cert that your Web Browser or\n"; +m += "Java VM Plugin has.\n"; +m += "\n"; +m += "Should this VNC Viewer applet now connect to the above VNC server?\n"; +m += "\n"; + +// String m = "\nShould this VNC Viewer applet use your Browser/JVM certs to\n"; +// m += "authenticate the VNC Server:\n"; +// m += "\n " + hostport + "\n\n " + vncServer + "\n\n"; +// m += "(NOTE: this *includes* any certs you have Just Now accepted in a\n"; +// m += "dialog box with your Web Browser or Java Applet Plugin)\n\n"; + + TextArea textarea = new TextArea(m, 20, 64, + TextArea.SCROLLBARS_VERTICAL_ONLY); + textarea.setEditable(false); + yes = new Button("Yes"); + yes.addActionListener(this); + no = new Button("No, Let Me See the Certificate."); + no.addActionListener(this); + + dialog.setLayout(new BorderLayout()); + dialog.add("North", textarea); + dialog.add("Center", yes); + dialog.add("South", no); + dialog.pack(); + dialog.resize(dialog.preferredSize()); + + dialog.show(); /* block here til Yes or No pressed. */ + return; + } + + public synchronized void actionPerformed(ActionEvent evt) { + System.out.println(evt.getActionCommand()); + if (evt.getSource() == yes) { + showCertDialog = false; + dialog.dispose(); + } else if (evt.getSource() == no) { + showCertDialog = true; + dialog.dispose(); + } + } +} + +class CertInfo { + String fields[] = {"CN", "OU", "O", "L", "C"}; + java.security.cert.Certificate cert; + String certString = ""; + + CertInfo(java.security.cert.Certificate c) { + cert = c; + certString = cert.toString(); + } + + String get_certinfo(String which) { + int i; + String cs = new String(certString); + String all = ""; + + /* + * For now we simply scrape the cert string, there must + * be an API for this... perhaps optionValue? + */ + for (i=0; i < fields.length; i++) { + int f, t, t1, t2; + String sub, mat = fields[i] + "="; + + f = cs.indexOf(mat, 0); + if (f > 0) { + t1 = cs.indexOf(", ", f); + t2 = cs.indexOf("\n", f); + if (t1 < 0 && t2 < 0) { + continue; + } else if (t1 < 0) { + t = t2; + } else if (t2 < 0) { + t = t1; + } else if (t1 < t2) { + t = t1; + } else { + t = t2; + } + if (t > f) { + sub = cs.substring(f, t); + all = all + " " + sub + "\n"; + if (which.equals(fields[i])) { + return sub; + } + } + } + } + if (which.equals("all")) { + return all; + } else { + return ""; + } + } +} diff -Naur JavaViewer.orig/VncCanvas.java JavaViewer/VncCanvas.java --- JavaViewer.orig/VncCanvas.java 2005-11-21 18:50:18.000000000 -0500 +++ JavaViewer/VncCanvas.java 2007-05-16 15:57:36.000000000 -0400 @@ -27,6 +27,9 @@ import java.lang.*; import java.util.zip.*; +// begin runge/x11vnc +import java.util.Collections; +// end runge/x11vnc // // VncCanvas is a subclass of Canvas which draws a VNC desktop on it. @@ -85,6 +88,22 @@ cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); +// begin runge/x11vnc +// kludge to not show any Java cursor in the canvas since we are +// showing the soft cursor (should be a user setting...) +Cursor dot = Toolkit.getDefaultToolkit().createCustomCursor( + Toolkit.getDefaultToolkit().createImage(new byte[4]), new Point(0,0), + "dot"); +this.setCursor(dot); + +// while we are at it... get rid of the keyboard traversals that +// make it so we can't type a Tab character: +this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, + Collections.EMPTY_SET); +this.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, + Collections.EMPTY_SET); +// end runge/x11vnc + colors = new Color[256]; // sf@2005 - Now Default for (int i = 0; i < 256; i++) @@ -237,6 +256,9 @@ } else { +// begin runge/x11vnc + viewer.options.oldEightBitColors = viewer.options.eightBitColors; +// end runge/x11vnc rfb.writeSetPixelFormat( 32, 24, @@ -1532,9 +1554,14 @@ else { result = - 0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF) - << 16 | (pixBuf[i * 4 + 2] & 0xFF) - << 8 | (pixBuf[i * 4 + 3] & 0xFF); +// begin runge/x11vnc +// 0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF) +// << 16 | (pixBuf[i * 4 + 2] & 0xFF) +// << 8 | (pixBuf[i * 4 + 3] & 0xFF); + 0xFF000000 | (pixBuf[i * 4 + 2] & 0xFF) + << 16 | (pixBuf[i * 4 + 1] & 0xFF) + << 8 | (pixBuf[i * 4 + 0] & 0xFF); +// end runge/x11vnc } } else { result = 0; // Transparent pixel @@ -1565,9 +1592,14 @@ else { result = - 0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF) - << 16 | (pixBuf[i * 4 + 2] & 0xFF) - << 8 | (pixBuf[i * 4 + 3] & 0xFF); +// begin runge/x11vnc +// 0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF) +// << 16 | (pixBuf[i * 4 + 2] & 0xFF) +// << 8 | (pixBuf[i * 4 + 3] & 0xFF); + 0xFF000000 | (pixBuf[i * 4 + 2] & 0xFF) + << 16 | (pixBuf[i * 4 + 1] & 0xFF) + << 8 | (pixBuf[i * 4 + 0] & 0xFF); +// end runge/x11vnc } } else { result = 0; // Transparent pixel diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java --- JavaViewer.orig/VncViewer.java 2006-05-24 15:14:40.000000000 -0400 +++ JavaViewer/VncViewer.java 2007-05-15 15:09:10.000000000 -0400 @@ -115,6 +115,22 @@ int i; // mslogon support 2 end +// begin runge/x11vnc +boolean disableSSL; +boolean GET; +String CONNECT; +String urlPrefix; +String httpsPort; +String oneTimeKey; +boolean forceProxy; +boolean ignoreProxy; +boolean trustAllVncCerts; +boolean trustUrlVncCert; + +boolean ignoreMSLogonCheck; +// end runge/x11vnc + + // // init() // @@ -336,7 +352,12 @@ // - prologueDetectAuthProtocol() ; +// begin runge/x11vnc +// prologueDetectAuthProtocol() ; + if (ignoreMSLogonCheck == false) { + prologueDetectAuthProtocol() ; + } +// end runge/x11vnc authenticator = new AuthPanel(mslogon); @@ -435,7 +456,7 @@ rfb.readVersionMsg(); - System.out.println("RFB server supports protocol version " + + System.out.println("RFB server supports protocol version: " + rfb.serverMajor + "." + rfb.serverMinor); rfb.writeVersionMsg(); @@ -804,6 +825,75 @@ deferScreenUpdates = readIntParameter("Defer screen updates", 20); deferCursorUpdates = readIntParameter("Defer cursor updates", 10); deferUpdateRequests = readIntParameter("Defer update requests", 50); + +// begin runge/x11vnc + // SSL + disableSSL = false; + str = readParameter("DisableSSL", false); + if (str != null && str.equalsIgnoreCase("Yes")) + disableSSL = true; + + httpsPort = readParameter("httpsPort", false); + + // Extra GET, CONNECT string: + CONNECT = readParameter("CONNECT", false); + if (CONNECT != null) { + CONNECT = CONNECT.replaceAll(" ", ":"); + } + + GET = false; + str = readParameter("GET", false); + if (str != null && str.equalsIgnoreCase("Yes")) { + GET = true; + } + if (str != null && str.equalsIgnoreCase("1")) { + GET = true; + } + + urlPrefix = readParameter("urlPrefix", false); + if (urlPrefix != null) { + urlPrefix = urlPrefix.replaceAll("%2F", "/"); + urlPrefix = urlPrefix.replaceAll("%2f", "/"); + urlPrefix = urlPrefix.replaceAll("_2F_", "/"); + if (urlPrefix.indexOf("/") != 0) { + urlPrefix = "/" + urlPrefix; + } + } else { + urlPrefix = ""; + } + System.out.println("urlPrefix: '" + urlPrefix + "'"); + + oneTimeKey = readParameter("oneTimeKey", false); + if (oneTimeKey != null) { + System.out.println("oneTimeKey: is set"); + } + + forceProxy = false; + str = readParameter("forceProxy", false); + if (str != null && str.equalsIgnoreCase("Yes")) { + forceProxy = true; + } + ignoreProxy = false; + str = readParameter("ignoreProxy", false); + if (str != null && str.equalsIgnoreCase("Yes")) { + ignoreProxy = true; + } + trustAllVncCerts = false; + str = readParameter("trustAllVncCerts", false); + if (str != null && str.equalsIgnoreCase("Yes")) { + trustAllVncCerts = true; + } + trustUrlVncCert = false; + str = readParameter("trustUrlVncCert", false); + if (str != null && str.equalsIgnoreCase("Yes")) { + trustUrlVncCert = true; + } + ignoreMSLogonCheck = false; + str = readParameter("ignoreMSLogonCheck", false); + if (str != null && str.equalsIgnoreCase("Yes")) { + ignoreMSLogonCheck = true; + } +// end runge/x11vnc } public String readParameter(String name, boolean required) {