You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

VncViewer.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
  3. * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
  4. * Copyright (C) 2011-2012 Brian P. Hinz
  5. *
  6. * This is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This software is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this software; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  19. * USA.
  20. */
  21. //
  22. // VncViewer - the VNC viewer applet. It can also be run from the
  23. // command-line, when it behaves as much as possibly like the windows and unix
  24. // viewers.
  25. //
  26. // Unfortunately, because of the way Java classes are loaded on demand, only
  27. // configuration parameters defined in this file can be set from the command
  28. // line or in applet parameters.
  29. package com.tigervnc.vncviewer;
  30. import java.awt.*;
  31. import java.awt.Color;
  32. import java.awt.Graphics;
  33. import java.awt.Image;
  34. import java.io.InputStream;
  35. import java.io.IOException;
  36. import java.io.File;
  37. import java.lang.Character;
  38. import java.util.jar.Attributes;
  39. import java.util.jar.Manifest;
  40. import java.util.ArrayList;
  41. import java.util.Iterator;
  42. import javax.swing.*;
  43. import javax.swing.plaf.FontUIResource;
  44. import com.tigervnc.rdr.*;
  45. import com.tigervnc.rfb.*;
  46. import com.tigervnc.network.*;
  47. import com.jcraft.jsch.JSch;
  48. import com.jcraft.jsch.Session;
  49. public class VncViewer extends java.applet.Applet implements Runnable
  50. {
  51. public static final String about1 = "TigerVNC Viewer for Java";
  52. public static final String about2 = "Copyright (C) 1998-2011 "+
  53. "TigerVNC Team and many others (see README)";
  54. public static final String about3 = "Visit http://www.tigervnc.org "+
  55. "for information on TigerVNC.";
  56. public static String version = null;
  57. public static String build = null;
  58. public static void setLookAndFeel() {
  59. try {
  60. String nativeLaf = UIManager.getSystemLookAndFeelClassName();
  61. if (nativeLaf.endsWith("WindowsLookAndFeel"))
  62. UIManager.setLookAndFeel(nativeLaf);
  63. UIManager.put("TitledBorder.titleColor",Color.blue);
  64. LookAndFeel laf = UIManager.getLookAndFeel();
  65. if (laf == null)
  66. return;
  67. if (laf.getName().equals("Metal")) {
  68. UIManager.put("swing.boldMetal", Boolean.FALSE);
  69. FontUIResource f = new FontUIResource("SansSerif", Font.PLAIN, 11);
  70. java.util.Enumeration keys = UIManager.getDefaults().keys();
  71. while (keys.hasMoreElements()) {
  72. Object key = keys.nextElement();
  73. Object value = UIManager.get(key);
  74. if (value instanceof javax.swing.plaf.FontUIResource)
  75. UIManager.put(key, f);
  76. }
  77. } else if (laf.getName().equals("Nimbus")) {
  78. FontUIResource f;
  79. String os = System.getProperty("os.name");
  80. if (os.startsWith("Windows")) {
  81. f = new FontUIResource("Verdana", 0, 11);
  82. } else {
  83. f = new FontUIResource("DejaVu Sans", 0, 11);
  84. }
  85. UIManager.put("TitledBorder.font", f);
  86. }
  87. } catch (java.lang.Exception e) {
  88. vlog.info(e.toString());
  89. }
  90. }
  91. public static void main(String[] argv) {
  92. setLookAndFeel();
  93. VncViewer viewer = new VncViewer(argv);
  94. viewer.firstApplet = true;
  95. viewer.stop = false;
  96. viewer.start();
  97. }
  98. public VncViewer(String[] argv) {
  99. applet = false;
  100. // Override defaults with command-line options
  101. for (int i = 0; i < argv.length; i++) {
  102. if (argv[i].equalsIgnoreCase("-log")) {
  103. if (++i >= argv.length) usage();
  104. System.err.println("Log setting: "+argv[i]);
  105. LogWriter.setLogParams(argv[i]);
  106. continue;
  107. }
  108. if (Configuration.setParam(argv[i]))
  109. continue;
  110. if (argv[i].charAt(0) == '-') {
  111. if (i+1 < argv.length) {
  112. if (Configuration.setParam(argv[i].substring(1), argv[i+1])) {
  113. i++;
  114. continue;
  115. }
  116. }
  117. usage();
  118. }
  119. if (vncServerName.getValue() != null)
  120. usage();
  121. vncServerName.setParam(argv[i]);
  122. }
  123. }
  124. public static void usage() {
  125. String usage = ("\nusage: vncviewer [options/parameters] "+
  126. "[host:displayNum] [options/parameters]\n"+
  127. " vncviewer [options/parameters] -listen [port] "+
  128. "[options/parameters]\n"+
  129. "\n"+
  130. "Options:\n"+
  131. " -log <level> configure logging level\n"+
  132. "\n"+
  133. "Parameters can be turned on with -<param> or off with "+
  134. "-<param>=0\n"+
  135. "Parameters which take a value can be specified as "+
  136. "-<param> <value>\n"+
  137. "Other valid forms are <param>=<value> -<param>=<value> "+
  138. "--<param>=<value>\n"+
  139. "Parameter names are case-insensitive. The parameters "+
  140. "are:\n\n"+
  141. Configuration.listParams());
  142. System.err.print(usage);
  143. System.exit(1);
  144. }
  145. /* Tunnelling support. */
  146. private void interpretViaParam(StringParameter gatewayHost,
  147. StringParameter remoteHost, IntParameter remotePort,
  148. StringParameter vncServerName, IntParameter localPort)
  149. {
  150. final int SERVER_PORT_OFFSET = 5900;;
  151. int pos = vncServerName.getValueStr().indexOf(":");
  152. if (pos == -1)
  153. remotePort.setParam(""+SERVER_PORT_OFFSET+"");
  154. else {
  155. int portOffset = SERVER_PORT_OFFSET;
  156. int len;
  157. pos++;
  158. len = vncServerName.getValueStr().substring(pos).length();
  159. if (vncServerName.getValueStr().substring(pos, pos).equals(":")) {
  160. /* Two colons is an absolute port number, not an offset. */
  161. pos++;
  162. len--;
  163. portOffset = 0;
  164. }
  165. try {
  166. if (len <= 0 || !vncServerName.getValueStr().substring(pos).matches("[0-9]+"))
  167. usage();
  168. portOffset += Integer.parseInt(vncServerName.getValueStr().substring(pos));
  169. remotePort.setParam(""+portOffset+"");
  170. } catch (java.lang.NumberFormatException e) {
  171. usage();
  172. }
  173. }
  174. if (vncServerName != null)
  175. remoteHost.setParam(vncServerName.getValueStr().split(":")[0]);
  176. gatewayHost.setParam(via.getValueStr());
  177. vncServerName.setParam("localhost::"+localPort.getValue());
  178. }
  179. private void
  180. createTunnel(String gatewayHost, String remoteHost,
  181. int remotePort, int localPort)
  182. {
  183. try{
  184. JSch jsch=new JSch();
  185. String homeDir = new String("");
  186. try {
  187. homeDir = System.getProperty("user.home");
  188. } catch(java.security.AccessControlException e) {
  189. System.out.println("Cannot access user.home system property");
  190. }
  191. // NOTE: jsch does not support all ciphers. User may be
  192. // prompted to accept host key authenticy even if
  193. // the key is in the known_hosts file.
  194. File knownHosts = new File(homeDir+"/.ssh/known_hosts");
  195. if (knownHosts.exists() && knownHosts.canRead())
  196. jsch.setKnownHosts(knownHosts.getAbsolutePath());
  197. ArrayList<File> privateKeys = new ArrayList<File>();
  198. privateKeys.add(new File(homeDir+"/.ssh/id_rsa"));
  199. privateKeys.add(new File(homeDir+"/.ssh/id_dsa"));
  200. for (Iterator i = privateKeys.iterator(); i.hasNext();) {
  201. File privateKey = (File)i.next();
  202. if (privateKey.exists() && privateKey.canRead())
  203. jsch.addIdentity(privateKey.getAbsolutePath());
  204. }
  205. // username and passphrase will be given via UserInfo interface.
  206. PasswdDialog dlg = new PasswdDialog(new String("SSH Authentication"), false, false);
  207. dlg.userEntry.setText((String)System.getProperties().get("user.name"));
  208. Session session=jsch.getSession(dlg.userEntry.getText(), gatewayHost, 22);
  209. session.setUserInfo(dlg);
  210. session.connect();
  211. session.setPortForwardingL(localPort, remoteHost, remotePort);
  212. } catch (java.lang.Exception e) {
  213. System.out.println(e);
  214. }
  215. }
  216. public VncViewer() {
  217. applet = true;
  218. firstApplet = true;
  219. }
  220. public static void newViewer(VncViewer oldViewer, Socket sock) {
  221. VncViewer viewer = new VncViewer();
  222. viewer.applet = oldViewer.applet;
  223. viewer.firstApplet = false;
  224. viewer.sock = sock;
  225. viewer.start();
  226. }
  227. public static void newViewer(VncViewer oldViewer) {
  228. newViewer(oldViewer, null);
  229. }
  230. public void init() {
  231. vlog.debug("init called");
  232. setLookAndFeel();
  233. setBackground(Color.white);
  234. ClassLoader cl = this.getClass().getClassLoader();
  235. ImageIcon icon = new ImageIcon(cl.getResource("com/tigervnc/vncviewer/tigervnc.png"));
  236. logo = icon.getImage();
  237. }
  238. public void start() {
  239. vlog.debug("start called");
  240. if (version == null || build == null) {
  241. ClassLoader cl = this.getClass().getClassLoader();
  242. InputStream stream = cl.getResourceAsStream("com/tigervnc/vncviewer/timestamp");
  243. try {
  244. Manifest manifest = new Manifest(stream);
  245. Attributes attributes = manifest.getMainAttributes();
  246. version = attributes.getValue("Version");
  247. build = attributes.getValue("Build");
  248. } catch (java.io.IOException e) { }
  249. }
  250. nViewers++;
  251. if (applet && firstApplet) {
  252. alwaysShowServerDialog.setParam(true);
  253. Configuration.readAppletParams(this);
  254. String host = getCodeBase().getHost();
  255. if (vncServerName.getValue() == null && vncServerPort.getValue() != 0) {
  256. int port = vncServerPort.getValue();
  257. vncServerName.setParam(host + ((port >= 5900 && port <= 5999)
  258. ? (":"+(port-5900))
  259. : ("::"+port)));
  260. }
  261. }
  262. thread = new Thread(this);
  263. thread.start();
  264. }
  265. public void stop() {
  266. stop = true;
  267. }
  268. public void paint(Graphics g) {
  269. g.drawImage(logo, 0, 0, this);
  270. int h = logo.getHeight(this)+20;
  271. g.drawString(about1+" v"+version+" ("+build+")", 0, h);
  272. h += g.getFontMetrics().getHeight();
  273. g.drawString(about2, 0, h);
  274. h += g.getFontMetrics().getHeight();
  275. g.drawString(about3, 0, h);
  276. }
  277. public void run() {
  278. CConn cc = null;
  279. /* Tunnelling support. */
  280. if (via.getValueStr() != null) {
  281. StringParameter gatewayHost = new StringParameter("", "", "");
  282. StringParameter remoteHost = new StringParameter("", "", "localhost");
  283. IntParameter localPort =
  284. new IntParameter("", "", TcpSocket.findFreeTcpPort());
  285. IntParameter remotePort = new IntParameter("", "", 5900);
  286. if (vncServerName.getValueStr() == null)
  287. usage();
  288. interpretViaParam(gatewayHost, remoteHost, remotePort,
  289. vncServerName, localPort);
  290. createTunnel(gatewayHost.getValueStr(), remoteHost.getValueStr(),
  291. remotePort.getValue(), localPort.getValue());
  292. }
  293. if (listenMode.getValue()) {
  294. int port = 5500;
  295. if (vncServerName.getValue() != null &&
  296. Character.isDigit(vncServerName.getValue().charAt(0)))
  297. port = Integer.parseInt(vncServerName.getValue());
  298. TcpListener listener = null;
  299. try {
  300. listener = new TcpListener(null, port);
  301. } catch (java.lang.Exception e) {
  302. System.out.println(e.toString());
  303. System.exit(1);
  304. }
  305. vlog.info("Listening on port "+port);
  306. while (true) {
  307. Socket new_sock = listener.accept();
  308. if (new_sock != null)
  309. newViewer(this, new_sock);
  310. }
  311. }
  312. try {
  313. cc = new CConn(this, sock, vncServerName.getValue());
  314. while (!stop)
  315. cc.processMsg();
  316. if (nViewers > 1) {
  317. cc = null;
  318. return;
  319. }
  320. } catch (EndOfStream e) {
  321. vlog.info(e.toString());
  322. } catch (java.lang.Exception e) {
  323. if (cc != null) cc.deleteWindow();
  324. if (cc == null || !cc.shuttingDown) {
  325. e.printStackTrace();
  326. JOptionPane.showMessageDialog(null,
  327. e.toString(),
  328. "VNC Viewer : Error",
  329. JOptionPane.ERROR_MESSAGE);
  330. }
  331. }
  332. if (cc != null) cc.deleteWindow();
  333. nViewers--;
  334. if (!applet && nViewers == 0) {
  335. System.exit(0);
  336. }
  337. }
  338. BoolParameter useLocalCursor
  339. = new BoolParameter("UseLocalCursor",
  340. "Render the mouse cursor locally", true);
  341. BoolParameter sendLocalUsername
  342. = new BoolParameter("SendLocalUsername",
  343. "Send the local username for SecurityTypes "+
  344. "such as Plain rather than prompting", true);
  345. StringParameter passwordFile
  346. = new StringParameter("PasswordFile",
  347. "Password file for VNC authentication", "");
  348. AliasParameter passwd
  349. = new AliasParameter("passwd", "Alias for PasswordFile", passwordFile);
  350. BoolParameter autoSelect
  351. = new BoolParameter("AutoSelect",
  352. "Auto select pixel format and encoding", true);
  353. BoolParameter fullColour
  354. = new BoolParameter("FullColour",
  355. "Use full colour - otherwise 6-bit colour is used "+
  356. "until AutoSelect decides the link is fast enough",
  357. true);
  358. AliasParameter fullColor
  359. = new AliasParameter("FullColor", "Alias for FullColour", fullColour);
  360. StringParameter preferredEncoding
  361. = new StringParameter("PreferredEncoding",
  362. "Preferred encoding to use (Tight, ZRLE, hextile or"+
  363. " raw) - implies AutoSelect=0", "Tight");
  364. BoolParameter viewOnly
  365. = new BoolParameter("ViewOnly", "Don't send any mouse or keyboard "+
  366. "events to the server", false);
  367. BoolParameter shared
  368. = new BoolParameter("Shared", "Don't disconnect other viewers upon "+
  369. "connection - share the desktop instead", false);
  370. BoolParameter fullScreen
  371. = new BoolParameter("FullScreen", "Full Screen Mode", false);
  372. BoolParameter acceptClipboard
  373. = new BoolParameter("AcceptClipboard",
  374. "Accept clipboard changes from the server", true);
  375. BoolParameter sendClipboard
  376. = new BoolParameter("SendClipboard",
  377. "Send clipboard changes to the server", true);
  378. StringParameter desktopSize
  379. = new StringParameter("DesktopSize",
  380. "Reconfigure desktop size on the server on "+
  381. "connect (if possible)", "");
  382. BoolParameter listenMode
  383. = new BoolParameter("listen", "Listen for connections from VNC servers",
  384. false);
  385. StringParameter scalingFactor
  386. = new StringParameter("ScalingFactor",
  387. "Reduce or enlarge the remote desktop image. "+
  388. "The value is interpreted as a scaling factor "+
  389. "in percent. If the parameter is set to "+
  390. "\"Auto\", then automatic scaling is "+
  391. "performed. Auto-scaling tries to choose a "+
  392. "scaling factor in such a way that the whole "+
  393. "remote desktop will fit on the local screen. "+
  394. "If the parameter is set to \"FixedRatio\", "+
  395. "then automatic scaling is performed, but the "+
  396. "original aspect ratio is preserved.", "100");
  397. BoolParameter alwaysShowServerDialog
  398. = new BoolParameter("AlwaysShowServerDialog",
  399. "Always show the server dialog even if a server "+
  400. "has been specified in an applet parameter or on "+
  401. "the command line", false);
  402. StringParameter vncServerName
  403. = new StringParameter("Server",
  404. "The VNC server <host>[:<dpyNum>] or "+
  405. "<host>::<port>", null);
  406. IntParameter vncServerPort
  407. = new IntParameter("Port",
  408. "The VNC server's port number, assuming it is on "+
  409. "the host from which the applet was downloaded", 0);
  410. BoolParameter acceptBell
  411. = new BoolParameter("AcceptBell",
  412. "Produce a system beep when requested to by the server.",
  413. true);
  414. StringParameter via
  415. = new StringParameter("via", "Gateway to tunnel via", null);
  416. BoolParameter customCompressLevel
  417. = new BoolParameter("CustomCompressLevel",
  418. "Use custom compression level. "+
  419. "Default if CompressLevel is specified.", false);
  420. IntParameter compressLevel
  421. = new IntParameter("CompressLevel",
  422. "Use specified compression level "+
  423. "0 = Low, 6 = High",
  424. 1);
  425. BoolParameter noJpeg
  426. = new BoolParameter("NoJPEG",
  427. "Disable lossy JPEG compression in Tight encoding.", false);
  428. IntParameter qualityLevel
  429. = new IntParameter("QualityLevel",
  430. "JPEG quality level. "+
  431. "0 = Low, 9 = High",
  432. 8);
  433. Thread thread;
  434. Socket sock;
  435. boolean applet, firstApplet, stop;
  436. Image logo;
  437. static int nViewers;
  438. static LogWriter vlog = new LogWriter("main");
  439. }