mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1022 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			1022 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| //
 | |
| //  Copyright (C) 2001-2004 HorizonLive.com, Inc.  All Rights Reserved.
 | |
| //  Copyright (C) 2002 Constantin Kaplinsky.  All Rights Reserved.
 | |
| //  Copyright (C) 1999 AT&T Laboratories Cambridge.  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; either version 2 of the License, or
 | |
| //  (at your option) any later version.
 | |
| //
 | |
| //  This software is distributed in the hope that it will be useful,
 | |
| //  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| //  GNU General Public License for more details.
 | |
| //
 | |
| //  You should have received a copy of the GNU General Public License
 | |
| //  along with this software; if not, write to the Free Software
 | |
| //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 | |
| //  USA.
 | |
| //
 | |
| 
 | |
| //
 | |
| // VncViewer.java - the VNC viewer applet.  This class mainly just sets up the
 | |
| // user interface, leaving it to the VncCanvas to do the actual rendering of
 | |
| // a VNC desktop.
 | |
| //
 | |
| 
 | |
| import java.applet.Applet;
 | |
| import java.awt.*;
 | |
| import java.awt.event.*;
 | |
| import java.awt.image.BufferedImage;
 | |
| import java.awt.image.MemoryImageSource;
 | |
| import java.io.*;
 | |
| import java.net.*;
 | |
| import java.util.Date;
 | |
| 
 | |
