/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. * Copyright 2011 Pierre Ossman for Cendio AB * Copyright (C) 2011 D. R. Commander. All Rights Reserved. * Copyright (C) 2011-2012 Brian P. Hinz * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ // // VncViewer - the VNC viewer applet. It can also be run from the // command-line, when it behaves as much as possibly like the windows and unix // viewers. // // Unfortunately, because of the way Java classes are loaded on demand, only // configuration parameters defined in this file can be set from the command // line or in applet parameters. package com.tigervnc.vncviewer; import java.awt.*; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.io.InputStream; import java.io.IOException; import java.io.File; import java.lang.Character; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.*; import javax.swing.*; import javax.swing.plaf.FontUIResource; import javax.swing.UIManager.*; import com.tigervnc.rdr.*; import com.tigervnc.rfb.*; import com.tigervnc.network.*; public class VncViewer extends java.applet.Applet implements Runnable { public static final String aboutText = new String("TigerVNC Java Viewer v%s (%s)%n"+ "Built on %s at %s%n"+ "Copyright (C) 1999-2012 TigerVNC Team and many others (see README.txt)%n"+ "See http://www.tigervnc.org for information on TigerVNC."); public static String version = null; public static String build = null; public static String buildDate = null; public static String buildTime = null; static ImageIcon frameIconSrc = new ImageIcon(VncViewer.class.getResource("tigervnc.ico")); public static final Image frameIcon = frameIconSrc.getImage(); public static final ImageIcon logoIcon = new ImageIcon(VncViewer.class.getResource("tigervnc.png")); public static final Image logoImage = logoIcon.getImage(); public static final InputStream timestamp = VncViewer.class.getResourceAsStream("timestamp"); public static void setLookAndFeel() { try { // Use Nimbus LookAndFeel if it's available, otherwise fallback // to the native laf, or Metal if no native laf is available. String laf = System.getProperty("swing.defaultlaf"); if (laf == null) { LookAndFeelInfo[] installedLafs = UIManager.getInstalledLookAndFeels(); for (int i = 0; i < installedLafs.length; i++) { if (installedLafs[i].getName().equals("Nimbus")) laf = installedLafs[i].getClassName(); } if (laf == null) UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } UIManager.setLookAndFeel(laf); UIManager.put("TitledBorder.titleColor",Color.blue); if (UIManager.getLookAndFeel().getName().equals("Metal")) { UIManager.put("swing.boldMetal", Boolean.FALSE); Enumeration keys = UIManager.getDefaults().keys(); while (keys.hasMoreElements()) { Object key = keys.nextElement(); Object value = UIManager.get(key); if (value instanceof FontUIResource) { String name = ((FontUIResource)value).getName(); int style = ((FontUIResource)value).getStyle(); int size = ((FontUIResource)value).getSize()-1; FontUIResource f = new FontUIResource(name, style, size); UIManager.put(key, f); } } } else if (UIManager.getLookAndFeel().getName().equals("Nimbus")) { Font f = UIManager.getFont("TitledBorder.font"); String name = f.getName(); int style = f.getStyle(); int size = f.getSize()-2; FontUIResource r = new FontUIResource(name, style, size); UIManager.put("TitledBorder.font", r); } } catch (java.lang.Exception e) { vlog.info(e.toString()); } } public static void main(String[] argv) { setLookAndFeel(); VncViewer viewer = new VncViewer(argv); viewer.start(); } public VncViewer(String[] argv) { applet = false; // load user preferences UserPreferences.load("global"); SecurityClient.setDefaults(); // Write about text to console, still using normal locale codeset getTimestamp(); System.err.format("%n"); System.err.format(aboutText, version, build, buildDate, buildTime); System.err.format("%n"); Configuration.enableViewerParams(); // Override defaults with command-line options for (int i = 0; i < argv.length; i++) { if (argv[i].length() == 0) continue; if (argv[i].equalsIgnoreCase("-config")) { if (++i >= argv.length) usage(); Configuration.load(argv[i]); continue; } if (argv[i].equalsIgnoreCase("-log")) { if (++i >= argv.length) usage(); System.err.println("Log setting: "+argv[i]); LogWriter.setLogParams(argv[i]); continue; } if (argv[i].equalsIgnoreCase("-tunnel") || argv[i].equalsIgnoreCase("-via")) { if (!tunnel.createTunnel(argv.length, argv, i)) exit(1); if (argv[i].equalsIgnoreCase("-via")) i++; continue; } if (Configuration.setParam(argv[i])) continue; if (argv[i].charAt(0) == '-') { if (i+1 < argv.length) { if (Configuration.setParam(argv[i].substring(1), argv[i+1])) { i++; continue; } } usage(); } if (vncServerName.getValue() != null) usage(); vncServerName.setParam(argv[i]); } if (!autoSelect.hasBeenSet()) { // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used autoSelect.setParam(!preferredEncoding.hasBeenSet() && !fullColour.hasBeenSet() && !fullColourAlias.hasBeenSet()); } if (!fullColour.hasBeenSet() && !fullColourAlias.hasBeenSet()) { // Default to FullColor=0 if AutoSelect=0 && LowColorLevel is set if (!autoSelect.getValue() && (lowColourLevel.hasBeenSet() || lowColourLevelAlias.hasBeenSet())) { fullColour.setParam(false); } } if (!customCompressLevel.hasBeenSet()) { // Default to CustomCompressLevel=1 if CompressLevel is used. customCompressLevel.setParam(compressLevel.hasBeenSet()); } } public static void usage() { String usage = ("\nusage: vncviewer [options/parameters] "+ "[host:displayNum]\n"+ " vncviewer [options/parameters] -listen [port] "+ "[options/parameters]\n"+ "\n"+ "Options:\n"+ " -log configure logging level\n"+ "\n"+ "Parameters can be turned on with - or off with "+ "-=0\n"+ "Parameters which take a value can be specified as "+ "- \n"+ "Other valid forms are = -= "+ "--=\n"+ "Parameter names are case-insensitive. The parameters "+ "are:\n"+ "\n"); System.err.print(usage); Configuration.listParams(79, 14); String propertiesString = ("\n"+ "System Properties (adapted from the TurboVNC vncviewer man page)\n"+ " When started with the -via option, vncviewer reads the VNC_VIA_CMD\n"+ " System property, expands patterns beginning with the \"%\" character,\n"+ " and uses the resulting command line to establish the secure tunnel\n"+ " to the VNC gateway. If VNC_VIA_CMD is not set, this command line\n"+ " defaults to \"/usr/bin/ssh -f -L %L:%H:%R %G sleep 20\".\n"+ "\n"+ " The following patterns are recognized in the VNC_VIA_CMD property\n"+ " (note that all of the patterns %G, %H, %L and %R must be present in \n"+ " the command template):\n"+ "\n"+ " \t%% A literal \"%\";\n"+ "\n"+ " \t%G gateway machine name;\n"+ "\n"+ " \t%H remote VNC machine name, (as known to the gateway);\n"+ "\n"+ " \t%L local TCP port number;\n"+ "\n"+ " \t%R remote TCP port number.\n"+ "\n"+ " When started with the -tunnel option, vncviewer reads the VNC_TUNNEL_CMD\n"+ " System property, expands patterns beginning with the \"%\" character, and\n"+ " uses the resulting command line to establish the secure tunnel to the\n"+ " VNC server. If VNC_TUNNEL_CMD is not set, this command line defaults\n"+ " to \"/usr/bin/ssh -f -L %L:localhost:%R %H sleep 20\".\n"+ "\n"+ " The following patterns are recognized in the VNC_TUNNEL_CMD property\n"+ " (note that all of the patterns %H, %L and %R must be present in \n"+ " the command template):\n"+ "\n"+ " \t%% A literal \"%\";\n"+ "\n"+ " \t%H remote VNC machine name (as known to the client);\n"+ "\n"+ " \t%L local TCP port number;\n"+ "\n"+ " \t%R remote TCP port number.\n"+ "\n"); System.err.print(propertiesString); // Technically, we shouldn't use System.exit here but if there is a parameter // error then the problem is in the index/html file anyway. System.exit(1); } public VncViewer() { UserPreferences.load("global"); applet = true; } public static void newViewer(VncViewer oldViewer, Socket sock, boolean close) { VncViewer viewer = new VncViewer(); viewer.applet = oldViewer.applet; viewer.sock = sock; viewer.start(); if (close) oldViewer.exit(0); } public static void newViewer(VncViewer oldViewer, Socket sock) { newViewer(oldViewer, sock, false); } public static void newViewer(VncViewer oldViewer) { newViewer(oldViewer, null); } public void init() { vlog.debug("init called"); setLookAndFeel(); setBackground(Color.white); } private void getTimestamp() { if (version == null || build == null) { try { Manifest manifest = new Manifest(timestamp); Attributes attributes = manifest.getMainAttributes(); version = attributes.getValue("Version"); build = attributes.getValue("Build"); buildDate = attributes.getValue("Package-Date"); buildTime = attributes.getValue("Package-Time"); } catch (java.io.IOException e) { } } } public void start() { vlog.debug("start called"); getTimestamp(); if (applet && nViewers == 0) { alwaysShowServerDialog.setParam(true); Configuration.global().readAppletParams(this); String host = getCodeBase().getHost(); if (vncServerName.getValue() == null && vncServerPort.getValue() != 0) { int port = vncServerPort.getValue(); vncServerName.setParam(host + ((port >= 5900 && port <= 5999) ? (":"+(port-5900)) : ("::"+port))); } } nViewers++; thread = new Thread(this); thread.start(); } public void exit(int n) { nViewers--; if (nViewers > 0) return; if (applet) { destroy(); } else { System.exit(n); } } public void paint(Graphics g) { g.drawImage(logoImage, 0, 0, this); int h = logoImage.getHeight(this)+20; g.drawString(String.format(aboutText, version, build, buildDate, buildTime), 0, h); } public void run() { CConn cc = null; if (listenMode.getValue()) { int port = 5500; if (vncServerName.getValue() != null && Character.isDigit(vncServerName.getValue().charAt(0))) port = Integer.parseInt(vncServerName.getValue()); TcpListener listener = null; try { listener = new TcpListener(null, port); } catch (java.lang.Exception e) { System.out.println(e.toString()); exit(1); } vlog.info("Listening on port "+port); while (true) { Socket new_sock = listener.accept(); if (new_sock != null) newViewer(this, new_sock); } } try { cc = new CConn(this, sock, vncServerName.getValue()); while (!cc.shuttingDown) cc.processMsg(); } catch (java.lang.Exception e) { if (e instanceof EndOfStream) { vlog.info(e.getMessage()); } else if (cc == null || !cc.shuttingDown) { e.printStackTrace(); JOptionPane op = new JOptionPane(e.getMessage(), JOptionPane.WARNING_MESSAGE); JDialog dlg = op.createDialog("TigerVNC Viewer"); dlg.setIconImage(frameIcon); dlg.setVisible(true); } else { if (!cc.shuttingDown) vlog.info(e.toString()); cc = null; } } exit(0); } BoolParameter useLocalCursor = new BoolParameter("UseLocalCursor", "Render the mouse cursor locally", true); BoolParameter sendLocalUsername = new BoolParameter("SendLocalUsername", "Send the local username for SecurityTypes "+ "such as Plain rather than prompting", true); StringParameter passwordFile = new StringParameter("PasswordFile", "Password file for VNC authentication", ""); AliasParameter passwd = new AliasParameter("passwd", "Alias for PasswordFile", passwordFile); BoolParameter autoSelect = new BoolParameter("AutoSelect", "Auto select pixel format and encoding", true); BoolParameter fullColour = new BoolParameter("FullColour", "Use full colour - otherwise 6-bit colour is "+ "used until AutoSelect decides the link is "+ "fast enough", true); AliasParameter fullColourAlias = new AliasParameter("FullColor", "Alias for FullColour", fullColour); IntParameter lowColourLevel = new IntParameter("LowColorLevel", "Color level to use on slow connections. "+ "0 = Very Low (8 colors), 1 = Low (64 colors), "+ "2 = Medium (256 colors)", 2); AliasParameter lowColourLevelAlias = new AliasParameter("LowColourLevel", "Alias for LowColorLevel", lowColourLevel); StringParameter preferredEncoding = new StringParameter("PreferredEncoding", "Preferred encoding to use (Tight, ZRLE, "+ "hextile or raw) - implies AutoSelect=0", "Tight"); BoolParameter viewOnly = new BoolParameter("ViewOnly", "Don't send any mouse or keyboard events to "+ "the server", false); BoolParameter shared = new BoolParameter("Shared", "Don't disconnect other viewers upon "+ "connection - share the desktop instead", false); BoolParameter fullScreen = new BoolParameter("FullScreen", "Full Screen Mode", false); BoolParameter acceptClipboard = new BoolParameter("AcceptClipboard", "Accept clipboard changes from the server", true); BoolParameter sendClipboard = new BoolParameter("SendClipboard", "Send clipboard changes to the server", true); StringParameter menuKey = new StringParameter("MenuKey", "The key which brings up the popup menu", "F8"); StringParameter desktopSize = new StringParameter("DesktopSize", "Reconfigure desktop size on the server on "+ "connect (if possible)", ""); BoolParameter listenMode = new BoolParameter("listen", "Listen for connections from VNC servers", false); StringParameter scalingFactor = new StringParameter("ScalingFactor", "Reduce or enlarge the remote desktop image. "+ "The value is interpreted as a scaling factor "+ "in percent. If the parameter is set to "+ "\"Auto\", then automatic scaling is "+ "performed. Auto-scaling tries to choose a "+ "scaling factor in such a way that the whole "+ "remote desktop will fit on the local screen. "+ "If the parameter is set to \"FixedRatio\", "+ "then automatic scaling is performed, but the "+ "original aspect ratio is preserved.", "100"); BoolParameter alwaysShowServerDialog = new BoolParameter("AlwaysShowServerDialog", "Always show the server dialog even if a server "+ "has been specified in an applet parameter or on "+ "the command line", false); StringParameter vncServerName = new StringParameter("Server", "The VNC server [:] or "+ "::", null); IntParameter vncServerPort = new IntParameter("Port", "The VNC server's port number, assuming it is on "+ "the host from which the applet was downloaded", 0); BoolParameter acceptBell = new BoolParameter("AcceptBell", "Produce a system beep when requested to by the server.", true); StringParameter via = new StringParameter("via", "Automatically create an encrypted TCP tunnel to "+ "machine gateway, then use that tunnel to connect "+ "to a VNC server running on host. By default, "+ "this option invokes SSH local port forwarding and "+ "assumes that the SSH client binary is located at "+ "/usr/bin/ssh. Note that when using the -via "+ "option, the host machine name should be specified "+ "from the point of view of the gateway machine. "+ "For example, \"localhost\" denotes the gateway, "+ "not the machine on which vncviewer was launched. "+ "See the System Properties section below for "+ "information on configuring the -via option.", null); StringParameter tunnelMode = new StringParameter("tunnel", "Automatically create an encrypted TCP tunnel to "+ "remote gateway, then use that tunnel to connect "+ "to the specified VNC server port on the remote "+ "host. See the System Properties section below "+ "for information on configuring the -tunnel option.", null); BoolParameter customCompressLevel = new BoolParameter("CustomCompressLevel", "Use custom compression level. "+ "Default if CompressLevel is specified.", false); IntParameter compressLevel = new IntParameter("CompressLevel", "Use specified compression level "+ "0 = Low, 6 = High", 1); BoolParameter noJpeg = new BoolParameter("NoJPEG", "Disable lossy JPEG compression in Tight encoding.", false); IntParameter qualityLevel = new IntParameter("QualityLevel", "JPEG quality level. "+ "0 = Low, 9 = High", 8); StringParameter config = new StringParameter("config", "Specifies a configuration file to load.", null); Thread thread; Socket sock; boolean applet; static int nViewers; static LogWriter vlog = new LogWriter("main"); }