| public class ConsoleViewer implements java.lang.Runnable {
 | |
| 
 | |
| 	int id = getNextId();
 | |
| 	boolean compressServerMessage = false;
 | |
| 	long createTime = System.currentTimeMillis();
 | |
| 	long lastUsedTime = System.currentTimeMillis();
 | |
| 
 | |
| 	boolean dropMe = false;
 | |
| 
 | |
| 	OutputStream clientStream;
 | |
| 	String clientStreamInfo;
 | |
| 
 | |
| 	int status;
 | |
| 	public final static int STATUS_ERROR = -1;
 | |
| 	public final static int STATUS_UNINITIALIZED = 0;
 | |
| 	public final static int STATUS_CONNECTING = 1;
 | |
| 	public final static int STATUS_INITIALIZING = 2;
 | |
| 	public final static int STATUS_NORMAL_OPERATION = 3;
 | |
| 	public final static int STATUS_AUTHENTICATION_FAILURE = 100;
 | |
| 
 | |
| 	boolean inAnApplet = true;
 | |
| 	boolean inSeparateFrame = false;
 | |
| 	boolean inProxyMode = false;
 | |
| 
 | |
| 	String[] mainArgs;
 | |
| 
 | |
| 	RfbProto rfb;
 | |
| 	Thread rfbThread;
 | |
| 
 | |
| 	ConsoleApplet applet;
 | |
| 	Frame vncFrame;
 | |
| 	Container vncContainer;
 | |
| 	ScrollPane desktopScrollPane;
 | |
| 	GridBagLayout gridbag;
 | |
| 	ButtonPanel buttonPanel;
 | |
| 	Label connStatusLabel;
 | |
| 	ConsoleCanvas vc;
 | |
| 	OptionsFrame options;
 | |
| 	ClipboardFrame clipboard;
 | |
| 	private RecordingFrame rec;
 | |
| 
 | |
| 	// Control session recording.
 | |
| 	Object recordingSync;
 | |
| 	String sessionFileName;
 | |
| 	boolean recordingActive;
 | |
| 	boolean recordingStatusChanged;
 | |
| 	String cursorUpdatesDef;
 | |
| 	String eightBitColorsDef;
 | |
| 
 | |
| 	// Variables read from parameter values.
 | |
| 	String socketFactory;
 | |
| 	String host;
 | |
| 	int port;
 | |
| 	String proxyHost;
 | |
| 	int proxyPort;
 | |
| 	String passwordParam;
 | |
| 	boolean showControls;
 | |
| 	boolean offerRelogin;
 | |
| 	boolean showOfflineDesktop;
 | |
| 	int deferScreenUpdates;
 | |
| 	int deferCursorUpdates;
 | |
| 	int deferUpdateRequests;
 | |
| 
 | |
| 	static int id_count = 1;
 | |
| 	synchronized static int getNextId() {
 | |
| 		return id_count++;
 | |
| 	}
 | |
| 	
 | |
| 	//
 | |
| 	// init()
 | |
| 	//
 | |
| 
 | |
| 	public void init() {
 | |
| 		if (inProxyMode) {
 | |
| 			initProxy();
 | |
| 			return;
 | |
| 		}
 | |
| 		readParameters();
 | |
| 
 | |
| 		if (inSeparateFrame) {
 | |
| 			vncFrame = new Frame("VMOps ConsoleViewer");
 | |
| 			if (!inAnApplet) {
 | |
| 				// vncFrame.add("Center", this);
 | |
| 			}
 | |
| 			vncContainer = vncFrame;
 | |
| 		} else {
 | |
| 			vncContainer = applet;
 | |
| 		}
 | |
| 
 | |
| 		recordingSync = new Object();
 | |
| 
 | |
| 		options = new OptionsFrame(this);
 | |
| 		// clipboard = new ClipboardFrame(this);
 | |
| 		// if (RecordingFrame.checkSecurity())
 | |
| 		// rec = new RecordingFrame(this);
 | |
| 
 | |
| 		sessionFileName = null;
 | |
| 		recordingActive = false;
 | |
| 		recordingStatusChanged = false;
 | |
| 		cursorUpdatesDef = null;
 | |
| 		eightBitColorsDef = null;
 | |
| 
 | |
| 		if (inSeparateFrame)
 | |
| 			vncFrame.addWindowListener(applet);
 | |
| 		rfbThread = new Thread(this);
 | |
| 		rfbThread.start();
 | |
| 	}
 | |
| 
 | |
| 	private void initProxy() {
 | |
| 		recordingSync = new Object();
 | |
| 
 | |
| 		options = new OptionsFrame(this);
 | |
| 		options.viewOnly = true;
 | |
| 
 | |
| 		sessionFileName = null;
 | |
| 		recordingActive = false;
 | |
| 		recordingStatusChanged = false;
 | |
| 		cursorUpdatesDef = null;
 | |
| 		eightBitColorsDef = null;
 | |
| 
 | |
| 		rfbThread = new Thread(this);
 | |
| 		rfbThread.setName("RFB Thread " + rfbThread.getId() + " >" + host + ":"
 | |
| 				+ port);
 | |
| 
 | |
| 		rfbThread.start();
 | |
| 	}
 | |
| 
 | |
| 	public void update(Graphics g) {
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// run() - executed by the rfbThread to deal with the RFB socket.
 | |
| 	//
 | |
| 
 | |
| 	public void run() {
 | |
| 		if (inProxyMode) {
 | |
| 			runProxy();
 | |
| 			return;
 | |
| 		}
 | |
| 		
 | |
| 		int[] pixels = new int[16 * 16];
 | |
| 		Image image = Toolkit.getDefaultToolkit().createImage(
 | |
| 		        new MemoryImageSource(16, 16, pixels, 0, 16));
 | |
| 		Cursor transparentCursor =
 | |
| 		        Toolkit.getDefaultToolkit().createCustomCursor
 | |
| 		             (image, new Point(0, 0), "invisibleCursor");
 | |
| 		vncContainer.setCursor(transparentCursor);
 | |
| 
 | |
| 
 | |
| 		gridbag = new GridBagLayout();
 | |
| 		vncContainer.setLayout(gridbag);
 | |
| 		GridBagConstraints gbc = new GridBagConstraints();
 | |
| 		gbc.gridwidth = GridBagConstraints.REMAINDER;
 | |
| 		gbc.anchor = GridBagConstraints.NORTHWEST;
 | |
| 		//gbc.anchor = GridBagConstraints.CENTER;
 | |
| 
 | |
| 		if (showControls) {
 | |
| 			buttonPanel = new ButtonPanel(this);
 | |
| 			gridbag.setConstraints(buttonPanel, gbc);
 | |
| 			vncContainer.add(buttonPanel);
 | |
| 		}
 | |
| 
 | |
| 		// FIXME: Use auto-scaling not only in a separate frame.
 | |
| 		if (options.autoScale && inSeparateFrame) {
 | |
| 			Dimension screenSize;
 | |
| 			try {
 | |
| 				screenSize = vncContainer.getToolkit().getScreenSize();
 | |
| 			} catch (Exception e) {
 | |
| 				screenSize = new Dimension(0, 0);
 | |
| 			}
 | |
| 			createCanvas(screenSize.width - 32, screenSize.height - 32);
 | |
| 		} else {
 | |
| 			createCanvas(0, 0);
 | |
| 		}
 | |
| 
 | |
| 		gbc.weightx = 1.0;
 | |
| 		gbc.weighty = 1.0;
 | |
| 
 | |
| 		if (inSeparateFrame) {
 | |
| 
 | |
| 			// Create a panel which itself is resizeable and can hold
 | |
| 			// non-resizeable VncCanvas component at the top left corner.
 | |
| 			Panel canvasPanel = new Panel();
 | |
| 			canvasPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
 | |
| 			canvasPanel.add(vc);
 | |
| 
 | |
| 			// Create a ScrollPane which will hold a panel with VncCanvas
 | |
| 			// inside.
 | |
| 			desktopScrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
 | |
| 			gbc.fill = GridBagConstraints.BOTH;
 | |
| 			gridbag.setConstraints(desktopScrollPane, gbc);
 | |
| 			desktopScrollPane.add(canvasPanel);
 | |
| 			// Finally, add our ScrollPane to the Frame window.
 | |
| 			vncFrame.add(desktopScrollPane);
 | |
| 			// vncFrame.setTitle(rfb.desktopName);
 | |
| 			vncFrame.pack();
 | |
| 			vc.resizeDesktopFrame();
 | |
| 		} else {
 | |
| 			gridbag.setConstraints(vc, gbc);
 | |
| 			applet.add(vc);
 | |
| 			applet.validate();
 | |
| 		}
 | |
| 
 | |
| 		if (showControls)
 | |
| 			buttonPanel.enableButtons();
 | |
| 		moveFocusToDesktop();
 | |
| 
 | |
| 		try {
 | |
| 			connectAndAuthenticate();
 | |
| 			doProtocolInitialisation();
 | |
| 			vc.rfb = rfb;
 | |
| 			vc.setPixelFormat();
 | |
| 			vc.rfb.writeFramebufferUpdateRequest(0, 0, vc.rfb.framebufferWidth,
 | |
| 					vc.rfb.framebufferHeight, false);
 | |
| 			vc.processNormalProtocol();
 | |
| 			// We should never get here, but just in case
 | |
| 			showMessage("Disconnected from server");
 | |
| 		} catch (NoRouteToHostException e) {
 | |
| 			fatalError("Network error: no route to server: " + host, e);
 | |
| 		} catch (UnknownHostException e) {
 | |
| 			fatalError("Network error: server name unknown: " + host, e);
 | |
| 		} catch (ConnectException e) {
 | |
| 			fatalError("Network error: could not connect to server: " + host
 | |
| 					+ ":" + port, e);
 | |
| 		} catch (EOFException e) {
 | |
| 			fatalError("Network error: remote side closed connection", e);
 | |
| 		} catch (IOException e) {
 | |
| 			fatalError(e.getMessage(), e);
 | |
| 		} catch (Exception e) {
 | |
| 			fatalError(e.getMessage(), e);
 | |
| 		} finally {
 | |
| 			encodingsSaved = null;
 | |
| 			nEncodingsSaved = 0;
 | |
| 			synchronized (this) {
 | |
| 				if (rfb != null) {
 | |
| 					rfb.close();
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void runProxy() {
 | |
| 		createCanvas(0, 0);
 | |
| 
 | |
| 		int delay = 0;
 | |
| 		while (!dropMe) {
 | |
| 			try {
 | |
| 				status = STATUS_CONNECTING;
 | |
| 				connectAndAuthenticate();
 | |
| 				delay = 0; // reset the delay interval
 | |
| 				status = STATUS_INITIALIZING;
 | |
| 				doProtocolInitialisation();
 | |
| 				vc.rfb = rfb;
 | |
| 				vc.setPixelFormat();
 | |
| 				vc.rfb.writeFramebufferUpdateRequest(0, 0,
 | |
| 						vc.rfb.framebufferWidth, vc.rfb.framebufferHeight,
 | |
| 						false);
 | |
| 				status = STATUS_NORMAL_OPERATION;
 | |
| 				vc.processNormalProtocol();
 | |
| 			} catch (AuthenticationException e) {
 | |
| 				status = STATUS_AUTHENTICATION_FAILURE;
 | |
| 				String msg = e.getMessage();
 | |
| 				Logger.log(Logger.INFO, msg);
 | |
| 			} catch (Exception e) {
 | |
| 				status = STATUS_ERROR;
 | |
| 				Logger.log(Logger.INFO, e.toString());
 | |
| 			} finally {
 | |
| 				String oldName = Thread.currentThread().getName();
 | |
| 				encodingsSaved = null;
 | |
| 				nEncodingsSaved = 0;
 | |
| 				synchronized (this) {
 | |
| 					if (rfb != null) {
 | |
| 						rfb.close();
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			if (dropMe) {
 | |
| 				break;
 | |
| 			}
 | |
| 			if (status == STATUS_AUTHENTICATION_FAILURE) {
 | |
| 				break;
 | |
| 			} else {
 | |
| 				Logger.log(Logger.INFO, "Exception caught, retrying in "
 | |
| 						+ delay + "ms");
 | |
| 				try {
 | |
| 					Thread.sleep(delay);
 | |
| 				} catch (InterruptedException e) {
 | |
| 					// ignored
 | |
| 				}
 | |
| 				delay = (int) ((float) (delay + 700) * 1.5);
 | |
| 				if (delay > 3000) {
 | |
| 					Logger.log(Logger.INFO, "Delay value gets too large " + delay + ", stop retrying");
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		Logger.log(Logger.INFO, "RFB thread terminating");
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Create a VncCanvas instance.
 | |
| 	//
 | |
| 
 | |
| 	void createCanvas(int maxWidth, int maxHeight) {
 | |
| 		/*
 | |
| 		 * // Determine if Java 2D API is available and use a special // version
 | |
| 		 * of VncCanvas if it is present. vc = null; try { // This throws
 | |
| 		 * ClassNotFoundException if there is no Java 2D API. Class cl =
 | |
| 		 * Class.forName("java.awt.Graphics2D"); // If we could load Graphics2D
 | |
| 		 * class, then we can use VncCanvas2D. cl = Class.forName("VncCanvas2");
 | |
| 		 * Class[] argClasses = { this.getClass(), Integer.TYPE, Integer.TYPE };
 | |
| 		 * java.lang.reflect.Constructor cstr = cl.getConstructor(argClasses);
 | |
| 		 * Object[] argObjects = { this, new Integer(maxWidth), new
 | |
| 		 * Integer(maxHeight) }; vc = (VncCanvas)cstr.newInstance(argObjects); }
 | |
| 		 * catch (Exception e) { Logger.log(Logger.INFO,
 | |
| 		 * "Warning: Java 2D API is not available"); }
 | |
| 		 * 
 | |
| 		 * // If we failed to create VncCanvas2D, use old VncCanvas. if (vc ==
 | |
| 		 * null)
 | |
| 		 */
 | |
| 		vc = new ConsoleCanvas2(this, maxWidth, maxHeight);
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * // // Process RFB socket messages. // If the rfbThread is being stopped,
 | |
| 	 * ignore any exceptions, // otherwise rethrow the exception so it can be
 | |
| 	 * handled. //
 | |
| 	 * 
 | |
| 	 * void processNormalProtocol() throws Exception { try {
 | |
| 	 * vc.processNormalProtocol(); } catch (Exception e) { if (rfbThread ==
 | |
| 	 * null) { Logger.log(Logger.INFO, "Ignoring RFB socket exceptions" +
 | |
| 	 * " because applet is stopping"); } else { throw e; } } }
 | |
| 	 */
 | |
| 	//
 | |
| 	// Connect to the RFB server and authenticate the user.
 | |
| 	//
 | |
| 	void connectAndAuthenticate() throws Exception {
 | |
| 		showConnectionStatus("Initializing...");
 | |
| 		if (!inProxyMode) {
 | |
| 			if (inSeparateFrame) {
 | |
| 				vncFrame.pack();
 | |
| 				vncFrame.show();
 | |
| 			} else {
 | |
| 				applet.validate();
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (proxyHost != null) {
 | |
| 			showConnectionStatus("Connecting to " + proxyHost + ", port "
 | |
| 					+ proxyPort + "...");
 | |
| 			rfb = new RfbProto(proxyHost, proxyPort, this);
 | |
| 			rfb.readProxyVersion();
 | |
| 			rfb.writeProxyString(host, port,
 | |
| 					(passwordParam != null) ? passwordParam : "");
 | |
| 		} else {
 | |
| 			showConnectionStatus("Connecting to " + host + ", port " + port
 | |
| 					+ "...");
 | |
| 			rfb = new RfbProto(host, port, this);
 | |
| 		}
 | |
| 		showConnectionStatus("Connected to server");
 | |
| 
 | |
| 		rfb.readVersionMsg();
 | |
| 		showConnectionStatus("RFB server supports protocol version "
 | |
| 				+ rfb.serverMajor + "." + rfb.serverMinor);
 | |
| 
 | |
| 		rfb.writeVersionMsg();
 | |
| 		showConnectionStatus("Using RFB protocol version " + rfb.clientMajor
 | |
| 				+ "." + rfb.clientMinor);
 | |
| 
 | |
| 		int secType = rfb.negotiateSecurity();
 | |
| 		int authType;
 | |
| 		if (secType == RfbProto.SecTypeTight) {
 | |
| 			showConnectionStatus("Enabling TightVNC protocol extensions");
 | |
| 			rfb.initCapabilities();
 | |
| 			rfb.setupTunneling();
 | |
| 			authType = rfb.negotiateAuthenticationTight();
 | |
| 		} else {
 | |
| 			authType = secType;
 | |
| 		}
 | |
| 
 | |
| 		switch (authType) {
 | |
| 		case RfbProto.AuthNone:
 | |
| 			showConnectionStatus("No authentication needed");
 | |
| 			rfb.authenticateNone();
 | |
| 			break;
 | |
| 		case RfbProto.AuthVNC:
 | |
| 			showConnectionStatus("Performing standard VNC authentication");
 | |
| 			if (passwordParam != null) {
 | |
| 				rfb.authenticateVNC(passwordParam);
 | |
| 			} else {
 | |
| 				throw new AuthenticationException("Bad password");
 | |
| 				/*
 | |
| 				 * String pw = askPassword(); rfb.authenticateVNC(pw);
 | |
| 				 */
 | |
| 			}
 | |
| 			break;
 | |
| 		default:
 | |
| 			throw new Exception("Unknown authentication scheme " + authType);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Show a message describing the connection status.
 | |
| 	// To hide the connection status label, use (msg == null).
 | |
| 	//
 | |
| 
 | |
| 	void showConnectionStatus(String msg) {
 | |
| 		if (inProxyMode) {
 | |
| 			if (msg != null) {
 | |
| 				Logger.log(Logger.INFO, msg);
 | |
| 			}
 | |
| 			return;
 | |
| 		}
 | |
| 		/*
 | |
| 		 * if (msg == null) { if (vncContainer.isAncestorOf(connStatusLabel)) {
 | |
| 		 * vncContainer.remove(connStatusLabel); } return; }
 | |
| 		 * 
 | |
| 		 * Logger.log(Logger.INFO, msg);
 | |
| 		 * 
 | |
| 		 * if (connStatusLabel == null) { connStatusLabel = new Label("Status: "
 | |
| 		 * + msg); connStatusLabel.setFont(new Font("Helvetica", Font.PLAIN,
 | |
| 		 * 12)); } else { connStatusLabel.setText("Status: " + msg); }
 | |
| 		 * 
 | |
| 		 * if (!vncContainer.isAncestorOf(connStatusLabel)) { GridBagConstraints
 | |
| 		 * gbc = new GridBagConstraints(); gbc.gridwidth =
 | |
| 		 * GridBagConstraints.REMAINDER; gbc.fill =
 | |
| 		 * GridBagConstraints.HORIZONTAL; gbc.anchor =
 | |
| 		 * GridBagConstraints.NORTHWEST; gbc.weightx = 1.0; gbc.weighty = 1.0;
 | |
| 		 * gbc.insets = new Insets(20, 30, 20, 30);
 | |
| 		 * gridbag.setConstraints(connStatusLabel, gbc);
 | |
| 		 * vncContainer.add(connStatusLabel); }
 | |
| 		 * 
 | |
| 		 * if (inSeparateFrame) { vncFrame.pack(); } else { validate(); }
 | |
| 		 */
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Show an authentication panel.
 | |
| 	//
 | |
| /*
 | |
| 	String askPassword() throws Exception {
 | |
| 		showConnectionStatus(null);
 | |
| 
 | |
| 		AuthPanel authPanel = new AuthPanel(this);
 | |
| 
 | |
| 		GridBagConstraints gbc = new GridBagConstraints();
 | |
| 		gbc.gridwidth = GridBagConstraints.REMAINDER;
 | |
| 		gbc.anchor = GridBagConstraints.NORTHWEST;
 | |
| 		gbc.weightx = 1.0;
 | |
| 		gbc.weighty = 1.0;
 | |
| 		gbc.ipadx = 100;
 | |
| 		gbc.ipady = 50;
 | |
| 		gridbag.setConstraints(authPanel, gbc);
 | |
| 		vncContainer.add(authPanel);
 | |
| 
 | |
| 		if (inSeparateFrame) {
 | |
| 			vncFrame.pack();
 | |
| 		} else {
 | |
| 			applet.validate();
 | |
| 		}
 | |
| 
 | |
| 		authPanel.moveFocusToDefaultField();
 | |
| 		String pw = authPanel.getPassword();
 | |
| 		vncContainer.remove(authPanel);
 | |
| 
 | |
| 		return pw;
 | |
| 	}
 | |
| */
 | |
| 	//
 | |
| 	// Do the rest of the protocol initialisation.
 | |
| 	//
 | |
| 
 | |
| 	void doProtocolInitialisation() throws IOException {
 | |
| 		rfb.writeClientInit();
 | |
| 		rfb.readServerInit();
 | |
| 		/*
 | |
| 		 * Logger.log(Logger.INFO, "Desktop name is " + rfb.desktopName);
 | |
| 		 * Logger.log(Logger.INFO, "Desktop size is " + rfb.framebufferWidth +
 | |
| 		 * " x " + rfb.framebufferHeight);
 | |
| 		 */
 | |
| 		setEncodings();
 | |
| 
 | |
| 		showConnectionStatus(null);
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Send current encoding list to the RFB server.
 | |
| 	//
 | |
| 
 | |
| 	int[] encodingsSaved;
 | |
| 	int nEncodingsSaved;
 | |
| 
 | |
| 	void setEncodings() {
 | |
| 		setEncodings(false);
 | |
| 	}
 | |
| 
 | |
| 	void autoSelectEncodings() {
 | |
| 		setEncodings(true);
 | |
| 	}
 | |
| 
 | |
| 	void setEncodings(boolean autoSelectOnly) {
 | |
| 		if (options == null || rfb == null || !rfb.inNormalProtocol)
 | |
| 			return;
 | |
| 
 | |
| 		int preferredEncoding = options.preferredEncoding;
 | |
| 		if (preferredEncoding == -1) {
 | |
| 			long kbitsPerSecond = rfb.kbitsPerSecond();
 | |
| 			if (nEncodingsSaved < 1) {
 | |
| 				// Choose Tight or ZRLE encoding for the very first update.
 | |
| 				// Logger.log(Logger.INFO, "Using Tight/ZRLE encodings");
 | |
| 				preferredEncoding = RfbProto.EncodingTight;
 | |
| 			} else if (kbitsPerSecond > 2000
 | |
| 					&& encodingsSaved[0] != RfbProto.EncodingHextile) {
 | |
| 				// Switch to Hextile if the connection speed is above 2Mbps.
 | |
| 				Logger.log(Logger.INFO, "Throughput " + kbitsPerSecond
 | |
| 						+ " kbit/s - changing to Hextile encoding");
 | |
| 				preferredEncoding = RfbProto.EncodingHextile;
 | |
| 			} else if (kbitsPerSecond < 1000
 | |
| 					&& encodingsSaved[0] != RfbProto.EncodingTight) {
 | |
| 				// Switch to Tight/ZRLE if the connection speed is below 1Mbps.
 | |
| 				Logger.log(Logger.INFO, "Throughput " + kbitsPerSecond
 | |
| 						+ " kbit/s - changing to Tight/ZRLE encodings");
 | |
| 				preferredEncoding = RfbProto.EncodingTight;
 | |
| 			} else {
 | |
| 				// Don't change the encoder.
 | |
| 				if (autoSelectOnly)
 | |
| 					return;
 | |
| 				preferredEncoding = encodingsSaved[0];
 | |
| 			}
 | |
| 		} else {
 | |
| 			// Auto encoder selection is not enabled.
 | |
| 			if (autoSelectOnly)
 | |
| 				return;
 | |
| 		}
 | |
| 
 | |
| 		int[] encodings = new int[20];
 | |
| 		int nEncodings = 0;
 | |
| 
 | |
| 		encodings[nEncodings++] = preferredEncoding;
 | |
| 		if (options.useCopyRect) {
 | |
| 			encodings[nEncodings++] = RfbProto.EncodingCopyRect;
 | |
| 		}
 | |
| 
 | |
| 		if (preferredEncoding != RfbProto.EncodingTight) {
 | |
| 			encodings[nEncodings++] = RfbProto.EncodingTight;
 | |
| 		}
 | |
| 		if (preferredEncoding != RfbProto.EncodingZRLE) {
 | |
| 			encodings[nEncodings++] = RfbProto.EncodingZRLE;
 | |
| 		}
 | |
| 		if (preferredEncoding != RfbProto.EncodingHextile) {
 | |
| 			encodings[nEncodings++] = RfbProto.EncodingHextile;
 | |
| 		}
 | |
| 		if (preferredEncoding != RfbProto.EncodingZlib) {
 | |
| 			encodings[nEncodings++] = RfbProto.EncodingZlib;
 | |
| 		}
 | |
| 		if (preferredEncoding != RfbProto.EncodingCoRRE) {
 | |
| 			encodings[nEncodings++] = RfbProto.EncodingCoRRE;
 | |
| 		}
 | |
| 		if (preferredEncoding != RfbProto.EncodingRRE) {
 | |
| 			encodings[nEncodings++] = RfbProto.EncodingRRE;
 | |
| 		}
 | |
| 
 | |
| 		if (options.compressLevel >= 0 && options.compressLevel <= 9) {
 | |
| 			encodings[nEncodings++] = RfbProto.EncodingCompressLevel0
 | |
| 					+ options.compressLevel;
 | |
| 		}
 | |
| 		if (options.jpegQuality >= 0 && options.jpegQuality <= 9) {
 | |
| 			encodings[nEncodings++] = RfbProto.EncodingQualityLevel0
 | |
| 					+ options.jpegQuality;
 | |
| 		}
 | |
| 
 | |
| 		if (options.requestCursorUpdates) {
 | |
| 			encodings[nEncodings++] = RfbProto.EncodingXCursor;
 | |
| 			encodings[nEncodings++] = RfbProto.EncodingRichCursor;
 | |
| 			if (!options.ignoreCursorUpdates)
 | |
| 				encodings[nEncodings++] = RfbProto.EncodingPointerPos;
 | |
| 		}
 | |
| 
 | |
| 		encodings[nEncodings++] = RfbProto.EncodingLastRect;
 | |
| 		encodings[nEncodings++] = RfbProto.EncodingNewFBSize;
 | |
| 
 | |
| 		boolean encodingsWereChanged = false;
 | |
| 		if (nEncodings != nEncodingsSaved) {
 | |
| 			encodingsWereChanged = true;
 | |
| 		} else {
 | |
| 			for (int i = 0; i < nEncodings; i++) {
 | |
| 				if (encodings[i] != encodingsSaved[i]) {
 | |
| 					encodingsWereChanged = true;
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (encodingsWereChanged) {
 | |
| 			try {
 | |
| 				rfb.writeSetEncodings(encodings, nEncodings);
 | |
| 				if (vc != null) {
 | |
| 					vc.softCursorFree();
 | |
| 				}
 | |
| 			} catch (Exception e) {
 | |
| 				Logger.log(Logger.ERROR, e.toString(), e);
 | |
| 			}
 | |
| 			encodingsSaved = encodings;
 | |
| 			nEncodingsSaved = nEncodings;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// setCutText() - send the given cut text to the RFB server.
 | |
| 	//
 | |
| 
 | |
| 	void setCutText(String text) {
 | |
| 		try {
 | |
| 			if (rfb != null && rfb.inNormalProtocol) {
 | |
| 				rfb.writeClientCutText(text);
 | |
| 			}
 | |
| 		} catch (Exception e) {
 | |
| 			Logger.log(Logger.ERROR, e.toString(), e);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Order change in session recording status. To stop recording, pass
 | |
| 	// null in place of the fname argument.
 | |
| 	//
 | |
| 
 | |
| 	void setRecordingStatus(String fname) {
 | |
| 		synchronized (recordingSync) {
 | |
| 			sessionFileName = fname;
 | |
| 			recordingStatusChanged = true;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Start or stop session recording. Returns true if this method call
 | |
| 	// causes recording of a new session.
 | |
| 	//
 | |
| 
 | |
| 	boolean checkRecordingStatus() throws IOException {
 | |
| 		synchronized (recordingSync) {
 | |
| 			if (recordingStatusChanged) {
 | |
| 				recordingStatusChanged = false;
 | |
| 				if (sessionFileName != null) {
 | |
| 					startRecording();
 | |
| 					return true;
 | |
| 				} else {
 | |
| 					stopRecording();
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Start session recording.
 | |
| 	//
 | |
| 
 | |
| 	protected void startRecording() throws IOException {
 | |
| 		/*
 | |
| 		 * synchronized(recordingSync) {
 | |
| 		 * 
 | |
| 		 * if (!recordingActive) { // Save settings to restore them after
 | |
| 		 * recording the session. cursorUpdatesDef =
 | |
| 		 * options.choices[options.cursorUpdatesIndex].getSelectedItem();
 | |
| 		 * eightBitColorsDef =
 | |
| 		 * options.choices[options.eightBitColorsIndex].getSelectedItem(); //
 | |
| 		 * Set options to values suitable for recording.
 | |
| 		 * options.choices[options.cursorUpdatesIndex].select("Disable");
 | |
| 		 * options.choices[options.cursorUpdatesIndex].setEnabled(false);
 | |
| 		 * options.setEncodings();
 | |
| 		 * options.choices[options.eightBitColorsIndex].select("No");
 | |
| 		 * options.choices[options.eightBitColorsIndex].setEnabled(false);
 | |
| 		 * options.setColorFormat(); } else { rfb.closeSession(); }
 | |
| 		 * 
 | |
| 		 * Logger.log(Logger.INFO, "Recording the session in " +
 | |
| 		 * sessionFileName); rfb.startSession(sessionFileName); recordingActive
 | |
| 		 * = true; }
 | |
| 		 */
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Stop session recording.
 | |
| 	//
 | |
| 
 | |
| 	protected void stopRecording() throws IOException {
 | |
| 		/*
 | |
| 		 * synchronized(recordingSync) { if (recordingActive) { // Restore
 | |
| 		 * options.
 | |
| 		 * options.choices[options.cursorUpdatesIndex].select(cursorUpdatesDef);
 | |
| 		 * options.choices[options.cursorUpdatesIndex].setEnabled(true);
 | |
| 		 * options.setEncodings();
 | |
| 		 * options.choices[options.eightBitColorsIndex].select
 | |
| 		 * (eightBitColorsDef);
 | |
| 		 * options.choices[options.eightBitColorsIndex].setEnabled(true);
 | |
| 		 * options.setColorFormat();
 | |
| 		 * 
 | |
| 		 * rfb.closeSession(); Logger.log(Logger.INFO,
 | |
| 		 * "Session recording stopped."); } sessionFileName = null;
 | |
| 		 * recordingActive = false; }
 | |
| 		 */
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// readParameters() - read parameters from the html source or from the
 | |
| 	// command line. On the command line, the arguments are just a sequence of
 | |
| 	// param_name/param_value pairs where the names and values correspond to
 | |
| 	// those expected in the html applet tag source.
 | |
| 	//
 | |
| 
 | |
| 	void readParameters() {
 | |
| 		String str;
 | |
| 		host = readParameter("HOST", true);
 | |
| 
 | |
| 		str = readParameter("PORT", true);
 | |
| 		port = Integer.parseInt(str);
 | |
| 
 | |
| 		proxyHost = readParameter("PROXYHOST", false);
 | |
| 		if (proxyHost != null) {
 | |
| 			str = readParameter("PROXYPORT", true);
 | |
| 			proxyPort = Integer.parseInt(str);
 | |
| 		}
 | |
| 
 | |
| 		// Read "ENCPASSWORD" or "PASSWORD" parameter if specified.
 | |
| 		readPasswordParameters();
 | |
| 
 | |
| 		if (inAnApplet) {
 | |
| 			str = readParameter("Open New Window", false);
 | |
| 			if (str != null && str.equalsIgnoreCase("Yes"))
 | |
| 				inSeparateFrame = true;
 | |
| 		}
 | |
| 
 | |
| 		// "Show Controls" set to "No" disables button panel.
 | |
| 		showControls = false;
 | |
| 		str = readParameter("Show Controls", false);
 | |
| 		if (str != null && str.equalsIgnoreCase("No"))
 | |
| 			showControls = false;
 | |
| 
 | |
| 		// "Offer Relogin" set to "No" disables "Login again" and "Close
 | |
| 		// window" buttons under error messages in applet mode.
 | |
| 		offerRelogin = false;
 | |
| 		str = readParameter("Offer Relogin", false);
 | |
| 		if (str != null && str.equalsIgnoreCase("No"))
 | |
| 			offerRelogin = false;
 | |
| 
 | |
| 		// Do we continue showing desktop on remote disconnect?
 | |
| 		showOfflineDesktop = false;
 | |
| 		str = readParameter("Show Offline Desktop", false);
 | |
| 		if (str != null && str.equalsIgnoreCase("Yes"))
 | |
| 			showOfflineDesktop = true;
 | |
| 
 | |
| 		// Fine tuning options.
 | |
| 		deferScreenUpdates = readIntParameter("Defer screen updates", 20);
 | |
| 		deferCursorUpdates = readIntParameter("Defer cursor updates", 10);
 | |
| 		deferUpdateRequests = readIntParameter("Defer update requests", 50);
 | |
| 
 | |
| 		// SocketFactory.
 | |
| 		socketFactory = readParameter("SocketFactory", false);
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Read password parameters.
 | |
| 	//
 | |
| 
 | |
| 	private void readPasswordParameters() {
 | |
| 		String str = readParameter("SID", false);
 | |
| 		if (str != null)
 | |
| 			passwordParam = str;
 | |
| 	}
 | |
| 
 | |
| 	public String readParameter(String name, boolean required) {
 | |
| 		if (inAnApplet) {
 | |
| 			String s = applet.getParameter(name);
 | |
| 			// Logger.log(Logger.INFO, "getParameter " + name + " = " + s);
 | |
| 			if ((s == null) && required) {
 | |
| 				fatalError(name + " parameter not specified");
 | |
| 			}
 | |
| 			return s;
 | |
| 		}
 | |
| 
 | |
| 		for (int i = 0; i < mainArgs.length; i += 2) {
 | |
| 			if (mainArgs[i].equalsIgnoreCase(name)) {
 | |
| 				try {
 | |
| 					return mainArgs[i + 1];
 | |
| 				} catch (Exception e) {
 | |
| 					if (required) {
 | |
| 						fatalError(name + " parameter not specified");
 | |
| 					}
 | |
| 					return null;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		if (required) {
 | |
| 			fatalError(name + " parameter not specified");
 | |
| 		}
 | |
| 		return null;
 | |
| 	}
 | |
| 
 | |
| 	int readIntParameter(String name, int defaultValue) {
 | |
| 		String str = readParameter(name, false);
 | |
| 		int result = defaultValue;
 | |
| 		if (str != null) {
 | |
| 			try {
 | |
| 				result = Integer.parseInt(str);
 | |
| 			} catch (NumberFormatException e) {
 | |
| 			}
 | |
| 		}
 | |
| 		return result;
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// moveFocusToDesktop() - move keyboard focus either to VncCanvas.
 | |
| 	//
 | |
| 
 | |
| 	void moveFocusToDesktop() {
 | |
| 		if (vncContainer != null) {
 | |
| 			if (vc != null && vncContainer.isAncestorOf(vc))
 | |
| 				vc.requestFocus();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// disconnect() - close connection to server.
 | |
| 	//
 | |
| 
 | |
| 	synchronized public void disconnect() {
 | |
| 		Logger.log(Logger.INFO, "Disconnect");
 | |
| 
 | |
| 		synchronized (this) {
 | |
| 		if (rfb != null)
 | |
| 			rfb.close();
 | |
| 		}
 | |
| 		// options.dispose();
 | |
| 		// clipboard.dispose();
 | |
| 		if (rec != null)
 | |
| 			rec.dispose();
 | |
| 
 | |
| 		if (inAnApplet) {
 | |
| 			showMessage("Disconnected");
 | |
| 		} else {
 | |
| 			System.exit(0);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// fatalError() - print out a fatal error message.
 | |
| 	// FIXME: Do we really need two versions of the fatalError() method?
 | |
| 	//
 | |
| 
 | |
| 	synchronized public void fatalError(String str) {
 | |
| 		Logger.log(Logger.INFO, str);
 | |
| 
 | |
| 		if (inAnApplet) {
 | |
| 			// vncContainer null, applet not inited,
 | |
| 			// can not present the error to the user.
 | |
| 			Thread.currentThread().stop();
 | |
| 		} else {
 | |
| 			System.exit(1);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	synchronized public void fatalError(String str, Exception e) {
 | |
| 		/*
 | |
| 		 * if (rfb != null && rfb.closed()) { // Not necessary to show error
 | |
| 		 * message if the error was caused // by I/O problems after the
 | |
| 		 * rfb.close() method call. Logger.log(Logger.INFO,
 | |
| 		 * "RFB thread finished"); return; }
 | |
| 		 */
 | |
| 		if (str == null) {
 | |
| 			str = "";
 | |
| 		}
 | |
| 		Logger.log(Logger.INFO, str, e);
 | |
| 		showMessage(str);
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Show message text and optionally "Relogin" and "Close" buttons.
 | |
| 	//
 | |
| 
 | |
| 	void showMessage(String msg) {
 | |
| 		if (vc != null) {
 | |
| 			vc.paintErrorString(msg);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		Logger.log(Logger.INFO, msg);
 | |
| 
 | |
| 		/*
 | |
| 		 * vncContainer.removeAll();
 | |
| 		 * 
 | |
| 		 * Logger.log(Logger.INFO, "showMessage " + msg);
 | |
| 		 * 
 | |
| 		 * Label errLabel = new Label(msg, Label.CENTER); errLabel.setFont(new
 | |
| 		 * Font("Helvetica", Font.PLAIN, 12));
 | |
| 		 * 
 | |
| 		 * if (offerRelogin) {
 | |
| 		 * 
 | |
| 		 * Panel gridPanel = new Panel(new GridLayout(0, 1)); Panel outerPanel =
 | |
| 		 * new Panel(new FlowLayout(FlowLayout.LEFT));
 | |
| 		 * outerPanel.add(gridPanel); vncContainer.setLayout(new
 | |
| 		 * FlowLayout(FlowLayout.LEFT, 30, 16)); vncContainer.add(outerPanel);
 | |
| 		 * Panel textPanel = new Panel(new FlowLayout(FlowLayout.CENTER));
 | |
| 		 * textPanel.add(errLabel); gridPanel.add(textPanel); gridPanel.add(new
 | |
| 		 * ReloginPanel(this));
 | |
| 		 * 
 | |
| 		 * } else {
 | |
| 		 * 
 | |
| 		 * vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30));
 | |
| 		 * vncContainer.add(errLabel);
 | |
| 		 * 
 | |
| 		 * }
 | |
| 		 * 
 | |
| 		 * if (inSeparateFrame) { vncFrame.pack(); } else { validate(); }
 | |
| 		 */
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Stop the applet.
 | |
| 	// Main applet thread will terminate on first exception
 | |
| 	// after seeing that rfb has been closed.
 | |
| 	//
 | |
| 
 | |
| 	public void stop() {
 | |
| 		Logger.log(Logger.INFO, "Stopping applet");
 | |
| 		synchronized (this) {
 | |
| 			if (rfb != null) {
 | |
| 				rfb.close();
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// This method is called before the applet is destroyed.
 | |
| 	//
 | |
| 
 | |
| 	public void destroy() {
 | |
| 		Logger.log(Logger.INFO, "Destroying applet");
 | |
| 
 | |
| 		vncContainer.removeAll();
 | |
| 		// options.dispose();
 | |
| 		// clipboard.dispose();
 | |
| 		if (rec != null)
 | |
| 			rec.dispose();
 | |
| 		synchronized (this) {
 | |
| 			if (rfb != null)
 | |
| 				rfb.close();
 | |
| 		}
 | |
| 		if (inSeparateFrame)
 | |
| 			vncFrame.dispose();
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Start/stop receiving mouse events.
 | |
| 	//
 | |
| 
 | |
| 	public void enableInput(boolean enable) {
 | |
| 		vc.enableInput(enable);
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Close application properly on window close event.
 | |
| 	//
 | |
| 
 | |
| 	public void windowClosing(WindowEvent evt) {
 | |
| 		Logger.log(Logger.INFO, "Closing window");
 | |
| 		if (rfb != null)
 | |
| 			disconnect();
 | |
| 
 | |
| 		vncContainer.hide();
 | |
| 
 | |
| 		if (!inAnApplet) {
 | |
| 			System.exit(0);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public String toString() {
 | |
| 		return ("{ConsoleViewer-"
 | |
| 				+ host
 | |
| 				+ ":"
 | |
| 				+ port
 | |
| 				+ (" created=\"" + new Date(createTime)) + "\""
 | |
| 				+ (" lastused=\"" + new Date(lastUsedTime)) + "\""
 | |
| 				+ (clientStream != null ? (" client=" + clientStreamInfo) : "") + "}");
 | |
| 	}
 | |
| 
 | |
| }
 |